Cannot send Authorization Bearer Token using Springfox
Asked Answered
F

10

16

I'm having trouble understanding why "Authorization: Bearer __" is not being sent in my api using Springfox 2.5.0. I have the following configuration:

private ApiKey apiKey() {
        return new ApiKey(
                "Authorization", // name: My key - Authorization
                "api_key", // keyname: api_key
                "header");
    }

@Bean
    SecurityConfiguration security() {
        return new SecurityConfiguration(
                null, null, null,
                "Docserver2_fwk", // app name
                "BEARER", // api key value
                ApiKeyVehicle.HEADER, "Authorization", ",");
    }

enter image description here And the curl being sent is:

enter image description here

It seems I am unable to send "Authorization: Bearer Token" in springfox (2.5.0), is this possible?, is it a known problem?

Similar issue: https://github.com/springfox/springfox/issues/1812

PS: OpenAPI 3.0 allows the "bearer" format, example: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#jwt-bearer-sample

Thanks.

Flashcube answered 25/1, 2018 at 12:3 Comment(9)
You should try and move to springfox 2.8.0. and springfox-swagger-ui 2.8.0. It should fix your issues I believe. As far as bearer token you would still need to prefix the token with Bearer Magenmagena
Thank you for the answer, but I am restricted to using a client library that has springfox 2.5.0. The client will not change the springfox version because it will impact several other applications. Although I have no control of springfox version, I am using the latest springfox-swagger-ui.Flashcube
Another thing, If I prefix my token with "Bearer" I obtain "Authorization Bearer: " instead of "Authorization: Bearer" (notice the colon).Flashcube
I meant in the key value: instead of using the key alone use Bearer <key>Magenmagena
Ah yes, that works, however it is not an ideal solution because it will force the people that will use the interface to know that they have to write "Bearer <key>" instead of just pasting the token in the text field.Flashcube
@DilipKrishnan : I upgraded from 2.7.0 to 2.8.0 and my working code stopped working i.e. swagger stopped passing Authorization header. I see lots of folks reporting on github for same issue but I am not able to make out any solution.Zalucki
@SabirKhan please create a new issue or comment on an existing issue that matches your problem.Magenmagena
@DilipKrishnan: Thanks & I would add comment but my issue is same as - thisZalucki
Got it! thanks @SabirKhanMagenmagena
P
22

A simple workaround is to type Bearer than paste the token after it. You will end up with a text box that contains:

Bearer <token>

enter image description here

I wish there was a more automated way. But for now, it appears as though what goes in the text box simple get's pasted into the value section of a given header entry. I suppose the reason the prefix Bearer does not get injected automatically is because then Swagger would be quite opinionated about which sort of authentication its users used!

@Configuration
@EnableSwagger2
class SwaggerConfig {

    @Bean
    Docket api() {

        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build()
                .securitySchemes(securitySchemes())
    }

    private static ArrayList<? extends SecurityScheme> securitySchemes() {

        return [new ApiKey("Bearer", "Authorization", "header")]
    }
}

The REST endpoint method:

@GetMapping("/count")
@ApiOperation(value = "Count the number of entities associated with resource name. This operation does not requires any role." , authorizations = [@Authorization(value = "Bearer")])
def count() {

    count(service)
}

The curl command before logging in:

curl -X GET "http://localhost:8080/category/count" -H "accept: */*"

Response:

{
  "timestamp": "2018-10-29T15:13:02.388+0000",
  "status": 401,
  "error": "Unauthorized",
  "message": "Unauthorized",
  "path": "/category/count"
}

The curl command after logging in:

curl -X GET "http://localhost:8080/category/count" -H "accept: */*" -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9..."

Response:

{
  "message": "There are 0 entities",
  "count": 0
}

Note: My code is in Groovy, I am sure you can translate if you are using standard Java.

Picrate answered 29/10, 2018 at 15:20 Comment(0)
Z
17

I am using 2.8.0 versions and below swagger configuration works for me. I mentioned in comments, everything was working fine for me for version 2.7.0 but then I upgraded to 2.8.0 and jwt token stopped being sent in request header. I am on Spring Boot version - 1.5.2.RELEASE .

