Incorrect link in Swagger - asp.net-core

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

Can't authorize swagger through my Authorization Server using OIDC

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 does not display the API correctly

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?

IdentityServer4 endless redirect after login

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.

Bearer token doesnot pass on swagger

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

How to do OAuth2 Authorization in ASP.NET Core for Swagger UI using Swashbuckle

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 .