Aplicação de exemplo
Consulte a aplicação de exemplo para detalhes de implementação.
Instalar
Instale o pacote NuGet ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect através do gestor de pacotes do Visual Studio.
Ou instale via PowerShell usando o seguinte comando.
Install-Package ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect
Ou via CLI.
dotnet add package ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect
Configuração e setup
Registe o OpenidConnectPkce, o HttpClient e o IHttpClientFactory no ficheiro Program.cs.
private static void ConfigureServices(IServiceCollection services, WebAssemblyHostConfiguration configuration, IWebAssemblyHostEnvironment hostEnvironment)
{
services.AddHttpClient("BlazorWebAssemblyOidcSample.API", client => client.BaseAddress = new Uri(hostEnvironment.BaseAddress))
.AddHttpMessageHandler<AccessTokenMessageHandler>();
services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorWebAssemblyOidcSample.API"));
services.AddOpenidConnectPkce(settings =>
{
configuration.Bind("IdentitySettings", settings);
});
}
Adicione appsettings.json e possíveis ficheiros appsettings.Development.json em wwwroot com a configuração IdentitySettings. O scope é adicionado como uma lista de valores separados por espaços.
{
"IdentitySettings": {
"Authority": "https://...some authority.../",
"ClientId": "...client id...",
"Scope": "...some scope..."
}
}
Adicione o namespace da biblioteca @using ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect a Imports.razor.
Chamadas de API para outro domínio
A configuração pode ser expandida para suportar chamadas de API para domínios diferentes do domínio base. Os AuthorizedUris confiáveis na configuração IdentitySettings são configurados no AccessTokenMessageHandler.
private static void ConfigureServices(IServiceCollection services, WebAssemblyHostConfiguration configuration, IWebAssemblyHostEnvironment hostEnvironment)
{
services.AddHttpClient("BlazorWebAssemblyOidcSample.API", client => client.BaseAddress = new Uri(hostEnvironment.BaseAddress))
.AddHttpMessageHandler(sp =>
{
var handler = sp.GetService<AccessTokenMessageHandler>();
configuration.Bind("IdentitySettings", handler);
return handler;
});
services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorWebAssemblyOidcSample.API"));
services.AddOpenidConnectPkce(settings =>
{
configuration.Bind("IdentitySettings", settings);
});
}
Adicione domínios confiáveis como AuthorizedUris na configuração IdentitySettings.
{
"IdentitySettings": {
"Authority": "https://...some authority.../",
"ClientId": "...client id...",
"Scope": "...some scope...",
"Resources": [ "...resource..." ],
"AuthorizedUris": [ "...authorized api Uri..." ]
}
}
Validação periódica do token de acesso
Defina SessionValidationIntervalSeconds em IdentitySettings (predefinido 300; defina 0 para desativar).
Quando ativado, OidcAuthenticationStateProvider chama periodicamente o endpoint UserInfo através de OidcHelper.ValidateAccessTokenWithUserInfoEndpoint.
Se a validação falhar, o provider limpa a sessão e dispara o evento de logout para que a UI possa pedir ao utilizador para iniciar sessão novamente.
Adicionar página de callback
Adicione uma página de callback Authentication.razor na pasta Pages com o seguinte conteúdo.
@page "/authentication/{action}"
@inherits AuthenticationPageBase
Autorizar vistas
Atualize App.razor para incluir AuthorizeRouteView/NotAuthorized com o elemento OidcRedirectToLogin. OidcRedirectToLogin inicia o fluxo de login se o utilizador não tiver acesso.
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<OidcRedirectToLogin />
</NotAuthorized>
<Authorizing>
<h1>Authentication in progress</h1>
<p>Only visible while authentication is in progress.</p>
</Authorizing>
</AuthorizeRouteView>
</Found>
<NotFound>
<CascadingAuthenticationState>
<LayoutView Layout="@typeof(MainLayout)">
<h1>Sorry</h1>
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</CascadingAuthenticationState>
</NotFound>
</Router>
Desta forma, tanto o atributo Authorize como o AuthorizeView são suportados.
Menu de login/logout
Adicione o LoginDisplay.razor com um menu de login/logout com o seguinte conteúdo.
@inject OpenidConnectPkce oenidConnectPkce
<AuthorizeView>
<Authorized>
Hello, @context.User.Identity.Name!
<button class="nav-link btn btn-link" @onclick="LogoutAsync">Logout</button>
</Authorized>
<NotAuthorized>
<button class="nav-link btn btn-link" @onclick="LoginAsync">Login</button>
</NotAuthorized>
</AuthorizeView>
@code{
private async Task LoginAsync(MouseEventArgs args)
{
await oenidConnectPkce.LoginAsync();
}
private async Task LogoutAsync(MouseEventArgs args)
{
await oenidConnectPkce.LogoutAsync();
}
}
O LoginDisplay pode ser adicionado ao MainLayout.razor desta forma.
@inherits LayoutComponentBase
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
<LoginDisplay />
</div>
<div class="content px-4">
@Body
</div>
</div>