Swagger UI passing authentication token to API call in header - rest

I am new to Swagger.
I am using Swagger UI to generate swagger documentation. I have two API calls. First call is to generate token based on user name and password. Second call needs token generated by first call.
How I set that token for second call using Swagger UI?

#ApiImplicitParams and #ApiImplicitParam should do the trick:
#GET
#Produces("application/json")
#ApiImplicitParams({
#ApiImplicitParam(name = "Authorization", value = "Authorization token",
required = true, dataType = "string", paramType = "header") })
public String getUser(#PathParam("username") String userName) {
...
}
From the documentation:
You may wish you describe operation parameters manually. This can be for various reasons, for example:
Using Servlets which don't use JAX-RS annotations.
Wanting to hide a parameter as it is defined and override it with a completely different definition.
Describe a parameter that is used by a filter or another resource prior to reaching the JAX-RS implementation.
The Swagger UI will be updated so you can send your token from there. No changes to HTML will be necessary.
Note: A while ago, when documenting a REST API with Swagger, I realized that just adding #ApiImplicitParam is not enough (even if you have only one parameter). Anyway, you must add #ApiImplicitParams too.

My configuration for 2.9.2 Swagger version to add Authorization on Swagger UI and send the Bearer token
#Configuration
public class SwaggerConfiguration{
//...
#Bean
public Docket api(ServletContext servletContext) {
return new Docket(DocumentationType.SWAGGER_2)...
.securitySchemes(Arrays.asList(apiKey()))
.securityContexts(Collections.singletonList(securityContext()));
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.regex("/.*")).build();
}
private List<SecurityReference> defaultAuth() {
final AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
final AuthorizationScope[] authorizationScopes = new AuthorizationScope[]{authorizationScope};
return Collections.singletonList(new SecurityReference("Bearer", authorizationScopes));
}
private ApiKey apiKey() {
return new ApiKey("Bearer", "Authorization", "header");
}
}

Another option is to add globalOperationParameters. It will add a field for authorization in every endpoint.
Define authorization header parameter:
Parameter authHeader = new ParameterBuilder()
.parameterType("header")
.name("Authorization")
.modelRef(new ModelRef("string"))
.build();
Add it to Docket configuration:
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(...)
.paths(...)
.build()
.apiInfo(...)
.globalOperationParameters(Collections.singletonList(authHeader));
And it will look like this:

