简体   繁体   中英

How to configure Identity Server with NSwag API using IIdentityServerBuilder's .AddApiAuthorization()?

I'd like to create an authentication/authorization flow of sorts using Identity Server to have a user authorize themselves in my Swagger API so that they may access endpoints marked with the [Authorize] attribute. This is the current flow I have:

I have Swagger set up with the NSwag middleware with the OAuth2 security scheme:

services.AddMvcCore().AddApiExplorer();

            services.AddOpenApiDocument(settings =>
            {
                settings.Title = "MyProject Services";
                settings.Version = "1.0";
                settings.AddSecurity("oauth2", new NSwag.OpenApiSecurityScheme
                {
                    Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
                    Flow = NSwag.OpenApiOAuth2Flow.AccessCode,
                    AuthorizationUrl = "/connect/authorize",
                    TokenUrl = "/connect/token",
                    Scopes = new Dictionary<string, string>
                    {
                        {  "MyProjectServicesAPI", "API Access" }
                    }
                });
                settings.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("oauth2"));
            });

And the OAuth2 client settings in Configure():

app.UseOpenApi();
app.UseSwaggerUi3(options =>
{
   options.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings
   {
       ClientId = "MyProjectAPI",
       ClientSecret = "mysecret",
       UsePkceWithAuthorizationCodeGrant = true
    };
});

After a user selects the scope and authorizes, they get redirected to my Identity Server Login Page I scaffolded and from there they can login. Once they put in their credentials and press, 'Login', they then get redirected back to the Swagger API. So far so good. Now this is where I start to have trouble cause I would like to later add policies so a user must have a specific claim to access an endpoint, but right now, I'm not able to see any of my user's claims in the JWT Bearer token that's in the request header when I access and endpoint. The only information I get about my user is in the 'sub' which is their GUID. I'd like to be able to get their username, email, and role(s) as well.

This is what I have setup for Identity Server so far (and where I'm currently stuck): Under ConfigureServices():

services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
  {
     options.IdentityResources = new IdentityResourceCollection
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
            new IdentityResources.Email(),
            new IdentityResource
            {
                 Name = "roles",
                 DisplayName = "roles",
                 UserClaims = new List<string> { JwtClaimTypes.Role }
            },
            new IdentityResource
            {
                Name = "basicInfo",
                DisplayName = "basic info",
                UserClaims = new List<string> {
                    JwtClaimTypes.PreferredUserName
                }
            }
       };
       options.Clients = new ClientCollection
       {
           new Client
           {
               ClientId = "MyProjectAPI",
               ClientName = "My Project Services API",
               ClientSecrets = { new Secret("mysecret".Sha256()) },
               AllowedGrantTypes = GrantTypes.Code,
               AllowAccessTokensViaBrowser = true,
               RedirectUris = { "https://localhost:44319/swagger/oauth2-redirect.html" },
               PostLogoutRedirectUris = { "https://localhost:44319/Identity/Account/Logout" },
               AllowedScopes = {
                    "basicInfo",
                    "roles",
                    "MyProjectServicesAPI",
                     IdentityServerConstants.StandardScopes.OpenId,
                     IdentityServerConstants.StandardScopes.Profile,
                     IdentityServerConstants.StandardScopes.Email,
              RequirePkce = true,
              RequireConsent = false
          }
     };                 
});


services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddIdentityServerJwt()
    .AddJwtBearer(options =>
       {
            options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
            {
                ValidateIssuer = true
            };
});

And then in the pipeline:

app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();

I recently got this error that's being thrown from Identity Server's OidcConfigurationController:

'Can't determine the type for the client 'MyProject''

I'm putting the Authorization Code type for the AllowedGrantTypes in my client so I'm not quite sure why it's throwing that error.

Do I need to be adding the claims to the Bearer token myself? If I'm including the scopes, why aren't those claims showing up? Thank you in advance for any assistance.

EDIT #1: I did resolve the error I was receiving from the OidcConfigurationController. I will add the JWT Bearer token only shows the 'MyProjectServicesAPI" scope and nothing else. However, my oidc discovery doc shows all of them?

I think I was able to partially solve my problem. So I didn't have Identity Server's Profile Service set up to grab my user's ID so it could grab the identity claims.

ProfileService.cs:

private readonly UserManager<ApplicationUser> _userManager;

        public ProfileService(UserManager<ApplicationUser> userManager)
        {
            _userManager = userManager;
        }

        // Add custom claims to access token.
        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            context.IssuedClaims.AddRange(context.Subject.Claims);

            var user = await _userManager.GetUserAsync(context.Subject);

            var roles = await _userManager.GetRolesAsync(user);

            var claims = new List<Claim>
            {
                new Claim(JwtClaimTypes.Email, user.Email),
                new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
            };

            foreach (var claim in claims)
            {
                context.IssuedClaims.Add(claim);
            }

            foreach (var role in roles)
            {
                context.IssuedClaims.Add(new Claim(JwtClaimTypes.Role, role));
            }
        }

        public async Task IsActiveAsync(IsActiveContext context)
        {
            var user = await _userManager.GetUserAsync(context.Subject);
            context.IsActive = (user != null) && user.LockoutEnabled;
        }

And then back in Startup.cs:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
     ....
    })
    .AddProfileService<ProfileService>();

And that's it. Order does matter as I did have AddProfileService() before my AddApiAuthorization() and that didn't work, All of my scopes still aren't showing in my JWT token, so I will need to revisit that. even though the right claims are being pulled from those Identity resources

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM