Search code examples
c#asp.net-coreasp.net-web-apiswaggeropeniddict

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


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.


Solution

  • 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