spring boot with swagger OAuth not working - spring-boot

I added swagger dependency and enabled it, and am able to see all the API but authorize API isn't working.
am using below version of 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>
Below is my code :
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Value("${security.oauth2.client.client-id}")
public String CLIENT_ID;
#Value("${security.oauth2.client.client-secret}")
public String CLIENT_SECRET;
public String AUTH_SERVER = "https://login.microsoftonline.com/common/oauth2/v2.0";
#Bean
public Docket swaggerConfiguration() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
//.apis(RequestHandlerSelectors.any())
//.paths(PathSelectors.ant("/api/v1/**/**"))
.apis(RequestHandlerSelectors.basePackage("edu.mayo.ima.ccs.rpc_backend.controller"))
.paths(PathSelectors.any())
.build()
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()))
.apiInfo(getApiInfo());
}
#Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.scopeSeparator(" ")
.useBasicAuthenticationWithAccessCodeGrant(true)
.build();
}
private SecurityScheme securityScheme() {
GrantType grantType = new AuthorizationCodeGrantBuilder()
.tokenEndpoint(new TokenEndpoint(AUTH_SERVER + "/token", "oauthtoken"))
.tokenRequestEndpoint(
new TokenRequestEndpoint(AUTH_SERVER + "/authorize", CLIENT_ID, CLIENT_SECRET))
.build();
SecurityScheme oauth = new OAuthBuilder().name("spring_oauth")
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
}
private ApiInfo getApiInfo() {
return new ApiInfo(
"Protocol Catalag ",
"",
"1.0.0",
"",
null,
"",
"",
Collections.emptyList()
);
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(
Arrays.asList(new SecurityReference("spring_oauth", scopes())))
.forPaths(PathSelectors.any())
.build();
}
private AuthorizationScope[] scopes() {
AuthorizationScope[] scopes = {
new AuthorizationScope("access_as_user", "access for application")
};
return scopes;
}
}
With the above configuration all Api are showing on the swagger but Authorize them give error.
Below is the screen when Authorize buttton is clicked.
Help is Appreciated.!

Please make sure to add the access_as_user permission under API permissions in the portal and make sure the API is exposed.
Application id uri is in the format api://, you can give other name to use in app.
In the Example here I gave app id uri : api://my_spring_boot_api
You should then be able to see added scope under scopes.
Then select the access_as_user permission you have added .(API Permissions>add permission>My APIs > select the required app >check the permission> add permissions)
Then you may grant consent as below
Here I exposed scope >> api://my_spring_boot_api/access_as_user. Make sure to use the same scope configured in portal is included in application configuration.
The scope should include the exposing resource's identifier (the Application ID URI) in the code too.
Here Ex:
scopes: "api://my_spring_boot_api/access_as_user "
and when you call web app please make sure to send Id_token and if you call graph api you may send access token.

Related

Spring security with OAuth2 and Github to only allow developers in an organization to use certain APIs

I have a set of apis that I only want developers in an organization in github to use. To authenticate the user, I've used OAuth of github. But using Spring security with oauth does not seem to do it as it allows developers who are not in the organization to use it. How do I go about this?
It looks like this spring.io tutorial has an example for this exact use case:
#Bean
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService(WebClient rest) {
DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
return request -> {
OAuth2User user = delegate.loadUser(request);
if (!"github".equals(request.getClientRegistration().getRegistrationId())) {
return user;
}
OAuth2AuthorizedClient client = new OAuth2AuthorizedClient
(request.getClientRegistration(), user.getName(), request.getAccessToken());
String url = user.getAttribute("organizations_url");
List<Map<String, Object>> orgs = rest
.get().uri(url)
.attributes(oauth2AuthorizedClient(client))
.retrieve()
.bodyToMono(List.class)
.block();
if (orgs.stream().anyMatch(org -> "spring-projects".equals(org.get("login")))) {
return user;
}
throw new OAuth2AuthenticationException(new OAuth2Error("invalid_token", "Not in Spring Team", ""));
};
}

