简体   繁体   中英

Implicit Bearer flow for swagger asp..net core 3.1

Is there a way to automatically put bearer token to every request in Swagger? I don't want to use oauth implicit flow where I should interact with identity.

I'd like to provide swagger an endpoint to my api where it could take access token and automatically put it to every request.

In your startup.s class:

// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");

var identityUrl = configuration.GetValue<string>("IdentityUrl");

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.Authority = identityUrl;
    options.RequireHttpsMetadata = false;
    options.Audience = "demo_api";
});

SwaggerGen

    services.AddSwaggerGen(options =>
    {
      ...
        options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
       {
            Type = SecuritySchemeType.OAuth2,
            Flows = new OpenApiOAuthFlows
            {
                Implicit = new OpenApiOAuthFlow
                {
                    AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrl")}/connect/authorize"),
                    TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrl")}/connect/token"),
                    Scopes = new Dictionary<string, string>()
                    {
                        { "api1", "Demo API - full access" }
                    }
                }
            }
        });

Operation filter

options.OperationFilter<AuthorizeCheckOperationFilter>();

The implementation

public class AuthorizeCheckOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() ||
                           context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();

        if (!hasAuthorize) return;

        var unauthorizedHashCode = HttpStatusCode.Unauthorized.GetHashCode().ToString();
        var unauthorizedDescription = HttpStatusCode.Unauthorized.ToString();

        var forbiddenHashCode = HttpStatusCode.Forbidden.GetHashCode().ToString();
        var forbiddenDescription = HttpStatusCode.Forbidden.ToString();

        operation.Responses.TryAdd(unauthorizedHashCode, new OpenApiResponse { Description = unauthorizedDescription });
        operation.Responses.TryAdd(forbiddenHashCode, new OpenApiResponse { Description = forbiddenDescription });

        var oAuthScheme = new OpenApiSecurityScheme
        {
            Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
        };

        operation.Security = new List<OpenApiSecurityRequirement>
        {
            new OpenApiSecurityRequirement
            {
                [ oAuthScheme ] = new [] { "api1" }
            }
        };

    }
}

Use this

// Keep both UseAuthentication and UseAuthorization IN THIS ORDER
app.UseAuthentication();
app.UseAuthorization();

Use Swagger

app.UseSwagger(c =>
        {
            c.RouteTemplate = "swagger/{documentName}/swagger.json";
        });
        app.UseSwaggerUI(s =>
        {
            s.SwaggerEndpoint("/swagger/v1/swagger.json", "Your awesome project name");

            s.OAuthAppName("My API - Swagger");
            s.OAuthClientId("client");

            // Should match the client RedirectUrl in the IdentityServer
            s.OAuth2RedirectUrl("https://localhost:5001/swagger/oauth2-redirect.html");
        });

Your controller

[Authorize]
[ApiController]
[Route("api/[controller]")] // TODO: Take care of the versioning
public class IndentityController : ControllerBase
{
    ...

Now in the IdentityServer Project. The ApiResources:

public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            new ApiResource("api1", "My API")
        };
    }

And finally, your Client should look like this:

new Client
{
    ClientId = "client",
    AllowedGrantTypes = GrantTypes.Implicit,
    RedirectUris = { "https://localhost:5001/swagger/oauth2-redirect.html" },
    AllowedScopes = { "api1" },
    AllowAccessTokensViaBrowser = true,
    RequireConsent = false
}

And for a complete source code, take a look at the eShopOnContainers repo

Good luck :)

the next code worked for me

services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v2", new Microsoft.OpenApi.Models.OpenApiInfo
            {
                Version = "v2",
                Title = "PRJ API",
                Description = "PRJ Web API",
            });

            if (oktaIssuer != null)
            {
                c.AddSecurityDefinition("Okta", new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Scheme = "Bearer",
                    In = ParameterLocation.Header,
                    Name = "Authorization",
                    Flows = new OpenApiOAuthFlows
                    {
                        Implicit = new OpenApiOAuthFlow
                        {
                            AuthorizationUrl = new Uri($"{oktaIssuer}/v1/authorize"),
                            Scopes = new Dictionary<string, string>
                            {
                                { "profile", "Access profile" },
                                { "email", "Email"},
                                { "openid", "OpenID"}
                            }
                        }
                    }
                });

                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Okta" },
                            Scheme = "oauth2",
                            Name = "Bearer",
                            In = ParameterLocation.Header
                        },
                       
                        new string[] {}
                    }
                });
                c.OperationFilter<AuthorizeCheckOperationFilter>();
            }

Also those strings must be the same! AddSecurityDefinition&AddSecurityRequirement

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