domaindrivendev / Swashbuckle.AspNetCore

Swagger tools for documenting API's built on ASP.NET Core
MIT License
5.27k stars 1.32k forks source link

Available Authorizations modal is empty with v5 OpenIdConnect configuration #1241

Closed cjamesrohan closed 3 years ago

cjamesrohan commented 5 years ago

Running .Net Core 2.2 and Swashbuckle.AspNetCore 5.0.0-rc2.

Trying to setup OIDC configuration, but the Available Authorizations modal is blank, so cannot proceed with authentication.

Configuration as follows:


services.AddSwaggerGen(c => {
...
c.AddSecurityDefinition("token", new OpenApiSecurityScheme {
    Type = SecuritySchemeType.OpenIdConnect,
    OpenIdConnectUrl = new Uri($"https://login.microsoftonline.com/{tenant}/v2.0/.well-known-openid-configuration")
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
    {
        new OpenApiSecurityScheme {
            Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "token"
        }, new string[] { }
    }
});
...
});

services.UseSwaggerUI(c => {
...
c.OAuthClientId(_clientId);
...
});
dev-victory commented 5 years ago

@cjamesrohan I've managed to populate the Available Authorization pop up with the required action items. My Startup.cs file looks as follows:

ConfigureServices method

...
 services.AddSwaggerGen(c =>
            {
                c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                {
                    Name = "oauth2",
                    Type = SecuritySchemeType.OAuth2,
                    Scheme = IdentityServerAuthenticationDefaults.AuthenticationScheme,
                    Flows = new OpenApiOAuthFlows()
                    {
                        Implicit = new OpenApiOAuthFlow()
                        {
                            Scopes = new Dictionary<string, string>
                     {
                         { _scope, _scope }
                     },
                            AuthorizationUrl = new System.Uri($"{_oAuthAuthority}/connect/authorize"),
                            TokenUrl = new System.Uri($"{_oAuthAuthority }/connect/token")
                        }
                    }
                });

                c.OperationFilter<AssignOAuth2SecurityRequirements>();
                c.OperationFilter<SwaggerControllerFilter>();
                c.SwaggerDoc("v1", new OpenApiInfo { Title = $"API (v1)", Version = "v1" });
            });
...

where AssignOAuth2SecurityRequirements Operation filter class is:

public class AssignOAuth2SecurityRequirements : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            // Determine if the operation has the [Authorize] attribute
            var actionAttributes = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
            var authorizeAttributes = context.ApiDescription.CustomAttributes()
            .Union(actionAttributes.MethodInfo.CustomAttributes)
            .OfType<AuthorizeAttribute>();

            if (!authorizeAttributes.Any())
                return;

            if (!operation.Responses.ContainsKey("401"))
            {
                // Indicate that their could be a 401 response
                operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
            }

            // Initialize the operation.security property if it hasn't already been
            operation.Security ??= new List<OpenApiSecurityRequirement>();

            var oAuthRequirements = new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "oauth2"
                        }
                    },
                    new[] { "api1" }
                }
            };

            operation.Security.Add(oAuthRequirements);
        }
    }

Note:

  1. Reference object inside OpenApiSecurityRequirement of the [Authorize]d operation should have reference to the Name of the same SecurityScheme you define inside services.AddSwaggerGen.AddSecurityDefinition (i.e. your global SecurityDefinition)(as shown above)
  2. I'm implementing OAuth2 implicit flow.

I hope this helps.

cjamesrohan commented 5 years ago

@dev-victory thank you so much for your reply. However, my issue is with SecuritySchemeType.OpenIdConnect and setting the OpenIdConnectUrl. I can get the popup to work fine with SecuritySchemeType.OAuth2 and ImplicitFlow, but this does not satisfy my requirements. We are running strictly on OIDC.

MatthijsKrempel commented 5 years ago

Have same issue.

HitDaCa commented 5 years ago

We have the same issue.

We are running .net core 3.0.0 with Swashbuckle.AspNetCore 5.0.0-rc4 The issue only seems to affect the type SecuritySchemeType.OpenIdConnect.

Dolphinsimon commented 5 years ago

We have the same issue. .net core 3.0.0 with Swashbuckle.AspNetCore 5.0.0-rc4

brattpurrie commented 5 years ago