Spring boot + Swagger UI how to tell endpoint to require bearer token

I'm using Spring Boot to build a REST API. I've added Swagger-ui to handle documentation. I'm having a problem implementation the client authentication flow into swagger, the problem being I can get swagger-ui to authorise a supplied client-id(username) and client-secret(password) via basic auth, but swagger UI doesn't appear to be then applying to resulting access token to endpoint calls.
To confirm, my authorisation process;
- Use basic auth to send base64 encoded username/password & grant_type=client_credentials to /oauth/token. Spring returns an access_token
- On future API calls, use the supplied access_token as the bearer token
I think that the problem may be because I need to place something on each method in my controllers to tell swagger that the endpoint requires authentication and what type, but I can't find any clear documentation on how to do this, and I don't know if I need to apply any further changes to my swagger config.
Here's an example of a controller (with most methods removed to reduce size);
#Api(value="Currencies", description="Retrieve, create, update and delete currencies", tags = "Currencies")
#RestController
#RequestMapping("/currency")
public class CurrencyController {
private CurrencyService currencyService;
public CurrencyController(#Autowired CurrencyService currencyService) {
this.currencyService = currencyService;
}
/**
* Deletes the requested currency
* #param currencyId the Id of the currency to delete
* #return 200 OK if delete successful
*/
#ApiOperation(value = "Deletes a currency item", response = ResponseEntity.class)
#RequestMapping(value="/{currencyId}", method=RequestMethod.DELETE)
public ResponseEntity<?> deleteCurrency(#PathVariable("currencyId") Long currencyId) {
try {
currencyService.deleteCurrencyById(currencyId);
} catch (EntityNotFoundException e) {
return new ErrorResponse("Unable to delete, currency with Id " + currencyId + " not found!").response(HttpStatus.NOT_FOUND);
}
return new ResponseEntity(HttpStatus.OK);
}
/**
* Returns a single currency by it's Id
* #param currencyId the currency Id to return
* #return the found currency item or an error
*/
#ApiOperation(value = "Returns a currency item", response = CurrencyResponse.class)
#RequestMapping(value="/{currencyId}", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<RestResponse> getCurrency(#PathVariable("currencyId") Long currencyId) {
Currency currency = null;
try {
currency = currencyService.findById(currencyId);
} catch (EntityNotFoundException e) {
return new ErrorResponse("Currency with Id " + currencyId + " could not be found!").response(HttpStatus.NOT_FOUND);
}
return new CurrencyResponse(currency).response(HttpStatus.OK);
}
/**
* Returns a list of all currencies available in the system
* #return Rest response of all currencies
*/
#ApiOperation(value = "Returns a list of all currencies ordered by priority", response = CurrencyListResponse.class)
#RequestMapping(value="", method=RequestMethod.GET, produces="application/json")
public ResponseEntity<RestResponse> getCurrencies() {
return new CurrencyListResponse(currencyService.getAllCurrencies()).response(HttpStatus.OK);
}
}
Here is my current swagger config;
#Configuration
#EnableSwagger2
public class SwaggerConfig extends WebMvcConfigurationSupport {
#Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.clientId("12345")
.clientSecret("12345")
.scopeSeparator(" ")
.useBasicAuthenticationWithAccessCodeGrant(true)
.build();
}
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.xompare.moo.controllers"))
.build()
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()))
.apiInfo(metaData());
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(Arrays.asList(new SecurityReference("spring_oauth", scopes())))
.forPaths(PathSelectors.regex("/.*"))
.build();
}
private AuthorizationScope[] scopes() {
AuthorizationScope[] scopes = {
new AuthorizationScope("read", "for read operations"),
new AuthorizationScope("write", "for write operations") };
return scopes;
}
public SecurityScheme securityScheme() {
GrantType grantType = new ClientCredentialsGrant("http://localhost:8080/oauth/token");
SecurityScheme oauth = new OAuthBuilder().name("spring_oauth")
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
}
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Authentication via spring works perfectly at this point, my only problem is getting it working with Swagger UI.
I think that you need to add "Bearer " in front of your key, just like it is shown at this post:
Spring Boot & Swagger UI. Set JWT token
I managed to resolve this by reverting from swagger-ui version 2.8.0 to 2.7.0 after reading the contents of this link which suggested it was a problem with version 2.8.0
https://github.com/springfox/springfox/issues/1961

