I have an ASPNET Core 6 service which uses Duende IdentityServer 6, which includes several endpoints such as /connect/token and /connect/authorize. I need these endpoints to show up in my Swagger UI page, however I cannot find a way to get them to show up.
Here is my AddSwaggerGen
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
{
AuthorizationUrl =
new Uri($"{builder.Configuration.GetSection("BaseUri").Value}connect/authorize",
UriKind.RelativeOrAbsolute),
TokenUrl = new Uri($"{builder.Configuration.GetSection("BaseUri").Value}connect/token",
UriKind.RelativeOrAbsolute),
Scopes = new Dictionary<string, string>
{
{ Constants.Api.ScopeName, "Base level access to API" }
}
}
}
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" },
In = ParameterLocation.Cookie
},
new string[] { }
}
});
});
And I am just using the basic app.AddSwagger() and app.AddSwaggerUI()
As far as my research has shown, CodingMytra is correct. IdentityServer endpoints must be added manually to the Swagger document.
Related
I'd like to create an authentication/authorization flow of sorts using Identity Server to have a user authorize themselves in my Swagger API so that they may access endpoints marked with the [Authorize] attribute. This is the current flow I have:
I have Swagger set up with the NSwag middleware with the OAuth2 security scheme:
services.AddMvcCore().AddApiExplorer();
services.AddOpenApiDocument(settings =>
{
settings.Title = "MyProject Services";
settings.Version = "1.0";
settings.AddSecurity("oauth2", new NSwag.OpenApiSecurityScheme
{
Type = NSwag.OpenApiSecuritySchemeType.OAuth2,
Flow = NSwag.OpenApiOAuth2Flow.AccessCode,
AuthorizationUrl = "/connect/authorize",
TokenUrl = "/connect/token",
Scopes = new Dictionary<string, string>
{
{ "MyProjectServicesAPI", "API Access" }
}
});
settings.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("oauth2"));
});
And the OAuth2 client settings in Configure():
app.UseOpenApi();
app.UseSwaggerUi3(options =>
{
options.OAuth2Client = new NSwag.AspNetCore.OAuth2ClientSettings
{
ClientId = "MyProjectAPI",
ClientSecret = "mysecret",
UsePkceWithAuthorizationCodeGrant = true
};
});
After a user selects the scope and authorizes, they get redirected to my Identity Server Login Page I scaffolded and from there they can login. Once they put in their credentials and press, 'Login', they then get redirected back to the Swagger API. So far so good. Now this is where I start to have trouble cause I would like to later add policies so a user must have a specific claim to access an endpoint, but right now, I'm not able to see any of my user's claims in the JWT Bearer token that's in the request header when I access and endpoint. The only information I get about my user is in the 'sub' which is their GUID. I'd like to be able to get their username, email, and role(s) as well.
This is what I have setup for Identity Server so far (and where I'm currently stuck):
Under ConfigureServices():
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.IdentityResources = new IdentityResourceCollection
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource
{
Name = "roles",
DisplayName = "roles",
UserClaims = new List<string> { JwtClaimTypes.Role }
},
new IdentityResource
{
Name = "basicInfo",
DisplayName = "basic info",
UserClaims = new List<string> {
JwtClaimTypes.PreferredUserName
}
}
};
options.Clients = new ClientCollection
{
new Client
{
ClientId = "MyProjectAPI",
ClientName = "My Project Services API",
ClientSecrets = { new Secret("mysecret".Sha256()) },
AllowedGrantTypes = GrantTypes.Code,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "https://localhost:44319/swagger/oauth2-redirect.html" },
PostLogoutRedirectUris = { "https://localhost:44319/Identity/Account/Logout" },
AllowedScopes = {
"basicInfo",
"roles",
"MyProjectServicesAPI",
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
RequirePkce = true,
RequireConsent = false
}
};
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerJwt()
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true
};
});
And then in the pipeline:
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
I recently got this error that's being thrown from Identity Server's OidcConfigurationController:
'Can't determine the type for the client 'MyProject''
I'm putting the Authorization Code type for the AllowedGrantTypes in my client so I'm not quite sure why it's throwing that error.
Do I need to be adding the claims to the Bearer token myself? If I'm including the scopes, why aren't those claims showing up? Thank you in advance for any assistance.
EDIT #1: I did resolve the error I was receiving from the OidcConfigurationController. I will add the JWT Bearer token only shows the 'MyProjectServicesAPI" scope and nothing else. However, my oidc discovery doc shows all of them?
I think I was able to partially solve my problem. So I didn't have Identity Server's Profile Service set up to grab my user's ID so it could grab the identity claims.
ProfileService.cs:
private readonly UserManager<ApplicationUser> _userManager;
public ProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
// Add custom claims to access token.
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
context.IssuedClaims.AddRange(context.Subject.Claims);
var user = await _userManager.GetUserAsync(context.Subject);
var roles = await _userManager.GetRolesAsync(user);
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.Email, user.Email),
new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
};
foreach (var claim in claims)
{
context.IssuedClaims.Add(claim);
}
foreach (var role in roles)
{
context.IssuedClaims.Add(new Claim(JwtClaimTypes.Role, role));
}
}
public async Task IsActiveAsync(IsActiveContext context)
{
var user = await _userManager.GetUserAsync(context.Subject);
context.IsActive = (user != null) && user.LockoutEnabled;
}
And then back in Startup.cs:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
....
})
.AddProfileService<ProfileService>();
And that's it! Order does matter as I did have AddProfileService() before my AddApiAuthorization() and that didn't work. All of my scopes still aren't showing in my JWT token, so I will need to revisit that, even though the right claims are being pulled from those Identity resources.
I have configured AzureAd token authentication for my webAPI but in swagger page its showing the actual clientId value but I don't want to show the actual value of ClientId to the end user. That means in the code I can hardcode but in the swagger page I want to show some dummy value, how that can be done?
In the clientId textbox, I want to pass any random value like 'swaggerClient'
services.AddSwaggerGen(op =>
{
var openApi = new OpenApiSecurityScheme
{
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = "https://abcde.com",
Scopes = new Dictionary<string, string>
{
{ Scope, "mvc1"}
},
TokenUrl = "https://abcde.com/token"
}
},
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.OAuth2
};
op.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "oauth2"
}
},
new string[] {}
}
});
options.AddSecurityDefinition("oauth2", openApi);
options.OperationFilter<SecurityRequirementsOperationFilter>();
});
Client_id is not hided as that it is not a secret as OAuth specification RFC 6749 - The OAuth 2.0 Authorization Framework indicates and it is exposed to the resource owner and must be used along with client secret for client authentication and secret is hided anyway.
Please check if the parameter can be hidden by using SchemaFilter with IgnoreDataMember something like C# ASP.NET : Hide model properties from Swagger doc - DEV Community
GET the client id from getter and setter class
[IgnoreDataMember]
public string ClientId { set; get; }
use ISchemaFilter to control it. Add new class.
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
namespace swaggertest
{
public class MySwaggerSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema?.Properties == null)
{
return;
}
var ignoreDataMemberProperties = context.Type.GetProperties()
.Where(t => t.GetCustomAttribute<IgnoreDataMemberAttribute>() != null);
foreach (var ignoreDataMemberProperty in ignoreDataMemberProperties)
{
var propertyToHide = schema.Properties.Keys
.SingleOrDefault(x => x.ToLower() == ignoreDataMemberProperty.Name.ToLower());
if (propertyToHide != null)
{
schema.Properties.Remove(propertyToHide);
}
}
}
}
}
And then specifying in the start up
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "swaggertest", Version = "v1" });
c.SchemaFilter<MySwaggerSchemaFilter>();
});
...
}
Or by changing the DOM properties as suggested by kievu in github issue
You can raise a azure support request or swagger support regarding the same.
References:
Testing Azure AD-protected APIs, part 1: Swagger UI - Joonas W's
blog
How To Swagger Hide API Or Route Method – Guidelines |TheCodeBuzz
I just upgraded my project from Swashbuckle 5.6.3 to 6.0.7. I have made no code changes, but now when attempting to use the Swagger page to test the API, the URL generated by Swagger is not using https even though the page is loaded through https and all the documentation I can find says that it should infer the scheme based on the URL used to load the Swagger page.
Here is the configuration code:
services.AddSwaggerGen(c => {
c.SwaggerDoc(apiSettings.Version, new OpenApiInfo { Title = apiSettings.Name, Version = apiSettings.Version });
c.CustomSchemaIds(type => type.FullName);
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
Description = "JWT Authorization header using the Bearer scheme.",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "bearer",
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{ new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new List<string>() }
});
});
and:
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint($"/swagger/{apiSettings.Version}/swagger.json", $"{apiSettings.Name} {apiSettings.Version}");
});
Is there a new configuration setting to specify the scheme now?
In version 6.0.7, you can change this behavior with the MapSwagger extension method.
app.UseEndpoints(endpoints =>
{
...
endpoints.MapSwagger($"/swagger/{apiSettings.Version}/swagger.json", o =>
{
o.PreSerializeFilters.Add((swagger, httpReq) =>
{
swagger.Servers.Clear();
});
});
});
In the end, this is the final code that worked for me.
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
endpoints.MapSwagger("/swagger/{documentName}/swagger.json", options => {
options.PreSerializeFilters.Add((swagger, httpRequest) => { });
});
});
I am developing some Web API with .NET Core 3.0 and want to integrate it with SwashBuckle.Swagger.
It is working fine, but when I add JWT authentication, it does not work as I expect.
To do that, I added the code below:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "My Web API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey
});
});
After adding AddSecurityDefinition function, I can see the Authorize button and when I click it, I see the form below:
Then I type Bearer WhatEverApiKeyIsfgdgdgdg845734987fgdhgiher635kjh. After doing it, I expect to see authorization: Bearer WhatEverApiKeyIsfgdgdgdg845734987fgdhgiher635kjh in the request's header when I send a request to the Web API from Swagger, but authorization is not added to the request header. I am using SwashBuckle.Swagger(5.0.0-rc3). Please note there are many samples which work fine on .NET Core 2.0, but Swashbuckle swagger functions has changed on the latest version so I cannot use those samples.
After some research, I eventually found the answer here
Before seeing this page, I knew that I should use AddSecurityRequirement after AddSecurityDefinition because of many samples, but it was a problem that the function parameters have changed on .NET Core 3.0.
By the way, the final answer is as below:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "My API",
Version = "v1"
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
In = ParameterLocation.Header,
Description = "Please insert JWT with Bearer into field",
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
});
If you are using Swagger 3.0 then it has build-in support for JWT authentication.
You need to use ParameterLocation.Header, SecuritySchemeType.Http, bearer, and JWT in OpenApiSecurityScheme as shown below.
After this, you wouldn't need to specify token in Bearer {token} format. Only specify the token and the security scheme will automatically apply it in the header.
// Bearer token authentication
OpenApiSecurityScheme securityDefinition = new OpenApiSecurityScheme()
{
Name = "Bearer",
BearerFormat = "JWT",
Scheme = "bearer",
Description = "Specify the authorization token.",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
};
c.AddSecurityDefinition("jwt_auth", securityDefinition);
// Make sure swagger UI requires a Bearer token specified
OpenApiSecurityScheme securityScheme = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference()
{
Id = "jwt_auth",
Type = ReferenceType.SecurityScheme
}
};
OpenApiSecurityRequirement securityRequirements = new OpenApiSecurityRequirement()
{
{securityScheme, new string[] { }},
};
c.AddSecurityRequirement(securityRequirements);
In the accepted answer, "Bearer " is required to be written before the actual token.
A similar approach in which typing "Bearer " can be skipped is the following:
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Example API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
In = ParameterLocation.Header,
Scheme = "bearer",
Description = "Please insert JWT token into field"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
Here, only pasting the JWT token is required for this to work.
Here's a solution updated for Swashbuckle.AspNetCore 5.3.2, integrated with IdentityServer4, with an API secured using a Bearer token.
In ConfigureServices() method:
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
options.AddSecurityDefinition("Bearer", SecuritySchemes.BearerScheme(Configuration));
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{ SecuritySchemes.OAuthScheme, new List<string>() }
});
});
In Configure() method:
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/My.Api/swagger/v1/swagger.json", "My API V1");
options.OAuthClientId(Clients.TestClient);
options.OAuthAppName("My Api - Swagger");
options.OAuthClientSecret(Configuration["TestClientSecret"]);
});
internal static class SecuritySchemes
{
public static OpenApiSecurityScheme BearerScheme(IConfiguration config) => new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Description = "Standard authorisation using the Bearer scheme. Example: \"bearer {token}\"",
In = ParameterLocation.Header,
Name = "Authorization",
Scheme = "Bearer",
OpenIdConnectUrl = new System.Uri($"{config["TokenServerUrl"]}.well-known/openid-configuration"),
BearerFormat = "JWT",
Flows = new OpenApiOAuthFlows
{
Password = new OpenApiOAuthFlow
{
AuthorizationUrl = new System.Uri($"{config["TokenServerUrl"]}connect/authorize"),
Scopes = new Dictionary<string, string>
{
{ Scopes.Api, "My Api" }
},
TokenUrl = new System.Uri($"{config["TokenServerUrl"]}connect/token")
}
}
};
public static OpenApiSecurityScheme OAuthScheme => new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
};
}
If anyone is using NSwag and has landed here after searching the solution, here is the link from the official documentation.
NSwag Enable JWT authentication
PS: I know the original question was for SwashBuckle, but Google shows this link first when searching for NSwag as well.
If you don't want to add a token manually and you want the scopes to be selectable along with passing a clientId to the identity server you can add something like this.
I have used implicit flow, but you can configure any flow using the following mechanism:
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme()
{
Flows = new OpenApiOAuthFlows
{
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("http://localhost"),
TokenUrl = new Uri("http://localhost"),
Scopes = new Dictionary<string, string>
{
{ "Foundation API", "FoundationApi" }
}
}
},
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.OAuth2
});
The output will be like this:
In Accept answer you have to manually append "bearer" with token, this will create new issues, Swagger can append "bearer" with token watch this following link
JWT Authentication and Swagger with .NET Core 3 and 5 YouTube video
I have basic Api that accepts a default header value for my-api-key and the corresponding value.
I'm trying to get the Swagger UI to allow me to enter the header one time for Authorization and have the key/value passed along with every request.
So far, I've only been successful with explicitly adding the header as a parameter to every endpoint, but that isn't ideal.
Relevant code snippets:
services.AddApiVersioning(
options =>
{
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
options.ReportApiVersions = true;
});
services.AddVersionedApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Api", Version = "v1" });
c.SwaggerDoc("v2", new OpenApiInfo { Title = "Api", Version = "v2" });
// this isn't ideal as I have to fill in the Api Key on ever request
//c.OperationFilter<ApiKeySwaggerFilter>();
c.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.ApiKey,
Name = "my-api-key",
In = ParameterLocation.Header
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{ new OpenApiSecurityScheme()
{
// Type = SecuritySchemeType.ApiKey,
Name = ""
//In = ParameterLocation.Header
//Reference = new OpenApiReference()
//{
// Id = "myToken",
// Type = ReferenceType.SecurityScheme
//},
}, new string[] { }
}
});
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Api v1");
c.SwaggerEndpoint("/swagger/v2/swagger.json", "Api v2");
});
Corresponding image of where I'm at:
Corresponding minimal spike: https://github.com/aherrick/SwaggerSample
I feel this is close, but how do I get the Api Header to get passed on every request, without having to force the user to fill in parameter on every method request.
Figured it out with the following section update:
c.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Description = "Enter your Api Key below:",
Name = "my-api-key",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "ApiKey"
},
},
new List<string>()
}
});