question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Swagger UI accessible only for authenticated users or user has authority (or role)

See original GitHub issue

Hi, I am pretty new with swagger and springfox.

Version of springfox-swagger2 and springfox-swagger-ui is 2.8.0.

Requirements:

  1. Secure Swagger UI and allow only specific role (Authority)

My application is currently using JWT Tokens for authentication and authorization and it is working fine. My Swagger UI is accessable publicly, which I do not want to expose when my application is deployed in production. Only specific users based on role/authority can access Swagger UI page.

I can now add JWT token via Authorize button, then I can see the Authorize icon button to change to Locked. Once the icon is locked, Try it out works and the JWT token is passed in the header as Bearer access token. Once refreshed… the access token is reset.

Not sure if I can use the existing spring security configurations of my application and apply it to swagger-ui. I already did, but with no success (see configuration below). I can manage to set swagger-ui accessable to the public (using permitAll() or web ignore configuration), but setting it to allow authenticated user (very basic), it wont’t work… how much more if I wanted to allow specific user role or authority to access swagger-ui.

I would like to start with securing swagger-ui to allow authenticated users (authenticated on my application) only, then later on specifying role/authority.

btw, how do I label this as help wanted?

Thanks!

Dependency (pom.xml):

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

Security Configuration class

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

    ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // I am use JWT authentication and authorization to API
        JWTAuthenticationFilter authenticationFilter =
                new JWTAuthenticationFilter(authenticationManager(), appContext);
        authenticationFilter.setFilterProcessesUrl("/auth/form");

        JWTAuthorizationFilter authorizationFilter =
                new JWTAuthorizationFilter(authenticationManager(), appContext);

        http
            .cors().and().csrf().disable() // no need CSRF since JWT based authentication
            .authorizeRequests()

            ...

            // If I use permitAll(), swagger-ui will be accessible,
            // even web ignore configuration below is not used
            //.antMatchers("/swagger-ui.html**").permitAll()

            // This won't work even if I remove web ignore configuration below
            // I want to start with allowing authenticated users only
            // Using this will make swagger-ui not accessible
            //.antMatchers("/swagger-ui.html**").authenticated()

            // Later on, I want to allow specific role or authorization
            //.antMatchers("/swagger-ui.html**").hasAnyAuthority(
            //    MyConstants.RoleType.ADMINISTRATOR.toString(),
            //    MyConstants.RoleType.DEVELOPER.toString())

            ...

            .anyRequest().authenticated()

            .and()
                .addFilter(authenticationFilter)
                .addFilter(authorizationFilter)

            // this disables session creation on Spring Security
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

            .and().exceptionHandling().authenticationEntryPoint(new MyAuthenticationEntryPoint());
    }

    // This is what I used to make swagger be accessable publicly
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/", "/v2/api-docs", "/configuration/ui", "/swagger-resources/**",
                "/configuration/**", "/swagger-ui.html", "/webjars/**");
    }

    ...

}

MyAuthenticationEntryPoint

@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private final Logger logger = LoggerFactory.getLogger(MyAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
            AuthenticationException e) {
        logger.debug("Pre-authenticated entry point called. Rejecting access");
        List<Message> errorMessagesList = Arrays.asList(new Message("Unauthorized access"));
        CommonErrorResponse commonErrorResponse =
                new CommonErrorResponse(errorMessagesList, HttpServletResponse.SC_UNAUTHORIZED);
        try {
            String json = Util.objectToJsonString(commonErrorResponse);
            httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
            httpServletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            httpServletResponse.getWriter().write(json);
        } catch (Exception e1) {
            logger.error("Unable to process json response: " + e1.getMessage());
        }
    }

}

Swagger Configuration

@EnableSwagger2
@Configuration
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(metadata())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.iyotbihagay.controller"))
                .paths(PathSelectors.any())
                .build()
                .securitySchemes(Collections.singletonList(apiKey()))
                .securityContexts(Collections.singletonList(securityContext()))
                .useDefaultResponseMessages(false);
    }

    @Bean
    SecurityConfiguration security() {
        return SecurityConfigurationBuilder.builder()
                .clientId(null)
                .clientSecret(null)
                .realm("My Realm")
                .appName("My System")
                .scopeSeparator(null)
                .additionalQueryStringParams(null)
                .useBasicAuthenticationWithAccessCodeGrant(false)
                .build();

    }

    List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope
                = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Collections.singletonList(new SecurityReference("JWT Token", authorizationScopes));
    }

    private ApiInfo metadata() {
        return new ApiInfoBuilder().title("Iyot Bihagay API Documentation")
                .description("API documentation for Iyot Bihagay REST Services.").version("1.6.9").build();
    }

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

    private SecurityContext securityContext() {
        // Optional swagger-ui security configuration for oauth and apiKey settings
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                // Selector for the paths this security context applies to.
                // Honestly, I don't understand this... should I place my API endpoints here? like `/api/users/**`
                .forPaths(PathSelectors.regex("/anyPath.*"))
                .build();
    }

}

On every API (Controller), add authorizations = { @Authorization(value="JWT Token") } like:

    @ApiOperation(value = "Creates an inquiry type", authorizations = { @Authorization(value="JWT Token") })
    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public Object create(@Valid @RequestBody InquiryType inquiryType, BindingResult bindingResult) throws Exception {
        ...
    }

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
hypery2kcommented, Feb 13, 2019

@dilipkrish Any news how to handle this?

1reaction
ketannaberacommented, Aug 10, 2020

@dilipkrish thanks for the update. I figured out the issue was with the path regex that i was providing. I am not sure how that regex was evaluated. But i got it to work now after trying a couple of combinations. (y)

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to display required user roles (access control information ...
Now, I have no idea how I can display in my swagger-ui those roles, so each endpoint has information, what user role is...
Read more >
Authentication - Swagger
Authentication is described by using the securityDefinitions and security keywords. You use securityDefinitions to define all authentication types supported by ...
Read more >
Swagger UI with login form and role-based api visibility
Show api documentation only for authenticated users with a custom UI who acquire user credentials and generate a JWT token.
Read more >
Using Authorization with Swagger in ASP.NET Core
We use the Authorize attribute when we want to restrict access to an action method and ensure that only authenticated users can access...
Read more >
Enabling user authentication in Swagger using Microsoft Identity
This should be a list of scopes that you want your Swagger UI to have access to. BTW, this should only been done...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found