Note that I wanted an UI where JWT token could be manually entered by user is format - Bearer ... and token should go in Authorization request header. Its not automatically integrated with OAuth server.

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import com.google.common.base.Predicates;
import com.google.common.collect.Lists;

@Configuration
@EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurerAdapter {

    @SuppressWarnings("unchecked")
    @Bean
    public Docket swaggerPlugin() {
    return new Docket(DocumentationType.SWAGGER_2)
        .select()
        .paths(PathSelectors.any())
        .apis(Predicates.or(RequestHandlerSelectors
            .basePackage("**controller package 1**"),
            RequestHandlerSelectors
                .basePackage("**controller package 2**")))
        .build().directModelSubstitute(LocalDate.class, String.class)
        .genericModelSubstitutes(ResponseEntity.class)
        .apiInfo(apiInfo())
        .securitySchemes(Lists.newArrayList(apiKey()))
        .securityContexts(Arrays.asList(securityContext()));
    }

    private ApiInfo apiInfo() {
    return new ApiInfoBuilder().title("**Comment**")
        .description("**Comment**")
        .termsOfServiceUrl("**Comment**")
        .contact("**Comment**")
        .license("Apache License Version 2.0")
        .licenseUrl("**Comment**").version("0.0.1")
        .build();
    }

    @Bean
    public SecurityConfiguration security() {
    return SecurityConfigurationBuilder.builder().scopeSeparator(",")
        .additionalQueryStringParams(null)
        .useBasicAuthenticationWithAccessCodeGrant(false).build();
    }

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
    registry.addResourceHandler("swagger-ui.html").addResourceLocations(
        "classpath:/META-INF/resources/");
    registry.addResourceHandler("/webjars/**").addResourceLocations(
        "classpath:/META-INF/resources/webjars/");
    }

    private ApiKey apiKey() {
    return new ApiKey("apiKey", "Authorization", "header");
    }

    private SecurityContext securityContext() {
    return SecurityContext.builder().securityReferences(defaultAuth())
        .forPaths(PathSelectors.any()).build();
    }

    private List<SecurityReference> defaultAuth() {
    AuthorizationScope authorizationScope = new AuthorizationScope(
        "global", "accessEverything");
    AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
    authorizationScopes[0] = authorizationScope;
    return Arrays.asList(new SecurityReference("apiKey",
        authorizationScopes));
    }

}

Reference - this github issue answer by JotaroJewstar

Zalucki answered 19/3, 2018 at 7:30 Comment(0)
T
9

As of Springfox 3.0.0, there are dedicated HttpAuthenticationScheme configurations for this kind of authorization.

An example of JWT authorization config:

@Bean
public Docket jwtSecuredDocket() {
    HttpAuthenticationScheme authenticationScheme = HttpAuthenticationScheme
            .JWT_BEARER_BUILDER
            .name("JWT Token")
            .build();
    
    return new Docket(DocumentationType.OAS_30)
            // <...>
            .securitySchemes(Collections.singletonList(authenticationScheme));
}

It is much handier solution because you don't have to put the "Bearer" keyword everytime in the Swagger UI's Auth modal - new mechanism fills that for you (that was a drawback of previous solutions).

Important note

According to the message of the commit introducing this feature, the DocumentationType of the docket has to be set to OAS_30 in order to make this working.

Trakas answered 17/2, 2021 at 14:3 Comment(0)
C
5