Protecting API visibility in Swagger and Oauth2 Token Storage in Browser Session/Local Storage

I have created Spring Boot Swagger API using the below configuration. I have created OAUth2 and integrated within my Swagger Configurations. Everything works fine, I am able to see the Authorize button at the top right. All Apis are protected using Oauth2, the user will be able to get the details of the API only after successful authentication.
Now the two issues which I am facing is
After Successful Authentication through OAuth2 (Authorize button), I am able to hit all the services and gets all the response, but when I reload the page the access token which I got after successful authentication is going off.Is there any way to store the access token within the browser session/local storage
Right now all users even without authentication will be able to see all the Apis available in my project when they hit the swagger url. Is there any way in which I can hide those Rest Apis and shows it only after successful
authentication. An Example is given below
Before Authentication
After Authentication
My Spring Boot Swagger Configuration is given below
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket userApi() {
List < ResponseMessage > list = new java.util.ArrayList < > ();
list.add(new ResponseMessageBuilder().code(500).message("500 message")
.responseModel(new ModelRef("Result")).build());
list.add(new ResponseMessageBuilder().code(401).message("Unauthorized")
.responseModel(new ModelRef("Result")).build());
list.add(new ResponseMessageBuilder().code(406).message("Not Acceptable")
.responseModel(new ModelRef("Result")).build());
return new Docket(DocumentationType.SWAGGER_2)
.groupName("otrms-reports-api")
.apiInfo(apiInfo())
.select().apis(RequestHandlerSelectors.basePackage("com.otrms.reports"))
.paths(PathSelectors.any())
.build()
.securitySchemes(newArrayList(oauth()))
.securityContexts(newArrayList(securityContext()))
.globalResponseMessage(RequestMethod.GET, list)
.globalResponseMessage(RequestMethod.POST, list);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("OTRMS")
.description("OTRMS API for Reports")
.termsOfServiceUrl("http://otrms.com")
.contact(contact())
.license("Apache License Version 2.0")
.licenseUrl("http://otrms.com/service/LICENSE")
.version("1.0")
.build();
}
private Contact contact() {
return new Contact("OTRMS", "http://otrms.com", "admin#otrms.com");
}
#Bean
SecurityContext securityContext() {
AuthorizationScope readScope = new AuthorizationScope("read:report", "read your report");
AuthorizationScope[] scopes = new AuthorizationScope[1];
scopes[0] = readScope;
SecurityReference securityReference = SecurityReference.builder()
.reference("report_auth")
.scopes(scopes)
.build();
return SecurityContext.builder()
.securityReferences(newArrayList(securityReference))
.forPaths(ant("/api/pet.*"))
.build();
}
#Bean
SecurityScheme oauth() {
return new OAuthBuilder()
.name("report_auth")
.grantTypes(grantTypes())
.scopes(scopes())
.build();
}
#Bean
SecurityScheme apiKey() {
return new ApiKey("header");
}
List < AuthorizationScope > scopes() {
List < AuthorizationScope > scopes = Lists. < AuthorizationScope > newArrayList();
scopes.add(new AuthorizationScope("resource-access", "Get Resource Access"));
return scopes;
}
List < GrantType > grantTypes() {
GrantType grantType = new ImplicitGrantBuilder()
.loginEndpoint(new LoginEndpoint("http://otrms.com/auth/oauth/authorize"))
.build();
return newArrayList(grantType);
}
#Bean
public SecurityConfiguration securityInfo() {
return new SecurityConfiguration("swaggerClient", "", "reports", "reportstore", "123", ApiKeyVehicle.HEADER, "", " ");
}
}

