Shorter story:
How can I convince Swagger UI to send authorization header when changing Spec (swagger end point) if user is authenticated in swagger-ui?
Longer story:
I added oauth2 to swagger.
services.AddSwaggerGen(c =>
{
foreach (var item in list)
{
c.SwaggerDoc($"1.0-{item.Name}", new Info
{
Title = $"1.0-{item.Title}",
Version = $"1.0-{item.Version}"
});
}
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", new string[] { } }
});
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "password",
TokenUrl = this.Configuration["Domain"] + "api/authorization/token"
});
c.IncludeXmlComments(Directory.GetCurrentDirectory() + #"/Files/Web.xml");
});
An Authorize button is shown and I can login with username, password.
When I call any API controller method (GET, POST, PUT, DELETE) authorization header is added to Request headers:
accept: text/plain
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9,en-US;q=0.8,sl;q=0.7
authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...
Cache-Control: no-cache
But when I change specification:
a new request is made to server for swagger.json file (swagger end point).
Problem is this request doesn't have auhorization header. How can I add this header, when requesting swagger end-point?
I am trying to follow official documentation to filter result based on authenticated user. (Show empty result for unauthorised user.)
Something like that:
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
{
var isLoggedIn = httpReq.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);
});
});
But Claims are empty, because authorization header is missing.
Related
I am trying to get OAuth code flow with PCKE to work with Swashbuckle (6.2.3) and swagger ui in .NET 6. There are a few things that happen successfully:
In swagger UI I can click on "Authorize" button and get redirected to Azure for login.
The redirect successfully returns to swagger ui and I can see in the network tab that the token is retrieved from Azure by swagger ui.
The problem is when I try to call the sample weather forecast API using swagger UI, no token is attached to the authorization header and it looks like this in the request:
authorization: Bearer undefined
And here is my code:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C"));
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
const string oAuth2 = "oauth2";
options.AddSecurityDefinition(oAuth2, new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri(builder.Configuration["AzureAdB2C:AuthorizationUrl"]),
TokenUrl = new Uri(builder.Configuration["AzureAdB2C:TokenUrl"]),
Scopes = {{"openid", "Sign users in"}, {"offline_access", "Maintain access to data you have given it access to"}}
}
},
In = ParameterLocation.Header,
BearerFormat = "JWT",
Scheme = "bearer",
Name = "authorization"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Id = oAuth2,
Type = ReferenceType.SecurityScheme
},
}, new List<string> {"openid", "offline_access"}
}
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.OAuthClientId(builder.Configuration["AzureAdB2C:ClientId"]);
options.OAuthScopes("openid", "offline_access");
options.OAuthUsePkce();
});
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
I'm not sure what I'm missing. Any ideas?
UPDATE:
I have been able to get it to work with something like this:
options.UseRequestInterceptor("(req) => { req.headers['Authorization'] = 'Bearer ' + window?.swaggerUIRedirectOauth2?.auth?.token?.id_token; return req; }");
But it doesn't look like a proper solution.
You can specify in the OpenApiSecurityScheme to use the id_token instead the access_token that is the default by adding it to the Extensions:
Extensions =
{
// Setting x-tokenName to id_token will send response_type=token id_token and the nonce to the auth provider.
// x-tokenName also specifieds the name of the value from the response of the auth provider to use as bearer token.
{ "x-tokenName", new OpenApiString("id_token") }
}
Source: https://github.com/inouiw/SwaggerUIJsonWebToken/blob/master/Program.cs
I have a ASP.NET MVC application with framework 4.7.2. The application is configured to use IdentityServer3 using OpenIDConnect. When user clicks on Logout button the following code is invoked
Action Method The logout action method get invoked first.
[HttpPost]
public ActionResult Logout()
{
Session.Clear();
if (Request.IsAuthenticated)
{
Request.GetOwinContext().Authentication.SignOut();
}
return Redirect("/");
}
In Owin Startup.cs i have configured OpenIDConnect. The RedirectToIdentityProvider event fires next.
Here, I am setting IdTokenHint when RequestType is Logout.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var cookieOptions = new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
LoginPath = new Microsoft.Owin.PathString("/Home"),
SlidingExpiration = true,
ExpireTimeSpan = GetCookieExpiration()
};
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["id:Authority"],
Scope = "openid email profile",
ClientId = "My ClientId",
RedirectUri = "http://localhost:58641/Home",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = (context) =>
{
//code here removed for brevity
return Task.FromResult(0);
},
RedirectToIdentityProvider = (context) =>
{
if (context.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = context.OwinContext.Authentication.User.FindFirst("id_token").Value;
context.ProtocolMessage.IdTokenHint = idTokenHint;
}
return Task.FromResult(0);
}
}
};
app.UseCookieAuthentication(cookieOptions);
app.UseOpenIdConnectAuthentication(openIdOptions);
MvcHandler.DisableMvcResponseHeader = true;
}
I fiddler i see it makes a call to
/identity/connect/endsession?id_token_hint= xxxxxxxx However, the HTTP Verb its using
is OPTIONS. So the IdentityServer throws error The requested resource does not support http method 'OPTIONS'
Not sure what i am missing Here.
Edit 1
In browser console i see the following error
Access to XMLHttpRequest at
'https://localhost:44300/identity/connect/endsession?id_token_hint=xxxxxxx'
(redirected from 'http://localhost:58641/account/logout') from origin
'http://localhost:58641' has been blocked by CORS policy: Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
Edit 2
I have another ASP.NET Application that has the same logout code. But its making GET request to endsession.
When you see the use of OPTIONS and the request contains the origin header, then that is a CORS preflight request. This is an extra security request that occurs when a JavaScript client tries to make an AJAX request to an API.
Is this intended to trigger the endsession from JavaScript? if so, you need for that client in identityServer set:
AllowedCorsOrigins =
{
"https://localhost:xxxxx"
},
This is set per client in IdentityServer.
I have a login form with username and password. I'm trying to validate these credentials using the Nest Js authentication strategy here. So in the corresponding auth.service.ts file, I'm using "nativescript core modules http" to do a POST request to OAuth URL to validate credentials . But this doesn't work:
import { Injectable } from '#nestjs/common';
import { request } from "tns-core-modules/http";
const OAUTH_URL = 'url';
#Injectable()
export class AuthService {
async validateUser(username: string, password: string): Promise<any> {
let data = new FormData();
data.set('client_id', 'sb-nestjs-app!t36258');
data.set('client_secret', 'XrHuBRhyvuVNYNJNHlWLgcuBIyc=');
data.set('username', username);
data.set('password', password);
data.set('grant_type', 'password');
data.set('response_type', 'token');
request({
url: OAUTH_URL,
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json;charset=utf8"
},
content: data
}).then((response) => {
console.log('response => ' + response + ' statuscode ' + response.statusCode);
if (response.statusCode == 200) {
const token = response.content['access_token'];
//TODO:
// need to send scope also
return token;
}
}, (e) => {
console.log('error' + e);
return null;
});
return null;
}
}
When I run 'nest start' after above code in place, I receive Error: Cannot find module './http-request'
I'm not sure what is going here, I tried "npm install http-request" it didn't work either. Basically, I need to POST credentials to a OAuth url in NestJs. Any guidance? Thank you.
Try with HttpModule from NestJS.
Also you can try request from npm, but they deprecated this package. From what I saw on their discussion, the package still works but you will not have support for it, or anything. Here are some alternatives to it .
I'm not sure you are using the correct request npm module. I'm talking about:
import { request } from "tns-core-modules/http"
Good Luck!
i use springfox 2.9.2
I have api like:
#Api(tags = "Users")
#RestController
#RequestMapping("users")
public class UsersController {
#ApiOperation(value = "Creates a user")
#ApiResponses(value = {
#ApiResponse(code = 201, message = "user created"),
#ApiResponse(code = 401, message = "not authorized")})
#PostMapping(value = "/add")
public ResponseEntity addUser(#Valid #RequestBody UserDTO userDTO) {
...
}
to make this call user needs authorization token
Authorization: Bearer {token}
witch comes from authentication server.
i try to make first call to this server in swagger and pass it to controller requests like the one above.
So i do
#Bean
public Docket api() {
final String swaggerToken = "";
return new Docket(DocumentationType.SWAGGER_2)
#Bean
public .select()
.apis(RequestHandlerSelectors.basePackage("com.mbv.coros.notification.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiEndPointsInfo())
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()))
.useDefaultResponseMessages(false);
}
private SecurityScheme securityScheme() {
GrantType grantType = new ResourceOwnerPasswordCredentialsGrant(AUTH_SERVER + "/token");
SecurityScheme oauth = new OAuthBuilder().name("spring_oauth")
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Lists.newArrayList(
new SecurityReference("JWT", authorizationScopes));
}
on Swagger ui authorization call returns the token successfully but it doesnt add it to request headers. It generates
curl -X GET "http://localhost:8080/users/get" -H "accept: */*"
if i set token like:
.securitySchemes(Arrays.asList(apiKey()))
private ApiKey apiKey() {
return new ApiKey("JWT", AUTHORIZATION_HEADER, "header");
}
it works perfectly.
any ideas why is this happening?
As far as I know, Swagger uses token only if you configure it to do so, and the configuration is done using the "Authorize" button on the top right of the Swagger UI page.
So, the ideal case would be:
Trigger Auth call which returns the token
Copy the token; Click on "Authorize" button and paste the JWT token in "Bearer "
After this, all the subsequent calls are supposed to use this token until you press logout.
My breeze services works great. But I just moved it behind a WSO2 API Manager. It now needs a Bearer Token for each call.
I have the Bearer Token. But I can't seem to figure out how to add it to the Metadata Call.
I tried something like this. But it did not add a header to the metadata call:
var ajaxAdapter: any = breeze.config.getAdapterInstance('ajax');
ajaxAdapter.defaultSettings = {
headers: {
"X-Test-Header": "foo2"
}
}
Does the fetchMetadata use a different system from the ajax adapter?
How can I add a header to the Fetch Metadata call?
Turns out I was using the Fetch API. So I had to do it that way. Here is what my setup looks like:
setupFetchClient() {
let httpClient = new HttpClient();
httpClient.configure(config => {
config.withDefaults({
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
}
})
.withInterceptor({
request(request: Request) {
let accessToken = getAccessToken();
request.headers.append('Authorization', 'Bearer ' + accessToken);
return request;
},
responseError(error) {
return error;
}
})
.useStandardConfiguration();
});
// Aurelia Specific Code.
Container.instance.registerInstance(HttpClient, httpClient);
}