I am using latest version of springfox(right now it's 3.0.0), and you can use specific bearer auth with HttpAuthenticationScheme instead of ApiKey:

HttpAuthenticationScheme
    .JWT_BEARER_BUILDER
    .name("JWT")
    .build()
Cantlon answered 6/11, 2020 at 18:56 Comment(6)
In which part do you put this code? Can you give more context? I'm trying to set authorization header in springfox 3.0.0Genitalia
@Genitalia sure. I am applying this while docket initialization in configuration class. Example: lang-kt @Configuration class SwaggerConfig { ... @Bean fun docket(): Docket = Docket(...) .apiInfo(...) ... ..securitySchemes(listOf(HttpAuthenticationScheme.JWT_BEARER_BUILDER...) } Cantlon
This throws up a null pointer exception while mapping the security definitions. Not able to use thisTerrance
does this work with documentationType.Swagger2 or OAS30Terrance
Could not get this working - getting a null pointer. Looks like the error is around here: at springfox.documentation.swagger2.mappers.SecurityMapper.toSecuritySchemeDefinitions(SecurityMapper.java:53) ~[springfox-swagger2-3.0.0.jar:3.0.0]Forras
I had an issue with multiple RequestPart annotations not being rendered correctly. Changed the docket to OAS_30 from SWAGGER2 and that broke JWT auth. After changing security schemes to use this solution instead of ApiKey, the problem is solved. Thank you so much.Townsend
C
3

The solution that worked for me with swagger versión 2.9.2 is the following:

package com.example.springsocial;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;

import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfiguration   {

    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
                    .ignoredParameterTypes(AuthenticationPrincipal.class)
                    .select()
                    .apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot")))
                    .paths(PathSelectors.any()).build()
                    .securitySchemes(Lists.newArrayList(apiKey()))
                    .securityContexts(Arrays.asList(securityContext()));

    }

    private ApiKey apiKey() {
        return new ApiKey("apiKey", "Authorization", "header");
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder().securityReferences(defaultAuth())
            .forPaths(PathSelectors.any()).build();
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope(
            "global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Arrays.asList(new SecurityReference("apiKey",
            authorizationScopes));
        }
}

POM.XML

<!-- SWAGGER  -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>2.9.2</version>
        </dependency>

SecurityConfig.JAVA

package com.example.springsocial.config;