There is a hack that might work by using responseInterceptor and requestInterceptor
First capture response of the the first API call using responseInterceptor and save the token (in the example in local storage), then use requestInterceptor to add the Authorization header with the saved token.
const ui = SwaggerUIBundle({
...
responseInterceptor:
function (response) {
if (response.obj.access_token) {
console.log(response.obj.access_token)
const token = response.obj.access_token;
localStorage.setItem("token", token)
}
return response;
},
requestInterceptor:
function (request) {
console.log('[Swagger] intercept try-it-out request');
request.headers.Authorization = "Bearer " + localStorage.getItem("token");
return request;
}
}

You would have to customise the swagger index page to accomplish that I believe.
You can make the input 'input_apiKey' hidden and add two inputs for username and password. Then you make an ajax call to update the hidden input with your token.

This is an old question but this is how I solved it recently with version 2.7.0 for my JWT tokens
In your swagger configuration, add below SecurityConfiguration bean. Important part being leaving fifth argument empty or null.
#Bean
public SecurityConfiguration securityInfo() {
return new SecurityConfiguration(null, null, null, null, "", ApiKeyVehicle.HEADER,"Authorization","");
}
Add securitySchemes(Lists.newArrayList(apiKey())) to your main Docket bean.
#Bean
public Docket docket()
{
return new Docket(DocumentationType.SWAGGER_2).select()
.....build().apiInfo(...).securitySchemes(Lists.newArrayList(apiKey()));
}
private ApiKey apiKey() {
return new ApiKey("Authorization", "Authorization", "header");
}
Then in UI , you need to click on Authorize button and input "Bearer access_token" (for Authorization text box )where access_token is token provided by jWT token server.
Once this authorization is saved,that will become effective for all end points. Adding a separate text field for each end point looks very cumbersome.

Using SpringDoc with the springdoc-openapi-maven-plugin my option is to use a SwaggerConfig.Java:
#Configuration
public class SwaggerConfiguration {
#Bean
public OpenAPI customOpenAPI(#Value("${project.version}") String appVersion) {
OpenAPI openApi = new OpenAPI();
openApi.info(
new Info()
.title("Title Example")
.version(appVersion)
.description("Swagger server created using springdocs - a library for OpenAPI 3 with spring boot.")
);
openApi.components(
new Components().addSecuritySchemes("bearer-jwt",
new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
.in(SecurityScheme.In.HEADER).name("Authorization"))
);
openApi.addSecurityItem(
new SecurityRequirement().addList("bearer-jwt", Arrays.asList("read", "write"))
);
return openApi;
}
}

Related

ASP.NET Core 6 - Use URI's extension to get Accept header value

I'm migrating an application from NancyFx to Kestrel in ASP.NET Core 6.
In Nancy, you could specify the Accept value in the URI. For example, these Uris:
http://localhost:5000/my/resource.json
http://localhost:5000/my/resource.protobuf
http://localhost:5000/my/resource.xml
Would be the equivalent of setting the Accepts header to application/json, application/protobuf or application/xml respectively.
Does this exist in Kestrel? I remember finding one example, long ago, of regex-ing the route and doing it somewhat manually. But
I can't find that post again, and
If I have to do that, I'm not sure I want to :)
Is there a way to configure this behavior in ASP.NET Core 6?
The object returned from my handler in the controller is already capable of being serialized to json/xml/whatever. I just need to check the URI to set the content-type of the response so the correct formatter will be invoked.
At the moment, I have a client that will speak to both Nancy and Kestrel and it was written to use the URI to get the type. I'm fine to rewrite/update the client so it will use the Accept header. But getting the URI method to work will make the initial integration easier and a refactor to use the headers can come next.
I created a very simple middleware that reads the accept value from the query string and sets the Accept header to the request:
public class AcceptHeaderFromQueryString
{
private readonly RequestDelegate _next;
public AcceptHeaderFromQueryString(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var accept = context.Request.Query["accept"];
if (!string.IsNullOrEmpty(accept))
{
context.Request.Headers.Accept = accept;
}
await _next(context);
}
}
Register the middleware:
app.UseMiddleware<AcceptHeaderFromQueryString>();
I added [Produces(MediaTypeNames.Application.Json, MediaTypeNames.Application.Xml)] attribute to my api controller action (this step is not required):
[HttpGet]
[Produces(MediaTypeNames.Application.Json, MediaTypeNames.Application.Xml)]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
Finally I added support for xml serialization in Program.cs:
builder.Services.AddControllers()
.AddXmlDataContractSerializerFormatters();
Then I tried these urls and they both gave appropriate response:
https://localhost:7258/weatherforecast?accept=application/json
https://localhost:7258/weatherforecast?accept=application/xml
You possibly want the [Consumes] attribute. This allows you to specify a controller action that only gets called from a route of the specified content type.
Obviously this is not using the Accepts header but the content type of the request.

Spring Boot Admin: custom header authentication

My app has a custom authentication mechanism based on a custom HTTP header. AFAIK, Spring Boot Admin supports only Basic auth and OAuth. But maybe there's a way for clients to supply some custom header along with their requests?
You can add the custom headers into existing headers by injecting the Bean as following.
#Bean
public HttpHeadersProvider customHttpHeadersProvider() {
return instance -> {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("X-CUSTOM", "My Custom Value");
return httpHeaders;
};
}
Alright, so if both SBA Server and SBA Client are launched along with the monitored application itself, and it has custom-headers security, we need to take care of 3 things:
As Nitin mentioned, one needs to register HttpHeadersProvider bean:
#Bean
public HttpHeadersProvider customHttpHeadersProvider() {
return instance -> {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("X-CUSTOM", "My Custom Value");
return httpHeaders;
};
}
Note, that these headers are not applied to OPTIONS requests to the Actuator endpoints, so one would either need to customize ProbeEndpointsStrategy, or disable Spring Security for OPTIONS calls to the management URL. Also, for some reason, I had to disable security for /actuator/health/**, although it should've been accessible with custom header provided:
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/actuator/**").antMatchers(HttpMethod.GET, "/actuator/health/**");
}
Finally, one needs to instantiate ApplicationRegistrator with a custom RestTemplate that would be pre-populated with a custom header:
#Bean
public ApplicationRegistrator registrator(ClientProperties client, ApplicationFactory applicationFactory) {
RestTemplateBuilder builder = new RestTemplateBuilder()
.setConnectTimeout(client.getConnectTimeout())
.setReadTimeout(client.getReadTimeout())
.additionalInterceptors((request, body, execution) -> {
request.getHeaders().set("X-CUSTOM", "My Custom Value");
return execution.execute(request, body);
});
if (client.getUsername() != null) {
builder = builder.basicAuthentication(client.getUsername(), client.getPassword());
}
return new ApplicationRegistrator(builder.build(), client, applicationFactory);
}

Using spring-security-oauth2 Authorization Server with kid and JWKS?

Following the documentation here and there, I managed to setup an Authorization Server that gives out JWT access tokens signed with asymmetric key, which are verified locally by a Resource Server using a local copy of the public key. So far so good.
My final goal is for Resource Servers to use the JWKS endpoint on the Authorization Server, and use the 'kid' header in the JWT to lookup the right key in the JWKS and verify locally, supporting key rotation.
I've found how to make the Authorization Server expose a JWKS endpoint, and also how to specify the key-set-uri for the resource server.
However, it seems that there is no way to
publish kid (key id) values in the JWKS
include the kid header in the JWT
Is there a way to do this?
I found a way to set the kid in jwks endpoint:
#FrameworkEndpoint
public class JwkSetEndpoint {
private final KeyPair keyPair;
public JwkSetEndpoint(KeyPair keyPair) {
this.keyPair = keyPair;
}
#GetMapping("/.well-known/jwks.json")
#ResponseBody
public Map<String, Object> getKey() {
RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic();
RSAKey key = new RSAKey.Builder(publicKey)
.keyID("YOUR_KID_HERE")
.keyUse(KeyUse.SIGNATURE).build();
return new JWKSet(key).toJSONObject();
}
}
What I did not find was a way to set it in the header of JWT.
While having the same problem I stumbled upon this post. So i hope it will be useful to someone. I do not think this is the best solution, so maybe some one comes up with a better answer i hope like setting some external bean for example.
Background:
The Jwk store is comparing the KID in the token header with the one in memory if not available it will request the well-known endpoint
So putting the KID in the JwkSetEndpoint will result in a json file with the kid inside.
next to this you need to get the KID on the header of the jwt token.
my solution in my class which extends JwtAccessTokenConverter
#Override
protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String content = null;
try {
content = objectMapper.formatMap(getAccessTokenConverter().convertAccessToken(accessToken, authentication));
} catch (Exception e) {
throw new IllegalStateException("Cannot convert access token to JSON", e);
}
Map<String, String> headers = getJwtHeader();
String token = JwtHelper.encode(content, signer, headers).getEncoded();
return token;
}
next to the KID header the Tokenstore expects a use header set to signing.
i also had to override the signer object because i got stuck with a hmac signer instead of the desired RsaSigner.

How to exclude RequestInterceptor for an specific Spring Cloud Feign client?

I have a number of clients for which a "global" RequestInterceptor has been defined. For one of the clients I need this "global" interceptor to be excluded. Is it possible to override the full set of RequestInterceptors for a particular FeignClient?
#FeignClient(value = "foo", configuration = FooClientConfig.class)
public interface FooClient {
//operations
}
#Configuration
public class FooClientConfig{
//How do I exclude global interceptors from this client configuration?
}
The spring-cloud-netflix version in use is 1.1.0 M5
It seems there is no easy way to override the global interceptor.
I think you could do it like this:
#Configuration
public class FooClientConfig{
#Bean
RequestInterceptor globalRequestInterceptor() {
return template -> {
if (template.url().equals("/your_specific_url")) {
//don't add global header for the specific url
return;
}
//add header for the rest of requests
template.header(AUTHORIZATION, String.format("Bearer %s", token));
};
}
}
Based on the issue stated here. Instead of excluding interceptors, you need to define different feign clients for each API. Add your interceptors based on your needs.
public class ConfigOne {
#Bean
public InterceptorOne interceptorOne(AdditionalDependency ad) {
return new InterceptorOne(ad);
}
}
Just make sure you don't use #Configuration annotation on above class.
Instead, importing this bean on client definition would be a working solution.
#FeignClient(name = "clientOne", configuration = ConfigOne.class)
public interface ClientOne { ... }
An enhanced way of solving this is to pass a custom header to your request like:
#PostMapping("post-path")
ResponseEntity<Void> postRequest(#RequestHeader(HEADER_CLIENT_NAME) String feignClientName, #RequestBody RequestBody requestBody);
I want to set the header in interceptor for only this feign client. Before setting the header, first, the interceptor checks HEADER_CLIENT_NAME header if exists and have the desired value:
private boolean criteriaMatches(RequestTemplate requestTemplate) {
Map<String, Collection<String>> headers = requestTemplate.headers();
return headers.containsKey(HEADER_CLIENT_NAME)
&& headers.get(HEADER_CLIENT_NAME).contains("feign-client-name");
}
Thus, you can check before setting the basic authentication. In interceptor:
#Override
public void apply(RequestTemplate template) {
if (criteriaMatches(template)) {
/*apply auth header*/
}
}
In this way, other feign client's requests won't be manipulated by this interceptor.
Finally, I set the feignClientName to the request:
feignClient.postRequest("feign-client-name", postBody);
One way to do this to remove the #Configuration annotation from the FooClientConfig class as in the current situation it is applied globally.
And then use
#FeignClient(value = "foo", configuration = FooClientConfig.class)
on all of the feign clients you want to use the config with.

