Skip to content

ITfoxtec Identity for Blazor WebAssembly add support for OpenID Connect with PKCE.

License

Notifications You must be signed in to change notification settings

ITfoxtec/ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect

Repository files navigation

ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect

A JavaScript free OpenID Connect PKCE library for Blazor WebAssembly.

  • Support .NET 8.0
  • Support .NET 7.0
  • Support .NET 6.0
  • Support .NET 5.0

The library support login and logout with OpenID Connect (OIDC) using Proof Key for Code Exchange (PKCE) instead of a client secret. The received ID token is validated by the component in the client using the OpenID Provider (OP) discovery document.
The component automatically handle token / session update with use of refresh tokens if the offline_access scope is specified.

Please see the sample application for implementation details.
The sample application is configured to authenticate with foxids.com using test user [email protected] or [email protected] and password TestAccess!
For more information about the open source FoxIDs please see the FoxIDs documentation.

Install

Install the ITfoxtec.Identity.BlazorWebAssembly.OpenidConnect NuGet package via the Visual Studio package manger.

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

Configuration the Blazor client as a 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 then 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..." ]
  }
}

Add call back page

Add a 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 start the login flow if the user do 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

Possible 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>

Support

If you have questions please ask them on Stack Overflow. Tag your questions with 'itfoxtec-identity-blazor' and I will answer as soon as possible.

About

ITfoxtec Identity for Blazor WebAssembly add support for OpenID Connect with PKCE.

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published