49

How to enable "Authorize" button in springdoc-openapi-ui (OpenAPI 3.0 /swagger-ui.html) for Bearer Token Authentication, for example JWT.

What annotations have to be added to Spring @Controller and @Configuration classes?

Authorize button

Authorize form for Bearer Token Authentication

Eugene Khyst
  • 9,236
  • 7
  • 38
  • 65

4 Answers4

80

I prefer to use bean initialization instead of annotation.

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info; 
import io.swagger.v3.oas.models.security.SecurityRequirement; 
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

@Configuration
public class OpenApi30Config {

  private final String moduleName;
  private final String apiVersion;

  public OpenApi30Config(
      @Value("${module-name}") String moduleName,
      @Value("${api-version}") String apiVersion) {
    this.moduleName = moduleName;
    this.apiVersion = apiVersion;
  }

  @Bean
  public OpenAPI customOpenAPI() {
    final String securitySchemeName = "bearerAuth";
    final String apiTitle = String.format("%s API", StringUtils.capitalize(moduleName));
    return new OpenAPI()
        .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
        .components(
            new Components()
                .addSecuritySchemes(securitySchemeName,
                    new SecurityScheme()
                        .name(securitySchemeName)
                        .type(SecurityScheme.Type.HTTP)
                        .scheme("bearer")
                        .bearerFormat("JWT")
                )
        )
        .info(new Info().title(apiTitle).version(apiVersion));
  }
}

The line of code

.addSecurityItem(new SecurityRequirement().addList(securitySchemeName))

allows to add global security schema and to get rid of writing security to each @Operation of method.

JenkaBY
  • 911
  • 5
  • 5
  • This works, but didn't get it to work with annotations. The Authorize Button wouldn't show when I used annotations, but I probably missed sth. there. – Stefan Haberl Jun 08 '20 at 09:58
  • Nice solution to avoid security to each `@Operation` of method. – SSK Jun 10 '20 at 06:06
  • 5
    import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; ---- add import command, because there are few ambiguous options – Mr Special Nov 08 '20 at 14:31
  • 1
    If you make global security schema, how do you tell swagger that a particular endpoint is opne and should not be marked as privete? – Luke Mar 12 '21 at 15:18
  • @JenkaBY why did you use the "@Value" annotation inside the constructor? Instead to avoid 5 lines more, putting in the attributes properly? – rios0rios0 Apr 06 '21 at 20:27
  • @rios0rios0 You're right. There is no reason to annotate constructor parameters with "@Value". Feel free to change and improve the code. – JenkaBY Apr 07 '21 at 07:37
  • @JenkaBY I'm sorry, but I can't. The StackOverflow is giving me the error message: "The edit queue is full at the moment - try again in a few minutes!". I tried for one hour. Can you edit, please? – rios0rios0 Apr 07 '21 at 14:35
  • Do all operations use defined bean config by default? Because even if I don't use @SecurityRequirement(name = "some name") the defined authorization is still added in my API operations. So this bugs me. Do I need to add those or not? If I have one authorization type. I suppose it makes sense when you have multiple ways of authorization, would that make generated API operations different? – cvetan May 09 '21 at 17:32
63

Define a global security scheme for OpenAPI 3.0 using annotation @io.swagger.v3.oas.annotations.security.SecurityScheme in a @Configuration bean:

@Configuration
@OpenAPIDefinition(info = @Info(title = "My API", version = "v1"))
@SecurityScheme(
    name = "bearerAuth",
    type = SecuritySchemeType.HTTP,
    bearerFormat = "JWT",
    scheme = "bearer"
)
public class OpenApi30Config {

}

Annotate each @RestController method requiring Bearer Token Authentication (JWT) with @io.swagger.v3.oas.annotations.Operation referencing the defined security scheme:

@Operation(summary = "My endpoint", security = @SecurityRequirement(name = "bearerAuth"))
Eugene Khyst
  • 9,236
  • 7
  • 38
  • 65
  • I am experimenting with this too. I had thought that after I add a bearer token in the authroization dialog that swagger would automatically add this in the headers for all subsequent requests.. but doesnt seem to – 1977 Oct 14 '21 at 12:44
  • Is there a way to configure the @SecurityScheme for https requests? Any ideas will be appreciated. – pratham gn Feb 22 '22 at 13:32
  • Guys don't forget that if you are passing the jwt in the header you have to put "in = SecuritySchemeIn.HEADER" in the SecirityScheme( – lemario Nov 03 '22 at 09:54
32

If you want to avoid annotating each and every @Operation inside your @RestController with the security attribute, you can add this at class level affecting every operation of your controller.

Please don't forget that your configuration bean needs to be the same as in the other example:

@Configuration
@OpenAPIDefinition(info = @Info(title = "My API", version = "v1"))
@SecurityScheme(
    name = "bearerAuth",
    type = SecuritySchemeType.HTTP,
    bearerFormat = "JWT",
    scheme = "bearer"
)
public class OpenApi30Config {
}

Adding security requirement at class level

All you have to do is just use @SecurityRequirement(name = "bearerAuth") on those classes, where you would like to restrict the API calls. Note, that these annotations are inherited, so you can add them to any interface as well.

Create a marker interface with the required annotation:

@SecurityRequirement(name = "bearerAuth")
public interface SecuredRestController {
}

Add the marker interface to those controllers where you would like to apply the restriction to all operations, for example:

@RestController
@RequestMapping("/hello")
public class HelloController implements SecuredController {

    @GetMapping
    public String hello() {
        return "Hello World";
    }

    @GetMapping("/{name}")
    public String helloWithName(@PathVariable String name) {
        return "Hello " + name;
    }

}

You can do this without the marker interface just saying:

@RestController
@RequestMapping("/hello")
@SecurityRequirement(name = "bearerAuth")
public class HelloController {
...
}

Now you have both operations protected and requiring a JWT token. enter image description here

Adding security requirement at method level

As it was said in another post, you have to add the @SecurityRequirement to your @Operation annotation of your method.

@RestController
@RequestMapping("/hello")
public class HelloController {

    @GetMapping
    @Operation(summary = "My endpoint", security = @SecurityRequirement(name = "bearerAuth"))
    public String hello() {
        return "Hello World";
    }

    @GetMapping("/{name}")
    public String helloWithName(@PathVariable String name) {
        return "Hello " + name;
    }

}

This restricts only the first operation, but not the second. enter image description here

Oresztesz
  • 2,294
  • 1
  • 15
  • 26
  • Thanks, you helped me! However, I'd have appreciated if you put your text "Please don't forget that..." rather at the *beginning* of your answer, it would have saved me one hour of debugging :) I was not interested in "Adding security requirement at method level" so I stopped reading your answer there :) – Honza Zidek Sep 14 '21 at 12:23
0

I have found a cleaner solution. It works fine for me.

Thanks to @JenkaBY.

package com.aliyun.horoscope.verse.configuration;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.stereotype.Component;

@Component
public class AuthOpenApiCustomizer implements OpenApiCustomiser {
    @Override
    public void customise(OpenAPI openApi) {
        var securitySchemeName = "bearerAuth";
        openApi.getComponents().addSecuritySchemes(securitySchemeName, new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT"));
        openApi.addSecurityItem(new SecurityRequirement().addList(securitySchemeName));
    }
}
Kazune
  • 1