How do I configure SwaggerGen with OpenIddict parameters for client credentials grant?
Asked Answered
P

2

6

I'm trying to figure out how I can configure SwaggerGen to populate/display the fields/parameters for OpenIddict and client credentials grant.

services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
    options.UseOpenIddict();
});

services.AddOpenIddict()
    .AddCore(options =>
    {
        // Configure OpenIddict to use the Entity Framework Core stores and models.
        // Note: call ReplaceDefaultEntities() to replace the default entities.
        options.UseEntityFrameworkCore().UseDbContext<AppDbContext>();
    })
    .AddServer(options =>
    {
        // Enable the token endpoint.
        options.SetTokenEndpointUris("/connect/token");

        // Enable the client credentials flow.
        options.AllowClientCredentialsFlow();

        // Register the signing and encryption credentials.
        options.AddDevelopmentEncryptionCertificate()
              .AddDevelopmentSigningCertificate();

        // Register the ASP.NET Core host and configure the ASP.NET Core options.
        options.UseAspNetCore()
              .EnableTokenEndpointPassthrough();
    })
    .AddValidation(options =>
    {
        // Import the configuration from the local OpenIddict server instance.
        options.UseLocalServer();

        // Register the ASP.NET Core host.
        options.UseAspNetCore();
    });

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo { Title = "PCM", Version = "v1" });
    options.AddSecurityDefinition("Authentication", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OpenIdConnect,
        Description = "Description", 
        In = ParameterLocation.Header, 
        Name = "Notsure", 
        Flows = new OpenApiOAuthFlows
        {
            ClientCredentials = new OpenApiOAuthFlow
            {
                AuthorizationUrl = new Uri("/connect/token", UriKind.Relative), 
                TokenUrl = new Uri("/connect/token", UriKind.Relative), 
                Scopes = new Dictionary<string, string>()
                {

                }
            }
        },
        OpenIdConnectUrl = new Uri("/connect/authorize", UriKind.Relative)
    });
});

It's displaying the Authorize button but when I click it, it opens an empty modal like shown in the below image:

enter image description here

Appreciate anyone who can point me at some docs that would explain what I need to configure in services.AddSwaggerGen() to get this configured so we can easily test our API through the interactive documentation generated by Swagger.

Palatinate answered 24/6, 2021 at 20:29 Comment(4)
Implicit = new OpenApiOAuthFlow seems wrong. You're trying to use client credentials flow, not implicit.Attachment
You're right, I changed to ClientCredentials but it didn't seem to advance me any further.Palatinate
I updated it to reflect my latest attempt with ClientCredentials.Palatinate
Ok, I've got you. Answer coming in couple in minutesAttachment
A
10

You need to specify a couple more options when defining an OpenApiSecurityScheme.

Here's how you can go about setting it up:

  • Specify TokenUrl. Client credentials flow works on /token endpoint, so we have to give it a correct URL. Here I've used IdentityServer's demo server
  • Specify how the token will be sent to the backend: We want it to be sent in Authorization header with Bearer scheme.
  • Specify which scopes the application needs. This is a dictionary that maps scope -> description.
  • Finally, add a security requirement (here it's for all endpoints) that will show a lock icon next to the endpoint. (That also helps other OpenAPI clients during code generation)
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSwaggerGen(
        c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiPlayground", Version = "v1" });
            c.AddSecurityDefinition(
                "oauth",
                new OpenApiSecurityScheme
                {
                    Flows = new OpenApiOAuthFlows
                    {
                        ClientCredentials = new OpenApiOAuthFlow
                        {
                            Scopes = new Dictionary<string, string>
                            {
                                ["api"] = "api scope description"
                            },
                            TokenUrl = new Uri("https://demo.identityserver.io/connect/token"),
                        },
                    },
                    In = ParameterLocation.Header,
                    Name = HeaderNames.Authorization,
                    Type = SecuritySchemeType.OAuth2
                }
            );
            c.AddSecurityRequirement(
                new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                                { Type = ReferenceType.SecurityScheme, Id = "oauth" },
                        },
                        new[] { "api" }
                    }
                }
            );
        }
    );
}

Here's how it looks when it's all set up:

auth popup

Once you authenticate, it gets filled with the token:

we got the token

Now we can send requests, and Swagger UI includes the token in the headers as we'd expect:

example request

Prefilling auth popup

As a finishing touch, we can pre-populate the auth dialog with some default values:

Inside the Startup:Configure methods where we set up the Swagger UI we can specify client id + secret (which defeats the purpose, but could prove useful in local development)

app.UseSwaggerUI(c => {
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiPlayground v1");
    c.OAuthClientId("m2m");
    c.OAuthClientSecret("secret");
});

Reference

Attachment answered 24/6, 2021 at 21:59 Comment(1)
This is super great. Thank you so much for the answer.Palatinate
W
1

You need to configure swagger to discover the OpenIddict configuration. See code sample below:

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo { Title = "PCM", Version = "v1" });
    options.AddSecurityDefinition("Authentication", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OpenIdConnect,
        Description = "Description", 
        In = ParameterLocation.Header,
        Name = HeaderNames.Authorization,
        Flows = new OpenApiOAuthFlows
        {
            ClientCredentials = new OpenApiOAuthFlow
            {
                AuthorizationUrl = new Uri("/connect/token", UriKind.Relative), 
                TokenUrl = new Uri("/connect/token", UriKind.Relative)
            }
        },
        OpenIdConnectUrl = new Uri("/.well-known/openid-configuration", UriKind.Relative)
    });

    options.AddSecurityRequirement(
                        new OpenApiSecurityRequirement
                        {
                            {
                                new OpenApiSecurityScheme
                                {
                                    Reference = new OpenApiReference
                                        { Type = ReferenceType.SecurityScheme, Id = "oauth" },
                                },
                                Array.Empty<string>()
                            }
                        }
                    );

});
Woodley answered 10/10, 2022 at 6:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.