How to test Rest API call with swagger if needs authentication

I have springfox-swagger2 (version 2.6.1) and springfox-swagger-ui (version 2.6.1) in spring application.
How to I can configure authorization token for calls which needs authorized for continue (how to set X-AUTH-TOKEN for swagger).
Thanks!
Define the following API key as the security scheme. In your cause a header called X-AUTH-TOKEN. We refer to this scheme using the key mykey.
private ApiKey apiKey() {
return new ApiKey("mykey", "X-AUTH-TOKEN", "header");
}
Setup the security context. This just means you're setting up what the authorization scope is for a given path in your API. For e.g. for /anyPath/customers we may require a scope of accessEverything.
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("/anyPath.*"))
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return newArrayList(
new SecurityReference("myKey", authorizationScopes));
}
Then in your docket associate the newly created security context and security schemes.
new Docket(...)
.securitySchemes(newArrayList(apiKey()))
.securityContexts(newArrayList(securityContext()))
Now to enable swagger UI you need to supply the following bean configuration
#Bean
SecurityConfiguration security() {
return new SecurityConfiguration(
"test-app-client-id",
"test-app-client-secret",
"test-app-realm",
"test-app",
"YOUR_API_AUTH_TOKEN",
ApiKeyVehicle.HEADER,
"X-AUTH-TOKEN",
"," /*scope separator*/);
}
This tells the swagger-ui that you're going to use the api key and provide an api auth token at build time (perhaps using an encrypted configuration property.
NOTE: The swagger-ui is limited in its configurability. It serves the 80% of uses cases. Additional customization might mean you won't be able to use the bundled swagger-ui.

How to use ResourceOwnerPasswordCredentialsGrant with swagger ui

I am using swagger, swagger ui with spring rest api to get a platform for testing/documenting the API, so I need to get oAuth2 authorisation working in swagger ui, I am using password grant with the authorisation server, so I had to use ResourceOwnerPasswordCredentialsGrant from the package springfox.documentation.servicewhich has a single parameter to its constructor, namely, the token url, I am setting that to the token endpoint in my authorisation server, but unfortunately, it does not persist token url and shows that as null in the authorisation window as follows:
I could not find any example to use this particular type of grant with swagger ui, any help is much appreciated.
This is my configuration
public Docket oauth() {
return new Docket(DocumentationType.SWAGGER_2).groupName("oauth")
.securitySchemes(Arrays.asList(userOAuthScheme())).securityContexts(Arrays.asList(securityContext()))
.select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any())
.paths(not(ant("/admin/**")))
.paths(not(ant("/admin.json")))
.paths(not(ant("/error/**")))
.paths(not(ant("/exception/**")))
.paths(not(ant("/ping/**"))).build();
}
private OAuth userOAuthScheme() {
List<AuthorizationScope> authorizationScopeList = new ArrayList<AuthorizationScope>();
GrantType grantType = new ResourceOwnerPasswordCredentialsGrant("http://localhost:8080/authServer/oauth/token");
return new OAuth("oauth2", authorizationScopeList, Arrays.asList(grantType));
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.any()).build();
}
#Bean
public SecurityConfiguration securityInfo() {
return new SecurityConfiguration("myClientId", "myClientSecret", "", "", "", ApiKeyVehicle.HEADER, "",
" ");
}
private List<SecurityReference> defaultAuth() {
final AuthorizationScope[] authorizationScopes = new AuthorizationScope[0];
return Arrays.asList(new SecurityReference("oauth2", authorizationScopes));
}
On the Swagger screen take care in the "Setup client authentication" section
Type: Basic auth/ Request Body
It depends on your implementation, in my case works Basic auth.
I dont use scopes but you can add it on
AuthorizationScope[] authorizationScopes
List<AuthorizationScope> authorizationScopeList

Resources