Same issue here. .net core 3.0.0 with Swashbuckle.AspNetCore 5.0.0-rc4

wterpstra commented 4 years ago

It looks like OpenIDConnect is not supported in the Swagger UI that Swashbuckle takes a dependency on: https://github.com/swagger-api/swagger-ui/issues/3517

And it does not look like it's on the roadmap either: https://github.com/swagger-api/swagger-ui/issues/5473

marcinjahn commented 4 years ago

Any update on this?

capcom923 commented 4 years ago

I met the same issue before and resolved it.

Now the available Authorization header works fine.

Please check my latest sample using SwashBuckle v5.5.1 and netcore 3.1 https://github.com/capcom923/MySwashBuckleSwaggerWithJwtToken

send-authorization-header

IngoVals commented 4 years ago

@capcom923 We are hoping to get it to work with SecuritySchemeType.OpenIdConnect which your sample doesn't resolve at all.

domaindrivendev commented 4 years ago

From the Swagger docs

OIDC is currently not supported in Swagger Editor and Swagger UI. Please follow this issue for updates

And here's the related issue in the swagger-ui repo: https://github.com/swagger-api/swagger-ui/issues/3517

cjamesrohan commented 4 years ago

From the Swagger docs

OIDC is currently not supported in Swagger Editor and Swagger UI. Please follow this issue for updates

And here's the related issue in the swagger-ui repo: swagger-api/swagger-ui#3517

I also have an open ticket somewhere for swagger-ui. Thanks for your response @domaindrivendev! Whenever this gets implemented by them, is there a plan to finish implementation in Swashbuckle? Just curious.

dafo commented 4 years ago

We are blocked by the same issue, so every information on the plans of introducing the fix would be greatly appreciated,

hpl002 commented 4 years ago

From the Swagger docs

OIDC is currently not supported in Swagger Editor and Swagger UI. Please follow this issue for updates

And here's the related issue in the swagger-ui repo: swagger-api/swagger-ui#3517

I also have an open ticket somewhere for swagger-ui. Thanks for your response @domaindrivendev! Whenever this gets implemented by them, is there a plan to finish implementation in Swashbuckle? Just curious.

please link the ticket

hkosova commented 3 years ago

OpenID Connect Discovery is now supported in Swagger UI v. 3.38.0.

IhnatKlimchuk commented 3 years ago

@cjamesrohan @hkosova FYI https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/1941 https://github.com/swagger-api/swagger-ui/pull/6750

domaindrivendev commented 3 years ago

@cjamesrohan - the latest master code now pulls in v3.38.0 of the swagger-ui and it seems that version has out-of-the-box support for OIDC. Don't have the official Swashbuckle release out yet but you can pull down the latest preview package from myget.org and try it out. Let me know if this resolves your issue.

cjamesrohan commented 3 years ago

@domaindrivendev - this does seem to let me authenticate appropriately, although it appears to populate the token with the 'access_token', and I don't see where we can configure it to use 'id_token'. At the moment, id_token is what our internal api's are expecting. Outside of that, I believe the OIDC authentication is working!

hkosova commented 3 years ago

@cjamesrohan

I don't see where we can configure it to use 'id_token'.

The security scheme needs to include the x-tokenName: id_token extension. OpenAPI YAML example: https://github.com/swagger-api/swagger-ui/issues/4084

cjamesrohan commented 3 years ago

@hkosova - great callout, thanks! I've tried implementing that, but still have the same results. See the comment here: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1235#issuecomment-671190428

Alex-Torres commented 3 years ago

Is there an example I can follow on how to configure Swashbuckle to use OIDC?

FinHorsley commented 3 years ago

Is there an example I can follow on how to configure Swashbuckle to use OIDC?

@Alex-Torres This isn't a working sample, but maybe somebody could point out if there is an issue with the following securityScheme, and then from there use that as a working example? It seems like the problem is with SwaggerUI , rather than Swashbuckle

securitySchemes:
    oidc:
        type: openIdConnect
        # IdentityServer4
        openIdConnectUrl: https://localhost:5000/.well-known/openid-configuration

The openApi.json file is being generated by the Swashbuckle.AspNetCore package, but the SwaggerUI project is running using the 3.43.0 docker image, in an attempt to isolate the SwaggerUI project with the latest OpenId connect support

