Quantcast
Channel: damienbod – Software Engineering
Viewing all articles
Browse latest Browse all 358

Handling OpenID Connect error events in ASP.NET Core

$
0
0

ASP.NET Core provides great extension points for handling OpenID Connect error events. This blog looks at implementing error handling in an ASP.NET Core application implemented using ASP.NET Core Identity.

Code: https://github.com/damienbod/IdentityExternalErrorHandling

Setup

The application uses OpenID Connect to implement the authentication of the user identities. This implements a standard OpenID Connect flow and uses Microsoft Entra ID as the identity provider. The application also uses ASP.NET Core Identity which can be used to implement user management. This is not required and I normally avoid using this in business applications as this logic can be delegated in most cases to the identity provider.

The OpenID Connect logic can be implemented using the default ASP.NET Core OpenID Connect handlers for any OpenID Connect implementation. Almost all products and services provide client implementations for the specific clients and all are just wrappers for the default ASP.NET Core interfaces. Microsoft provides the Microsoft.Identity.Web Nuget packages for Microsoft Entra products. This works fine as long as you do not use any other OAuth or OpenID connect services in the same application.

// Identity.External
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
    options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
    options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddOpenIdConnect("EntraID", "EntraID", oidcOptions =>
{
    oidcOptions.SignInScheme = IdentityConstants.ExternalScheme;
    oidcOptions.SignOutScheme = IdentityConstants.ApplicationScheme;
    oidcOptions.RemoteSignOutPath = new PathString("/signout-callback-oidc-entra");
    oidcOptions.SignedOutCallbackPath = new PathString("/signout-oidc-entra");
    oidcOptions.CallbackPath = new PathString("/signin-oidc-entra");

    oidcOptions.Scope.Add("user.read");
    oidcOptions.Authority = $"https://login.microsoftonline.com/{builder.Configuration["AzureAd:TenantId"]}/v2.0/";
    oidcOptions.ClientId = builder.Configuration["AzureAd:ClientId"];
    oidcOptions.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
    oidcOptions.ResponseType = OpenIdConnectResponseType.Code;
    oidcOptions.UsePkce = true;

    oidcOptions.MapInboundClaims = false;
    oidcOptions.SaveTokens = true;
    oidcOptions.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name;
    oidcOptions.TokenValidationParameters.RoleClaimType = "role";
})

OpenID Connect events

Where implementing custom logic in the OpenID Connect flows, the ASP.NET Core implementation provides a lot of events. All can be used for a specific need. When implementing error logic being returned from an identity provider, no one event can be used for this logic as every product or service implements and supports this differently. For example some providers don’t return the user authentication errors, others do.

oidcOptions.Events = new OpenIdConnectEvents
{
	// Add event handlers
	OnTicketReceived = async context => {}
	OnRedirectToIdentityProvider = async context => {}
	OnPushAuthorization = async context => {}
	OnMessageReceived = async context => {}
	OnAccessDenied = async context => {}
	OnAuthenticationFailed = async context => {}
	OnRemoteFailure = async context => {}
	// ...
};

Handle a remote error

The OnRemoteFailure can be used to handle flow errors like an incorrect secret in the request. The HandleResponse can be used to prevent further processing for the event of the error and the user can be redirected to a user friendly UI view.

OnRemoteFailure = async context =>
{
    var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("OnRemoteFailure from identity provider. Scheme: {Scheme: }", context.Scheme.Name);

    if (context.Failure != null)
    {
        context.HandleResponse();
        context.Response.Redirect($"/Error?remoteError={context.Failure.Message}");
    }

    await Task.CompletedTask;
}

UI Error Page

A Razor Page can be used to display the error.

public class ErrorModel : PageModel
{
    public string? RequestId { get; set; }
    public string? Error { get; set; }
    public string? ErrorDescription { get; set; }
    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

    public void OnGet(string? remoteError)
    {
        if (remoteError != null)
        {
            Error = "Remote authentication error";
            ErrorDescription = remoteError;
        }

        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
    }
}

Notes

The ASP.NET Core APIs for implementing OpenID Connect are excellent. All products and services that implement OpenID Connect servers, handle the error processing differently. Depending on the identity products used in the software, different events are required to handle this.

Links

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/configure-oidc-web-authentication

https://docs.duendesoftware.com/identityserver/fundamentals/openid-connect-events/

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectevents


Viewing all articles
Browse latest Browse all 358

Trending Articles