Support
Ask questions on Stack Overflow and tag with 'itfoxtec-identity-blazor'.
Email support@foxids.com with a link to your question.
Package
Releases
NuGet ITfoxtec Identity Blazor WebAssembly OpenID Connect
Code and license
GitHub code and sample
Open-source license
ITfoxtec Identity Blazor WebAssembly OpenID Connect
ITfoxtec Identity Blazor WebAssembly OpenID Connect is a JavaScript free OpenID Connect PKCE library for Blazor WebAssembly.
The company name ITfoxtec has changed to FoxIDs but the components will keep the ITfoxtec name as part of the component name for now.
Supported .NET versions:
- .NET 10.0
- .NET 9.0
- .NET 8.0
- .NET 7.0
- .NET 6.0
- .NET 5.0
The library supports login and logout with OpenID Connect using PKCE instead of a client secret. It validates ID tokens in the browser using the OpenID Provider discovery document.
The component automatically handles token and session updates with refresh tokens when the offline_access scope is specified.
Please see the sample application.
You can use the JWT tool to decode tokens and create self-signed certificates with the certificate tool.
Sample application
Please see the sample application for implementation details.
The sample application is configured to authenticate with foxids.com using test user test1@foxids.com or test2@foxids.com and password TestAccess!
For more information about the European Identity Services FoxIDs, please see the FoxIDs documentation.
Install
Install the ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect NuGet package via the Visual Studio package manager.
Or install via PowerShell using the following command.
Install-Package ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect
Or via CLI.
dotnet add package ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect
Setup and configuration
Register the OpenidConnectPkce, the HttpClient and the IHttpClientFactory in the Program.cs file.
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);
});
}
Add appsettings.json and possible appsettings.Development.json configuration files under wwwroot with the IdentitySettings configuration. The scope is added as a space separated list of values.
{
"IdentitySettings": {
"Authority": "https://...some authority.../",
"ClientId": "...client id...",
"Scope": "...some scope..."
}
}
Add the library namespace @using ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect to Imports.razor.
IdP configuration
Configure the Blazor client as an OpenID Connect client on the IdP.
- Response types: code
- Enable PKCE: true
- Login call back: ...base URL.../authentication/login_callback
- Logout call back: ...base URL.../authentication/logout_callback
API calls to another domain
The configuration can be expanded to support API calls to another domains than the base domain. The trusted AuthorizedUris in the IdentitySettings configuration is configured on the 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);
});
}
Add trusted domains as AuthorizedUris in the IdentitySettings configuration.
{
"IdentitySettings": {
"Authority": "https://...some authority.../",
"ClientId": "...client id...",
"Scope": "...some scope...",
"Resources": [ "...resource..." ],
"AuthorizedUris": [ "...authorized api Uri..." ]
}
}
Periodic access token validation
Set SessionValidationIntervalSeconds in IdentitySettings (defaults to 300; set to 0 to disable).
When enabled, OidcAuthenticationStateProvider periodically calls the UserInfo endpoint via OidcHelper.ValidateAccessTokenWithUserInfoEndpoint.
If validation fails, the provider clears the session and raises the logout event so the UI can prompt the user to sign in again.
Add call back page
Add an Authentication.razor call back page in the Pages folder with the following content.
@page "/authentication/{action}"
@inherits AuthenticationPageBase
Authorize views
Update the App.razor to include AuthorizeRouteView/NotAuthorized with the OidcRedirectToLogin element. The OidcRedirectToLogin starts the login flow if the user does not have access.
<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>
Thereby both the Authorize attribute and AuthorizeView are supported.
Login / logout menu
Add the LoginDisplay.razor with a login / logout menu with the following content.
@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();
}
}
The LoginDisplay can be added to the MainLayout.razor like this.
@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>