docker run -it -p 80:8080 -e API_URL="http://localhost:5003/swagger/v1/swagger.json" swaggerapi/swagger-ui:v3.43.0

Edit I tried adding the security section as below, but the modal is still empty

security:
  - oidc:
      - pets_read
      - pets_write
      - admin
Bluhman commented 3 years ago

I'm assuming that the way that the OIDC scheme is meant to be invoked is basically just like the OAuth Flow scheme, but with the addition of populating the OpenIdConnectUrl parameter as well, right?

var oidcFlow = new OpenApiSecurityScheme
{
        Description = "Get a bearer token using OAUTH & OIDC and logging in through Microsoft AAD.",
        Name = "Authorization",
        Type = SecuritySchemeType.OpenIdConnect,
        OpenIdConnectUrl = new Uri($"{AppConfig.AuthAuthority}.well-known/openid-configuration"),
        Flows = new OpenApiOAuthFlows
        {
            Implicit = new OpenApiOAuthFlow
            {
                AuthorizationUrl = new Uri($"{AppConfig.AuthAuthority}oauth2/v2.0/authorize"),
                Scopes = DelegatedPermissions
            }
        },
        Reference = new OpenApiReference
        {
            Type = ReferenceType.SecurityScheme,
            Id = "OIDC Flow"
        }
};

config.AddSecurityDefinition("OIDC Flow", oidcFlow);

config.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
        {
            oidcFlow,
            new List<string>()
        }
});

despite what I'd think would be a straightforward and intuitive way to do this, this doesn't produce anything useful: image

cdowney commented 3 years ago

I had a similar issue where "Available authorizations" was appearing empty when using openIdConnect. After some frustrating debugging, I discovered it was a CORS issue. My auth server is on a different domain so when swagger-ui attempts to fetch the /.well-known/openid-configuration it is blocked by the CORS policy. Try loading swagger-ui with the developer console open and check for errors. Firefox seems better at reporting CORS issues than Chrome.

evanshortiss commented 3 years ago

@cdowney, can you share the JSON spec you used? I have a similar issue to @FinHorsley. Nothing shows in the modal, and there are no JS/CORS errors reported. There are no requests made to my authorization server at all, so I'm thinking SwaggerUI is ignoring the scheme entirely!

szalapski commented 2 years ago

How do I set up swaggerGen to use OpenID Connect Discovery? I can't find any documentation on how to do so. So far I have this:

services.AddSwaggerGen(c =>
{
    OpenApiInfo apiInfo = new(){ /* ... */ };
    c.SwaggerDoc("v1", apiInfo);
    c.AddSecurityDefinition("AccountsOpenID", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OpenIdConnect,
        OpenIdConnectUrl = new Uri($"https://login.microsoftonline.com/{tenantId}/.well-known/openid-configuration"),
    });
    c.OperationFilter<MethodNeedsAuthorizationFilter>(); // puts auth UI on the right action
}

Where MethodNeedsAuthorizationFilter is...

public class MethodNeedsAuthorizationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation is null) throw new ArgumentNullException(nameof(operation));
        if (context is null) throw new ArgumentNullException(nameof(context));

        object[] methodAttributes = context.MethodInfo.GetCustomAttributes(true);
        bool needsAuth =
            methodAttributes.OfType<AuthorizeAttribute>().Any()
            || (context.MethodInfo.DeclaringType != null
                && context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
                && !methodAttributes.OfType<AllowAnonymousAttribute>().Any());

        if (needsAuth)
        {
            OpenApiSecurityScheme scheme = new()
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" },
                Scheme = "AccountsOpenID",
                Name = "AccountsOpenID",
                In = ParameterLocation.Header,
            };
            operation.Security = new List<OpenApiSecurityRequirement>()
            {
                new () { {scheme, new List<string>() } }
            };
        }
    }
}

When I try it, the authentication button seems to work but I get "Failed to fetch." when I run any of my actions that require authorization. Can anyone help refine this, perhaps @Bluhman or @FinHorsley ? I'm reaching in the dark here.

szalapski commented 2 years ago

Added a question here: https://stackoverflow.com/questions/72187874/how-do-i-set-up-swashbuckle-and-swagger-ui-to-authorize-using-open-id-connect-di . If anyone has time to take a look, I'd appreciate whatever thoughts you have.