RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.78k stars 1.29k forks source link

Swagger UI: Using implicit flow for OAuth2/OpenIdConnect #1539

Open jsancho opened 6 years ago

jsancho commented 6 years ago

After some trial and error I've managed to configure the OAuth2 authorization against AWS Cognito in the Swagger UI client.

Ideally, I would have used the OpenIdConnectUrl configuration in the SwaggerSecuritySchemeType.OpenIdConnect settings. Which seemed to work to an extent, as it was correctly downloading the configuration from the endpoint published by AWS Cognito and then redirecting to the login page.

However, Swagger UI was not receiving the configuration for the implicit flow, so the process was failing due to the missing response_type=token parameter in the query string.

As a workaround, the following configuration works when using OAuth2 config for Cognito. I'm leaving it here so that it can be found by others trying to use a similar setup.

// Note: This is a custom POCO used to retrieve the settings
var swaggerOptions = app.ApplicationServices.GetService<IOptions<SwaggerUIOptions>>()?.Value;

app.UseSwaggerUi3WithApiExplorer(settings =>
            {
                settings.GeneratorSettings.OperationProcessors.Add(new OperationSecurityScopeProcessor("JWT token"));

                settings.OAuth2Client = new OAuth2ClientSettings()
                {
                    AppName = "MyDemoApp",
                    ClientId = swaggerOptions.SwaggerClientId
                };

                settings.GeneratorSettings.DocumentProcessors.Add(new SecurityDefinitionAppender("JWT token",
                    new SwaggerSecurityScheme
                    {
                        Type = SwaggerSecuritySchemeType.OAuth2,
                        Flow = SwaggerOAuth2Flow.Implicit,
                        Flows = new OpenApiOAuthFlows() {
                            Implicit = new OpenApiOAuthFlow()
                            {
                                Scopes = new Dictionary<string, string> { { "openid", "User Profile" } },
                                AuthorizationUrl = swaggerOptions.AuthorizationUrl,
                                TokenUrl = swaggerOptions.TokenUrl
                            }
                        }
                    }));
                });
RicoSuter commented 6 years ago

Thanks for reporting here! Is there any action we should take? Or is this only informational? Maybe we should move this to the wiki?

jsancho commented 6 years ago

I'd say that there might be a bug with the OpenIdConnect scheme, in that it doesn't seem to pass the oauth2 flow settings to Swagger UI.

The nice thing about the OpenIdConnect scheme is that it uses a public endpoint to download the public keys and all the token endpoints. Which would make the configuration of AuthorizationUrl and TokenUrl redundant.

Anyway, since you can still configure Oauth2, as per my example above, I would argue that the priority of this bug should be minimal when you consider how much other stuff you already have going with the project.

Agree that the best use of this info might be simply move this config sample to the wiki.

RicoSuter commented 6 years ago

Looking at the Swagger UI processing it seems that the scheme is not passed:

https://github.com/RSuter/NSwag/blob/master/src/NSwag.AspNetCore/SwaggerUi3/index.html#L104

Docs: https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md

RicoSuter commented 6 years ago

Maybe we just have to update the Swagger UI 3 and then it is fixed... currently we are probably not using latest Swagger UI 3

jsancho commented 6 years ago

OK, I've just found this in your docs https://github.com/RSuter/NSwag/wiki/Middlewares#enable-authorization-in-generator-and-swagger-ui

Which describes how to configure an OAuth2 client, but using a "client credentials grant" rather than an "implicit" flow.

I'm guessing that something might be getting lost in the process of translating from the SwaggerSecurityScheme to the index.html template that you've linked.

If you could maybe point me how this process works, I could have a debugging session this weekend and if all goes well (a big ambitious if) put together a PR?

RicoSuter commented 6 years ago

Its pretty simple, the placeholders from index.html are replaced here: https://github.com/RSuter/NSwag/blob/master/src/NSwag.AspNetCore/SwaggerUi3Settings.cs#L54

Just open the solution (i think nswag.min.sln is enough) and start this project: https://github.com/RSuter/NSwag/tree/master/src/NSwag.Sample.NETCore21

RicoSuter commented 6 years ago

If you have time it would also be awesome if you improve the wiki with your findings.

jsancho commented 6 years ago

brilliant, thanks for that, I'll try to get some time to give this a shot :)

zyofeng commented 4 years ago

Sorry to dig up an old ticket, but can we get an example on how to configure OpenIDConnect client with swaggerui3? Currently I am manually setting up the SecurityScheme as Oauth2 with Implicit flow and various endpoints but would be keen to use proper discovery endpoint instead.

RicoSuter commented 4 years ago

we use this

        services.AddOpenApiDocument((options, s) =>
        {
            options.Title = title;
            options.Version = version;
            options.ApiGroupNames = new[] { groupName };

            var authOptions = s.GetRequiredService<IOptions<AuthOptions>>();
            var envOptions = s.GetRequiredService<IOptions<EnvironmentOptions>>();

            options.AddSecurity("oauth2", Enumerable.Empty<string>(), new OpenApiSecurityScheme
            {
                AuthorizationUrl = $"{authOptions.Value.AuthorizationUrl}",
                Flow = OpenApiOAuth2Flow.Implicit,
                Type = OpenApiSecuritySchemeType.OAuth2,
                Scopes = new Dictionary<string, string>
                {
                    {
                        $"{authOptions.Value.ApiScope}", "Access APIs"
                    }
                }
            });

            options.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("oauth2"));
        });

and this

        app.UseOpenApi();
        app.UseSwaggerUi3(options =>
        {
            options.OAuth2Client = new OAuth2ClientSettings
            {
                ClientId = clientId
            };
        });
zyofeng commented 4 years ago

Is there a sample to use OpenIdConnectUrl? In combination with IdentityServer4 Discovery Endpoint https://demo.identityserver.io/.well-known/openid-configuration With or without authorization code?

RicoSuter commented 4 years ago

https://identityserver.github.io/Documentation/docsv2/endpoints/authorization.html

RicoSuter commented 4 years ago

Its /connect/authorize

zyofeng commented 4 years ago

That's Authorization endpoint, the Discovery Endpoint retrieves metadata for the OpenID Provider including Authorization endpoint, available grant types and scopes.

So technically this means if Authorization point changes I wouldn't have to update anything for SwaggerUI to continue functioning.

http://docs.identityserver.io/en/latest/endpoints/discovery.html

FinHorsley commented 4 years ago

Agree with @zyofeng, being able to use the discovery endpoint to configure the authorise/token endpoints would be useful when using the OpenID Connect type.

The discovery endpoint is a well-defined part of the openId Connect specification, meaning it's not only useful for those of us that are using IdentityServer 🙂