import com.example.springsocial.security.*;
import com.example.springsocial.security.oauth2.CustomOAuth2UserService;
import com.example.springsocial.security.oauth2.HttpCookieOAuth2AuthorizationRequestRepository;
import com.example.springsocial.security.oauth2.OAuth2AuthenticationFailureHandler;
import com.example.springsocial.security.oauth2.OAuth2AuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        jsr250Enabled = true,
        prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private CustomOAuth2UserService customOAuth2UserService;

    @Autowired
    private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;

    @Autowired
    private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;

    @Autowired
    private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;

    @Bean
    public TokenAuthenticationFilter tokenAuthenticationFilter() {
        return new TokenAuthenticationFilter();
    }

    /*
      By default, Spring OAuth2 uses HttpSessionOAuth2AuthorizationRequestRepository to save
      the authorization request. But, since our service is stateless, we can't save it in
      the session. We'll save the request in a Base64 encoded cookie instead.
    */
    @Bean
    public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
        return new HttpCookieOAuth2AuthorizationRequestRepository();
    }

    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(customUserDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .cors()
                    .and()
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                .csrf()
                    .disable()
                .formLogin()
                    .disable()
                .httpBasic()
                    .disable()
                .exceptionHandling()
                    .authenticationEntryPoint(new RestAuthenticationEntryPoint())
                    .and()
                .authorizeRequests()
                    .antMatchers("/",
                        "/error",
                        "/favicon.ico",
                        "/**/*.png",
                        "/**/*.gif",
                        "/**/*.svg",
                        "/**/*.jpg",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        // swagger
                        "/swagger-ui.html**", "/swagger-resources/**",
                        "/v2/api-docs**", "/webjars/**"
                         )
                        .permitAll()
                    .antMatchers("/auth/**", "/oauth2/**")
                        .permitAll()
                    .anyRequest()
                        .authenticated()
                    .and()
                .oauth2Login()
                    .authorizationEndpoint()
                        .baseUri("/oauth2/authorize")
                        .authorizationRequestRepository(cookieAuthorizationRequestRepository())
                        .and()
                    .redirectionEndpoint()
                        .baseUri("/oauth2/callback/*")
                        .and()
                    .userInfoEndpoint()
                        .userService(customOAuth2UserService)
                        .and()
                    .successHandler(oAuth2AuthenticationSuccessHandler)
                    .failureHandler(oAuth2AuthenticationFailureHandler);

        // Add our custom Token based authentication filter
        http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/common/**", "/v2/api-docs", "/configuration/ui", "/swagger-resources",
                "/configuration/security", "/swagger-ui.html", "/webjars/**");
    }

}
Cadastre answered 5/11, 2019 at 21:56 Comment(0)
U
2

I used springfox-swagger2 version-2.9.2 with below configuration and it works fine to pass JWT token via swagger-ui.

private ApiKey apiKey() {    
    return new ApiKey("apiKey", Authorization, "header"); 
}

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2).select()
       .apis(RequestHandlerSelectors.basePackage("com.mycompany.dept.controller"))
       .paths(PathSelectors.any())
       .build().apiInfo(metaData()).securitySchemes(Lists.newArrayList(apiKey()));
}

And in controller we need to use @ApiOperation (value = "Get dummy by id.", authorizations = { @Authorization(value="apiKey") })

Please refer to https://github.com/springfox/springfox/issues/2194

Underpinning answered 3/6, 2019 at 14:33 Comment(0)
A
1

The below solution worked for me in swagger 2.8.0 version.

Add the below code in your Docket configuration

@Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .securitySchemes(Collections.singletonList(new ApiKey("JWT", "Authorization", "header")))
                .securityContexts(Collections.singletonList(
                        SecurityContext.builder()
                                .securityReferences(
                                        Collections.singletonList(SecurityReference.builder()
                                                .reference("JWT")
                                                .scopes(new AuthorizationScope[0])
                                                .build()
                                        )
                                )
                                .build())
                )
                .select()
                .apis(RequestHandlerSelectors
                        .basePackage("com.test.controller"))
                .paths(PathSelectors.regex("/.*"))
                .build().apiInfo(apiEndPointsInfo());
    }

In the text box after clicking Authorize button in the swagger UI, type Bearer "XXXXXXXX(Token)"

Appreciation answered 20/11, 2019 at 12:32 Comment(0)
T
1

Another working solution with springfox 3.0.0.

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.HttpAuthenticationScheme;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
@EnableOpenApi
public class SwaggerConfig {
    
    @Autowired
    private ServletContext context;

    @Bean
    public Docket api() {       
        HttpAuthenticationScheme authenticationScheme = HttpAuthenticationScheme.JWT_BEARER_BUILDER
                .name("Authorization")
                .build();

        return new Docket(DocumentationType.OAS_30)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.ant(context.getContextPath() + "/api/**"))
                .build()
                .securityContexts(Arrays.asList(securityContext()))
                .securitySchemes(Collections.singletonList(authenticationScheme));
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder().securityReferences(defaultAuth()).build();
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Arrays.asList(new SecurityReference("Authorization", authorizationScopes));
    }

}
Tricyclic answered 14/7, 2021 at 10:59 Comment(0)
U
0

Solution given by Jorge Santos Neill worked for me.

Only variation is my SecurityConfig.java as mentioned below

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

@EnableWebSecurity
@EnableResourceServer
@EnableOAuth2Sso
@Configuration

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/v2/api-docs", "/configuration/**", "/configuration/ui/**", "/swagger-resources/**",
            "/configuration/security/**", "/swagger-ui.html", "/webjars/**", "/health", "/csrf");

    }

}
Unreel answered 25/6, 2020 at 9:10 Comment(0)
C
0

facing same issue, just put simple config and working fine now with bearer authentication key. Detail implementation here: https://github.com/mnguyencntt/springboot-webflux-swagger/blob/addBearer/src/main/java/com/example/swagger/SwaggerConfiguration.java

package com.example.swagger;

import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import org.springframework.context.annotation.Configuration;

@Configuration
@SecurityScheme(
        name = "Bearer Authentication",
        type = SecuritySchemeType.HTTP,
        bearerFormat = "JWT",
        scheme = "bearer"
)
public class SwaggerConfiguration {
}
Chloramine answered 10/11, 2022 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.