I am using the ASP.NET Core 2.0 with Swashbuckle.AspNetCore (2.4.0)
in Startup ConfigureServices
services.AddSwaggerGen(c =>
{
c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "Library API",
Description = "ASP.NET Core Web API",
TermsOfService = "None",
Contact = new Contact
{
Name = "my name",
Email = "myname#mywebsite"
}
});
});
});
in Startup configure
app.UseSwagger(c =>
{
c.RouteTemplate =
"api-docs/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "api-docs";
c.SwaggerEndpoint("v1/swagger.json", "Api v1");
});
public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);
if (isAuthorized && !allowAnonymous)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
operation.Parameters.Add(new NonBodyParameter
{
Name = "Authorization",
In = "header",
Description = "access token",
Required = true,
Type = "string"
});
}
}
}
which looks like this
however the links go to the url
about:blank
How do I set the v1/swagger.json link and the Terms of service link to go to the right place?
Use c.SwaggerEndpoint("./v1/swagger.json", "Api v1");
It's looks like a bug that relative path (without '/') renders empty link but swagger.json is available and all works.
I use relative path to work properly with reverse proxies but this is other story...
Related
I'm using Swashbuckle configured as
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v2", new OpenApiInfo { Title = "API", Version = "v2" });
c.AddSecurityDefinition("OpenId", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OpenIdConnect,
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "Bearer",
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri($"{authority}connect/authorize"),
TokenUrl = new Uri($"{authority}connect/token"),
Scopes = new Dictionary<string, string>
{
{
"openid", "openid"
},
{
"api", "api"
},
},
},
},
OpenIdConnectUrl = new Uri($"{authority}.well-known/openid-configuration"),
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "OpenId",
},
},
new List<string> { "api", "openid" }
},
});
});
And after that
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v2/swagger.json", "API v2");
c.OAuthUsePkce();
c.OAuthClientId(Configuration.GetRequiredSection("SwaggerOptions:ClientId").Value);
c.OAuthClientSecret(Configuration.GetRequiredSection("SwaggerOptions:ClientSecret").Value);
c.EnablePersistAuthorization();
c.OAuthScopes("api", "openid");
});
I see resulting swagger.json seems to be correct, as it declared at the docs
But something goes definitely wrong - I get CORS header 'Access-Control-Allow-Origin' missing reason for discovery request rejecting, simultaneously it returns a correct configuration with 200 ok
What have I missed?
Eventually, I was able to get this to work. I was misunderstanding which part does require CORS in this case. To fix that, I added my Swagger UI host to allowed hosts on auth server side and switch CORS on there. Now, all work fine!
Swashbuckle was used in my project, I followed the documentation, but at the end an error was displayed.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "ToDo API",
Description = "A simple example ASP.NET Core Web API",
TermsOfService = new Uri("https://example.com/terms"),
Contact = new OpenApiContact
{
Name = "Shayne Boyer",
Email = string.Empty,
Url = new Uri("https://twitter.com/spboyer"),
},
License = new OpenApiLicense
{
Name = "Use under LICX",
Url = new Uri("https://example.com/license"),
}
});
});
app.UseSwagger(c =>
{
c.SerializeAsV2 = true;
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
What did i do wrong? Thanks for the help!
I have encountered this problem before, and I can reproduce your problem, but I am not sure if this is your problem.
All configurations follow the official documentation:
Get started with Swashbuckle and ASP.NET Core
Then the reason for this error is that you must specify the request method, you need to add [HttpGet], [HttpPost], etc. to the method, and you can view the API through Swagger UI,like this:
[ApiController]
public class HomeController : Controller
{
[HttpGet]
[Route("test")]
public string Test()
{
return "Test";
}
}
Result:
If the above method cannot be solved, can you post your specific error message?
I am trying to setup IdentityServer4 for the first time, and am following the steps in the docs for adding a JS client. I must have something configured incorrectly, but I can't figure out what it is. The flow is as follows:
User hits "login" on Client A
Client A sends login request to IS4 (upon debugging, the "returnUrl" parameter seems correct)
using the IS4 extension method I am signing the user in (HttpContext.SignInAsync)
user is redirected to "connect/authorize/callback" which redirects them to the login method again and the circular reference continues until the browser stops it and throws error.
Relevant code:
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<UsersContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("UsersRuntime")));
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer(o =>
{
})
.AddTestUsers(Identity.Users.Get())
.AddConfigurationStore(o =>
{
o.ConfigureDbContext = b => b.UseSqlServer(Configuration.GetConnectionString("UsersRuntime"),
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(Configuration.GetConnectionString("UsersRuntime"),
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
InitializeDatabase(app);
app.UseRouting();
app.UseDefaultFiles();
app.UseStaticFiles();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseHttpStatusCodeExceptionMiddleware();
}
else
{
app.UseHttpStatusCodeExceptionMiddleware();
app.UseHsts();
}
app.UseCors(MyAllowSpecificOrigins);
if (env.IsProduction())
{
app.UseHttpsRedirection();
}
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Users API");
c.RoutePrefix = string.Empty;
});
}
private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<UsersContext>().Database.Migrate();
var persistedGrantDbContext =
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>();
persistedGrantDbContext.Database.Migrate();
var configDb = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
configDb.Database.Migrate();
var testClient = new Client
{
ClientId = "TestClient",
RequireClientSecret = false,
AllowOfflineAccess = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedIdentityTokenSigningAlgorithms = new List<string>{SecurityAlgorithms.RsaSha256},
UpdateAccessTokenClaimsOnRefresh = true,
RefreshTokenExpiration = (int)TokenExpiration.Sliding,
AllowedGrantTypes = new List<string>
{
IdentityServerConstants.PersistedGrantTypes.AuthorizationCode
},
AllowedScopes = new List<string>
{
"Read",
"Write"
},
AllowedCorsOrigins = new List<string>
{
"https://localhost:5003"
},
RedirectUris = new List<string>{"https://localhost:5003/callback.html"}
};
configDb.Clients.Add(testClient.ToEntity());
configDb.SaveChanges();
var resource = new ApiResource
{
Name = "TestApi",
ShowInDiscoveryDocument = true,
AllowedAccessTokenSigningAlgorithms = new List<string>{SecurityAlgorithms.RsaSha256},
Scopes = new List<string>
{
"Read",
"Write"
}
};
configDb.ApiResources.Add(resource.ToEntity());
var readScope = new ApiScope("Read");
var writeScope = new ApiScope("Write");
configDb.ApiScopes.AddRange(new []{readScope.ToEntity(), writeScope.ToEntity()});
configDb.SaveChanges();
}
}
login controller
[Route("account/login")]
[Produces("application/json")]
[ApiController]
public class LoginControllerOidc: ControllerBase
{
[HttpGet]
public async Task<IActionResult> Get(string returnUrl)
{
await HttpContext.SignInAsync(new IdentityServerUser("Test")
{
DisplayName = "Test Display Name",
AdditionalClaims = new List<Claim>
{
new Claim("additionalClaim", "claimValue")
}
});
return Redirect(returnUrl);
}
}
config for oidc-client.js
var config = {
authority: "https://localhost:5001",
client_id: "TestClient",
redirect_uri: "https://localhost:5003/callback.html",
response_type: "code",
scope:"Read Write",
post_logout_redirect_uri : "https://localhost:5003/index.html"
};
Redirect Issue Screenshot
I'm at a loss for what it left to do. Following the docs I think I have everything setup correctly. Guides I am following can be found Here (adding javascript client) and Here (sign in)
The cookie is being set correctly (I think) as seen here
The problem that I was having was due to the subjectId in the controller not matching a subjectId in the TestUsers.
I am trying to implement OAuth2 ClientCredentials flow on ASP.NET CORE 3.1. I follow guidelines on the official GitHub repo.
The problem is regarding getting bearer token on Swagger-UI. It doesn't pass automatically. I investigated the issue on Swashbuckle Github repo. There are some closed issues. There's no solution.
Below my implementation :
public static class SwaggerExtensions
{
public static IServiceCollection EnableSwagger(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Sample API", Version = "v1" });
c.AddSecurityDefinition("oauth2",
new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
{
TokenUrl = new Uri("/api/auth/token", UriKind.Relative),
Scopes = new Dictionary<string, string>
{
{ "readAccess", "Access read operations" },
{ "writeAccess", "Access write operations" }
},
}
},
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }, Name = "oauth2"
},
new[] { "readAccess", "writeAccess" }
}
});
c.OperationFilter<OAuth2OperationFilter>();
});
return services;
}
public class AuthenticationAttribute : Attribute, IAsyncAuthorizationFilter
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
bool isAnonymousAllowed = context.Filters.Any(f => f.GetType() == typeof(AllowAnonymousFilter));
if (isAnonymousAllowed)
{
return;
}
if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var authHeaderValue))
{
throw new UnauthorizedAccessException("A valid key must be supplied");
}
string authHeader = authHeaderValue.ToString();
if (string.IsNullOrEmpty(authHeader))
....
}
}
After creating an access_token, I cannot get this token on another controller on Swagger-UI.
Swashbuckle.AspNetCore version -> 5.5.1
Swashbuckle.AspNetCore.Swagger -> 5.5.1
Swashbuckle OAuth2 Authorization with Client Credentials Flow in DotNet Core 2
I want to set Implicit Flow, AuthorizationUrl, different Scopes, default selected Client-id,
so, after clicking authorize, it should navigate to different tab, opening AuthorizationUrl and make user logged in Swagger. So, next time user can see log out option.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info()
{
Title = "",
Description = "All rights reserved."
});
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Flow = "implicit",
AuthorizationUrl = "https://...",
Scopes = new Dictionary<string, string> {
{ "", "Read/Write" }
}
});
});
and Configure() having,
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "iModelAcquisitionService");
});
You can try below steps to enable implict Oauth2 flow :
Change the Startup.cs and replace in the ConfigureServices method the previous added with this:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = "https://login.microsoftonline.com/cb1c3f2e-a2dd-4fde-bf8f-f75ab18b21ac/oauth2/authorize",
Scopes = new Dictionary<string, string>
{
{ "accessApi", "Access read operations" },
},
TokenUrl = "https://login.microsoftonline.com/cb1c3f2e-a2dd-4fde-bf8f-f75ab18b21ac/oauth2/token"
});
});
And replace the following in the Configure method:
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
c.OAuthClientId("19c73866-562f-482a-bafb-89d9fe9b0aaa");
c.OAuthAppName("Swagger Api Calls");
});
Go to the swagger endpoint: http://localhost:xxx/swagger and click Authorize button .