Swagger OpenId Security Scheme on ASPNET Core

I’ve followed the instructions found in this post on setting up a swagger security scheme to authenticate during testing. When I click on “Authorize”, I am prompted to login as expected and am taken through my social login flow as expected. The problem comes after I’ve successfully authenticated.

The issue is that it appears to be using the incorrect token in order to talk to the server. The token seems really short and my server rejects with the following message

content-length: 0
date: Sun,05 Dec 2021 19:09:46 GMT
server: Kestrel
www-authenticate: Bearer error=“invalid_token”

In my Auth0 dashboard, I’ve created a separate application in my environment just for swagger, so it can have all it’s own configuration. It’s created as a SPA, and as I said, it redirects as expected and the auth flow appears to work.

In my Startup.cs, I’ve configured swagger as follows.

services.AddSwaggerGen(c =>
{
	c.CustomSchemaIds(type => type.ToString());
	c.SchemaFilter<SwaggerIgnoreDataMemberAttributeFilter>();
	c.SchemaFilter<SwaggerEnumAsStringFilter>();
	c.OperationFilter<SecurityRequirementsOperationFilter>();
	c.SwaggerDoc("v1", new OpenApiInfo
	{
		Title = "cannect Bridge",
		Description = "REST API Definition",
		Version = "v1"
	});
	c.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme
	{
		Name = "Authorization",
		Scheme = JwtBearerDefaults.AuthenticationScheme,
		In = ParameterLocation.Header,
		Type = SecuritySchemeType.OAuth2,
		Flows = new OpenApiOAuthFlows
		{
			Implicit = new OpenApiOAuthFlow
			{
				Scopes = new Dictionary<string, string>
				{
					{"openid", "Open Id"},
					{"email", "User Email"},
					{"sub", "User ID"},
					{"aud", "Audience"},
					{"offline_access", "Offline Access"}
				},
				AuthorizationUrl = new Uri($"{Configuration["Auth0:Authority"]}/authorize")
			}
		}
	});
	c.AddSecurityRequirement(new OpenApiSecurityRequirement
	{
		{
			new OpenApiSecurityScheme
			{
				Reference = new OpenApiReference
				{
					Type = ReferenceType.SecurityScheme,
					Id = JwtBearerDefaults.AuthenticationScheme
				},
				Scheme = "oauth2",
				Name = JwtBearerDefaults.AuthenticationScheme,
				In = ParameterLocation.Header
			},
			Array.Empty<string>()
		}
	});
});

services
	.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
	.AddJwtBearer(options =>
	{
		options.Authority = Configuration["Auth0:Authority"];
		options.Audience = Configuration["Auth0:Audience"];
		// If the access token does not have a `sub` claim, `User.Identity.Name` will be `null`. Map it to a different claim by setting the NameClaimType below.
		options.TokenValidationParameters = new TokenValidationParameters
		{
			NameClaimType = ClaimTypes.NameIdentifier
		};
	});

And when I configure SwaggerUI

app.UseSwagger();
app.UseSwaggerUI(c =>
{
	c.OAuthClientId(Configuration["Auth0:Swagger:ClientId"]);
	c.OAuthClientSecret(Configuration["Auth0:Swagger:ClientSecret"]);
	c.SwaggerEndpoint("/swagger/v1/swagger.json", "cannect Bridge v1");
	c.InjectStylesheet("/swagger-ui/SwaggerDark.css");
});

Am I missing something obvious here? How come I cannot get a full JWB Bearer Token when authenticating this way? The AuthToken isn’t enough for my system, and ASPNET Core complains.

1 Like

i got id_tokens to work by using x-tokenName and a js injection hack
you could also look into auth0 rules to add the claims your system is looking for

 opt.AddSecurityDefinition(SecuritySchemaId , new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Extensions = new Dictionary<string, IOpenApiExtension> { { "x-tokenName", new OpenApiString("id_token") } },
                    Flows = new OpenApiOAuthFlows
                    {
                        Implicit = new OpenApiOAuthFlow
                        {
                            AuthorizationUrl = options.JwtOptions.AuthorizationUrl,
                            Scopes = scopes
                        }
                    }
                });

however, swashbuckle ui still doesn’t support it and i had to write a custom middleware to inject js

public class OpenApiUiStaticFileMiddleware : IMiddleware
    {
        private const string Js = @"if (!window.isOpenReplaced) {
  window.open = function (open) {
    return function (url) {
      url = url
        // Swagger UI does not support custom response_type parameters. Azure Active Directory requires an 'id_token' value to
        // be passed instead of 'token' (See https://github.com/swagger-api/swagger-ui/issues/1974).
        .replace('response_type=token', 'response_type=id_token token');
      console.log(url);
      return open.call(window, url);
    };
  }(window.open);
  window.isOpenReplaced = true;
  console.log('control tower id_token script injected');
}";
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            if (context.Request.Path.Value != null && context.Request.Path.Value.EndsWith("/swagger-ui/id-token-fix.js"))
            {
                context.Response.StatusCode = 200;
                context.Response.ContentType = "application/javascript";
                await context.Response.WriteAsync(Js, context.RequestAborted);
                await context.Response.CompleteAsync();

                return;
            }

            await next(context);
        }
    }
 source.UseSwaggerUI(opt =>
            {
                opt.InjectJavascript("/swagger-ui/id-token-fix.js");
                opt.OAuthAdditionalQueryStringParams(new Dictionary<string, string>
                {
                    {"nonce", "swagger-ui"}
                });
                opt.ShowExtensions();
            });