Blazor WASM - .NET Core 3.1 API UserInfo

Disappointed that no one could help point me in a direction on this… But after researching more and more I figured it out. Figured I post incase someone finds it and needs the answer.

Sorry for the editing but the preformatted text in this editor kind s bad and doesn’t work well.

FOR WEB API

Need a class

    public class OktaJwtBearerEvents : JwtBearerEvents
    {
        private IConfiguration _configuration;
        public OktaJwtBearerEvents(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        public override async Task TokenValidated(TokenValidatedContext context)
        {
            try
            {
                var userPrincipal = context.Principal;
                var claimsIdentity = (ClaimsIdentity)userPrincipal.Identity;

                var userId = userPrincipal.Claims.FirstOrDefault(c => c.Type == "uid")?.Value;

                var client = new OktaClient(new OktaClientConfiguration
                {
                    OktaDomain = _configuration["Okta:Domain"],
                    Token = _configuration["Okta:ApiKey"]
                });

                var userInfo = await client.Users.GetUserAsync(userId);
                claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, userInfo.Profile.DisplayName));
                claimsIdentity.AddClaim(new Claim(ClaimTypes.Email, userInfo.Profile.Email));
                claimsIdentity.AddClaim(new Claim("login", userInfo.Profile.Login));
                var groups = await userInfo.Groups.ToListAsync();

                // Assign Roles
                foreach (var group in groups)
                    claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, group.Profile.Name));

            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }

.NET Core 3.1 Web API Startup.cs

                services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
            {
                options.SaveToken = true;
                options.Authority = Configuration["Okta:Authority"];
                options.Audience = "api://default";
                options.EventsType = typeof(OktaJwtBearerEvents);
            });

            services.AddAuthorization();

            services.AddTransient<OktaJwtBearerEvents>();

WASM

Need a two classes

        public class IasAppWebApiAuthMsgHandler : AuthorizationMessageHandler
        {
            public IasAppWebApiAuthMsgHandler(IAccessTokenProvider provider,
                NavigationManager navigationManager)
                : base(provider, navigationManager)
            {
                ConfigureHandler(
                    authorizedUrls: new[] { "http://localhost:58095/api/" },
                    scopes: new[] { "openid"});
            }
        }

    public class RolesClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
    {
        public RolesClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor) : base(accessor)
        {
        }
        public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
            RemoteUserAccount account, RemoteAuthenticationUserOptions options)
        {
            var user = await base.CreateUserAsync(account, options);
            if (!user.Identity.IsAuthenticated)
            {
                return user;
            }

            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(claim => claim.Type == "groups");
            if (roleClaims == null || !roleClaims.Any())
            {
                return user;
            }

            foreach (var existingClaim in roleClaims)
            {
                identity.RemoveClaim(existingClaim);
            }

            var rolesElem = account.AdditionalProperties["groups"];
            if (!(rolesElem is JsonElement roles))
            {
                return user;
            }

            if (roles.ValueKind == JsonValueKind.Array)
            {
                foreach (var role in roles.EnumerateArray())
                {
                    identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
                }
            }
            else
            {
                identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
            }

            return user;
        }
    }

Program.cs

            builder.Services.AddScoped<IasAppWebApiAuthMsgHandler>();
            builder.Services.AddHttpClient("ServerAPI",
                    client => client.BaseAddress = new Uri("http://localhost:58095/api/"))
                    .AddHttpMessageHandler<IasAppWebApiAuthMsgHandler>();

            builder.Services.AddOidcAuthentication(options =>
            {
                options.ProviderOptions.ResponseType = "code";

                options.UserOptions.RoleClaim = "role";
                builder.Configuration.Bind("Okta", options.ProviderOptions);

            }).AddAccountClaimsPrincipalFactory<RolesClaimsPrincipalFactory>();

Now to get the WASM to work you need to create an spa application. then go into your authorization server.
Add a claim named “groups” with a token type id token on UserInfo with value type groups and regexp match on .* on any scope. --> this is for the WASM
Create an API Token in your API | Token area this is API key you will use.

Make you appsettings.json for both applications reflecting the data needed for the above code.

You will need the Okta.Sdk nuget package.

Hope this helps someone else in the future…

–Angela

1 Like