Is this the right way to do stateless authentication per call on ServiceStack?

I have REST service requirements in which some calls require authentication and some don't. Absolutely no state is used, as the calls are all independent from one another. I have put something together which seems to work, but is this the right way to go about not using sessions?
This question is kind of related to my WCF question which is answered here.
Firstly I registered the authentication method:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
}
));
I then attribute the respective calls (or service or DTO) with the Authenticate attribute:
[Authenticate]
public HelloResponse Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name + " with POST & Auth"};
}
I inherit from the BasicAuthProvider class which does the authentication:
public class CustomCredentialsAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return userName == "dylan" && password == "abc123";
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
session.IsAuthenticated = true;
//Important: You need to save the session!
authService.SaveSession(session, new TimeSpan(0,0,10));
}
}
As you can see, I do save the session but it times out after 10 seconds. This is the part that I'm sure can potentially be done better. It seems to work nicely though.
Is there a better way of doing what I'm trying to accomplish?
Is there also any way, due to the sessionless nature of these services, to remove the Auth, AssignRoles and UnassignRoles methods?
If you wanted to keep using ServiceStack's Authentication and Session support you could just add a response filter that clears the user session after the service is executed, e.g:
this.ResponseFilters.Add((req, res, dto) => req.RemoveSession());
Basically after each request is executed it clears the session, so no record of them having authenticated exists.
Otherwise you can just skip using ServiceStack's Authentication completely and just provide your own via RequestFitlers of FilterAttributes (which is essentially what SS Auth does under the hood).