Aplicación de ejemplo
Consulta la aplicación de ejemplo para detalles de implementación.
Instalar
Instala el paquete NuGet ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect mediante el administrador de paquetes de Visual Studio.
O instala vía PowerShell usando el siguiente comando.
Install-Package ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect
O vía CLI.
dotnet add package ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect
Configuración y ajustes
Registra OpenidConnectPkce, HttpClient y IHttpClientFactory en el archivo 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);
});
}
Agrega appsettings.json y, si aplica, appsettings.Development.json bajo wwwroot con la configuración IdentitySettings. El scope se agrega como una lista de valores separados por espacios.
{
"IdentitySettings": {
"Authority": "https://...some authority.../",
"ClientId": "...client id...",
"Scope": "...some scope..."
}
}
Agrega el espacio de nombres de la biblioteca @using ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect a Imports.razor.
Llamadas API a otro dominio
La configuración se puede ampliar para soportar llamadas API a dominios distintos del dominio base. Las AuthorizedUris de confianza en la configuración IdentitySettings se configuran en 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);
});
}
Agrega dominios de confianza como AuthorizedUris en la configuración IdentitySettings.
{
"IdentitySettings": {
"Authority": "https://...some authority.../",
"ClientId": "...client id...",
"Scope": "...some scope...",
"Resources": [ "...resource..." ],
"AuthorizedUris": [ "...authorized api Uri..." ]
}
}
Validación periódica del token de acceso
Establece SessionValidationIntervalSeconds en IdentitySettings (por defecto 300; establece 0 para desactivar).
Cuando está habilitado, OidcAuthenticationStateProvider llama periódicamente al endpoint UserInfo mediante OidcHelper.ValidateAccessTokenWithUserInfoEndpoint.
Si la validación falla, el proveedor limpia la sesión y dispara el evento de logout para que la UI pida al usuario volver a iniciar sesión.
Agregar página de callback
Agrega una página de callback Authentication.razor en la carpeta Pages con el siguiente contenido.
@page "/authentication/{action}"
@inherits AuthenticationPageBase
Vistas de autorización
Actualiza App.razor para incluir AuthorizeRouteView/NotAuthorized con el elemento OidcRedirectToLogin. OidcRedirectToLogin inicia el flujo de login si el usuario no tiene acceso.
<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>
De este modo se admiten tanto el atributo Authorize como AuthorizeView.
Menú de login / logout
Agrega LoginDisplay.razor con un menú de login / logout con el siguiente contenido.
@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();
}
}
LoginDisplay se puede añadir a MainLayout.razor así.
@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>