I have spring boot rest api (resources) which uses another spring boot authorisation server, I have added Swagger config to the resource application to get a nice and quick documentation/test platform for the rest API. my Swagger config looks like this:
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Autowired
private TypeResolver typeResolver;
@Value("${app.client.id}")
private String clientId;
@Value("${app.client.secret}")
private String clientSecret;
@Value("${info.build.name}")
private String infoBuildName;
public static final String securitySchemaOAuth2 = "oauth2";
public static final String authorizationScopeGlobal = "global";
public static final String authorizationScopeGlobalDesc = "accessEverything";
@Bean
public Docket api() {
List<ResponseMessage> list = new java.util.ArrayList<ResponseMessage>();
list.add(new ResponseMessageBuilder()
.code(500)
.message("500 message")
.responseModel(new ModelRef("JSONResult«string»"))
.build());
list.add(new ResponseMessageBuilder()
.code(401)
.message("Unauthorized")
.responseModel(new ModelRef("JSONResult«string»"))
.build());
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext()))
.pathMapping("/")
.directModelSubstitute(LocalDate.class,String.class)
.genericModelSubstitutes(ResponseEntity.class)
.alternateTypeRules(
newRule(typeResolver.resolve(DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
.useDefaultResponseMessages(false)
.apiInfo(apiInfo())
.globalResponseMessage(RequestMethod.GET,list)
.globalResponseMessage(RequestMethod.POST,list);
}
private OAuth securitySchema() {
List<AuthorizationScope> authorizationScopeList = newArrayList();
authorizationScopeList.add(new AuthorizationScope("global", "access all"));
List<GrantType> grantTypes = newArrayList();
final TokenRequestEndpoint tokenRequestEndpoint = new TokenRequestEndpoint("http://server:port/oauth/token", clientId, clientSecret);
final TokenEndpoint tokenEndpoint = new TokenEndpoint("http://server:port/oauth/token", "access_token");
AuthorizationCodeGrant authorizationCodeGrant = new AuthorizationCodeGrant(tokenRequestEndpoint, tokenEndpoint);
grantTypes.add(authorizationCodeGrant);
OAuth oAuth = new OAuth("oauth", authorizationScopeList, grantTypes);
return oAuth;
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth())
.forPaths(PathSelectors.ant("/api/**")).build();
}
private List<SecurityReference> defaultAuth() {
final AuthorizationScope authorizationScope =
new AuthorizationScope(authorizationScopeGlobal, authorizationScopeGlobalDesc);
final AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Collections
.singletonList(new SecurityReference(securitySchemaOAuth2, authorizationScopes));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(“My rest API")
.description(" description here … ”)
.termsOfServiceUrl("https://www.example.com/")
.contact(new Contact(“XXXX XXXX”,
"http://www.example.com", “xxxx@example.com”))
.license("license here”)
.licenseUrl("https://www.example.com")
.version("1.0.0")
.build();
}
}
The way I get the access token from the Authorisation server is by using http POST to this link with basic authorisation in the header for clientid/clientpass:
http://server:port/oauth/token?grant_type=password&username=<username>&password=<password>
the response is something like:
{
"access_token": "e3b98877-f225-45e2-add4-3c53eeb6e7a8",
"token_type": "bearer",
"refresh_token": "58f34753-7695-4a71-c08a-d40241ec3dfb",
"expires_in": 4499,
"scope": "read trust write"
}
in Swagger UI I can see an Authorisation button, which opens a dialog to make the authorisation request, but it is not working and directing me to a link as following,
http://server:port/oauth/token?response_type=code&redirect_uri=http%3A%2F%2Fserver%3A8080%2Fwebjars%2Fspringfox-swagger-ui%2Fo2c.html&realm=undefined&client_id=undefined&scope=global%2CvendorExtensions&state=oauth
what I am missing here?
I am not sure as what was the issue for you but Authorize button is working for me for swagger version 2.7.0, though I have to get JWT token manually.
First I make a hit for auth token then I insert token like below,
Key here is that my tokens are JWT and I was not able to insert token value after Bearer ** and changing **api_key name to Authorization and that I achieved with below Java configuration ,
There seems a bug in swagger about scope separator which by default is : . In my config, I tried to modify it to
: Bearer
but that is not happening so I have to enter that on UI .This is a bug on swagger-ui 2.6.1 which sends vendorExtensions scope everytime. And that causes the requests get out of scope which results in rejected requests. Since swagger can't get the access token it can't pass oauth2
Upgrading on maven should solve the problem. Minimum version should be 2.7.0
The best way so far to work with oAuth2 Authorisation is by using Swagger Editor, I have installed Swagger Editor quickly in Docker (from here), then used the import parameter to download the API JSON descriptor (your API should include CORS filter), then I can get Swagger Documentation and an interface where I can add a token which I get using curl, postman, or Firefox rest client.
The link which I am using now looks like this
http://docker.example.com/#/?import=http://mywebserviceapi.example.com:8082/v2/api-docs&no-proxy
the interface in Swagger Editor to enter the token looks like this:
if there are better solutions, or workaround please post your answer here.
After 8 months, finally the password flow is supported in Swagger UI, here is the final code and settings which works for me:
1) Swagger Config:
2) in POM use this Swagger UI version 2.7.0:
3) in the application.properties add the following properties:
4) in the Authorisation server add a CORS filter:
If you run with these settings you will get the authorize button in the link http://apiServer.example.com:8080/swagger-ui.html#/ (if you run on 8080) as follows:
Then when you click on the authorize button you will get the following dialogue, add the data for your username/password and the client id and the client secret, the type has to be request body, I am not sure why but this is what works with me, although I thought it should be basic auth as this is how the client secret is sent, anyway this is how Swagger-ui works with password flow and all your API endpoints are working again. Happy swaggering!!! :)