asp.net-coreokta

OKTA Authentication with ASP.Net Core throws unhandled exception when user not assigned to client application


I've integrated OKTA to my ASP.Net Core application from the following tutorial: https://developer.okta.com/quickstart/#/okta-sign-in-page/dotnet/aspnetcore

It works fine if a user is assigned to my OKTA application, but if not I get an unhandled exception in the authorization callback:

OpenIdConnectProtocolException: Message contains error: 'access_denied', error_description: 'User is not assigned to the client application.', error_uri: 'error_uri is null'.

I would like to catch this exception and handle it gracefully.

With OpenId, you could create an OnRemoteFailure event that to deal with this, but I can't figure out how to do it with the Okta.AspNetCore library.


Solution

  • The default schema of Okta.AspNetCore is OpenIdConnectDefaults.AuthenticationScheme:

    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Authentication.OpenIdConnect;
    
    namespace Okta.AspNetCore
    {
        public static class OktaDefaults
        {
            public const string MvcAuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme;
    
            public const string ApiAuthenticationScheme = JwtBearerDefaults.AuthenticationScheme;
    
            public const string CallbackPath = "/authorization-code/callback";
    
            public const string SignOutCallbackPath = "/signout/callback";
    
            public static readonly string[] Scope = new string[] { "openid", "profile" };
        }
    }
    

    So you can configure the OpenIdConnectOptions by using the scheme names from above, including access to OpenIdConnectEvents:

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OktaDefaults.MvcAuthenticationScheme;
    })
    .AddCookie()
    .AddOktaMvc(new OktaMvcOptions
    {
        // Replace these values with your Okta configuration
        OktaDomain = Configuration.GetValue<string>("Okta:OktaDomain"),
        ClientId = Configuration.GetValue<string>("Okta:ClientId"),
        ClientSecret = Configuration.GetValue<string>("Okta:ClientSecret"),
    });
    services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
    {
        options.Events = new OpenIdConnectEvents
        {
            OnRedirectToIdentityProvider = async ctxt =>
            {
                // Invoked before redirecting to the identity provider to authenticate. This can be used to set ProtocolMessage.State
                // that will be persisted through the authentication process. The ProtocolMessage can also be used to add or customize
                // parameters sent to the identity provider.
                await Task.Yield();
            },
            OnMessageReceived = async ctxt =>
            {
                // Invoked when a protocol message is first received.
                await Task.Yield();
            },
            OnTicketReceived = async ctxt =>
            {
                // Invoked after the remote ticket has been received.
                // Can be used to modify the Principal before it is passed to the Cookie scheme for sign-in.
                // This example removes all 'groups' claims from the Principal (assuming the AAD app has been configured
                // with "groupMembershipClaims": "SecurityGroup"). Group memberships can be checked here and turned into
                // roles, to be persisted in the cookie.
            
                await Task.Yield();
            },
            OnRemoteFailure = context =>
            {
    
                ..........
                context.HandleResponse();
                context.Response.Redirect("AccessDenied?error=" + context.Failure.Message);
    
                return Task.FromResult(0);
            },
        };
    });