How to use ResourceOwnerPasswordCredentialsGrant with swagger ui - spring-boot

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

Related

How to create custom claims in JWT using spring-authorization-server

I'm building an OAuth2 authorization server based on the experimental Spring project Spring Authorization Server
My use case is quite simple, fetch users from a DB, and based on some properties of the user, set some custom claims in the JWT being produced.
I haven't found a way to do so with Spring Authorization Server, the only way I could work out is to inject a jwtCustomizer object as part of the JwtEncoder bean definition:
#Bean
public JwtEncoder jwtEncoder(CryptoKeySource keySource) {
NimbusJwsEncoder jwtEncoder = new NimbusJwsEncoder(keySource);
jwtEncoder.setJwtCustomizer((headersBuilder, claimsBuilder) -> {
// Inject some headers and claims...
});
return jwtEncoder;
}
This obviously doesn't give me access to users information, therefore I can't set the claims I need at this point.
Did anyone manage to solve this problem?
The solution for this is in a test of the library
#Bean
OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return context -> {
if (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) {
Authentication principal = context.getPrincipal();
Set<String> authorities = principal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
context.getClaims().claim(AUTHORITIES_CLAIM, authorities);
}
};
}
You can try following way. Though it is Kotlin code, not Java, but approach should be clear:
import org.springframework.security.oauth2.provider.token.TokenEnhancer
class UserTokenEnhancer : TokenEnhancer {
override fun enhance(accessToken: OAuth2AccessToken,
authentication: OAuth2Authentication): OAuth2AccessToken {
val username = authentication.userAuthentication.name
val additionalInfo = mapOf( /* populate with some data for given username */ )
(accessToken as DefaultOAuth2AccessToken).additionalInformation = additionalInfo
return accessToken
}
}
Then just register bean:
#Bean
fun userTokenEnhancer(): TokenEnhancer {
return UserTokenEnhancer()
}

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", ""));
};
}

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.

Setting OAuth2 token for RestTemplate in an app that uses both #ResourceServer and #EnableOauth2Sso

On my current project I have an app that has a small graphical piece that users authenticate using SSO, and a portion that is purely API where users authenticate using an Authorization header.
For example:
/ping-other-service is accessed using SSO.
/api/ping-other-service is accessed using a bearer token
Being all cloud native our app communicates with other services that uses the same SSO provider using JWT tokens (UAA), so I figured we'd use OAuth2RestTemplate since according to the documentation it can magically insert the authentication credentials. It does do that for all endpoints that are authenticated using SSO. But when we use an endpoint that is authed through bearer token it doesn't populate the rest template.
My understanding from the documentation is that #EnableOAuth2Client will only extract the token from a SSO login, not auth header?
What I'm seeing
Failed request and what it does:
curl -H "Authorization: Bearer <token>" http://localhost/api/ping-other-service
Internally uses restTemplate to call http://some-other-service/ping which responds 401
Successful request and what it does:
Chrome http://localhost/ping-other-service
Internally uses restTemplate to call http://some-other-service/ping which responds 200
How we worked around it
To work around this I ended up creating the following monstrosity which will extract the token from the OAuth2ClientContext if it isn't available from an authorization header.
#PostMapping(path = "/ping-other-service")
public ResponseEntity ping(#PathVariable String caseId, HttpServletRequest request, RestTemplate restTemplate) {
try {
restTemplate.postForEntity(adapterUrl + "/webhook/ping", getRequest(request), Map.class);
} catch (HttpClientErrorException e) {
e.printStackTrace();
return new ResponseEntity(HttpStatus.SERVICE_UNAVAILABLE);
}
return new ResponseEntity(HttpStatus.OK);
}
private HttpEntity<?> getRequest(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getRequestToken(request));
return new HttpEntity<>(null, headers);
}
private String getRequestToken(HttpServletRequest request) {
Authentication token = new BearerTokenExtractor().extract(request);
if (token != null) {
return (String) token.getPrincipal();
} else {
OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
if (accessToken != null) {
return accessToken.getValue();
}
}
throw new ResourceNotFound("No valid access token found");
}
In the /api/** resources there is an incoming token, but because you are using JWT the resource server can authenticate without calling out to the auth server, so there is no OAuth2RestTemplate just sitting around waiting for you to re-use the context in the token relay (if you were using UserInfoTokenServices there would be one). You can create one though quite easily, and pull the incoming token out of the SecurityContext. Example:
#Autowired
private OAuth2ProtectedResourceDetails resource;
private OAuth2RestTemplate tokenRelayTemplate(Principal principal) {
OAuth2Authentication authentication = (OAuth2Authentication) principal;
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
details.getTokenValue();
OAuth2ClientContext context = new DefaultOAuth2ClientContext(new DefaultOAuth2AccessToken(details.getTokenValue()));
return new OAuth2RestTemplate(resource, context);
}
You could probably turn that method into #Bean (in #Scope("request")) and inject the template with a #Qualifier if you wanted.
There's some autoconfiguration and a utility class to help with this pattern in Spring Cloud Security, e.g: https://github.com/spring-cloud/spring-cloud-security/blob/master/spring-cloud-security/src/main/java/org/springframework/cloud/security/oauth2/client/AccessTokenContextRelay.java
I came across this problem when developing a Spring resource server, and I needed to pass the OAuth2 token from a request to the restTemplate for a call to a downstream resource server. Both resource servers use the same auth server, and I found Dave's link helpful but I had to dig a bit to find out how to implement this. I ended up finding the documentation here, and it turn's out the implemetation was very simple. I was using #EnableOAuth2Client, so I had to create the restTemplate bean with the injected OAuth2ClientContext and create the appropriate resource details. In my case it was ClientCredentialsResourceDetails. Thanks for all great work Dave!
#Bean
public OAuth2RestOperations restTemplate (OAuth2ClientContext context) {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
// Configure the details here
return new OAuth2RestTemplate(details, context)
}
#Dave Syer
My UAA service is also an oauth2 client, which needs to relay JWT tokens coming in from Zuul. When configuring the oauth2 client the following way
#Configuration
#EnableOAuth2Client
#RibbonClient(name = "downstream")
public class OAuthClientConfiguration {
#Bean
public OAuth2RestTemplate restTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
}
I do get a 401 response from the downstream service as my access token has a very short validity and the AccessTokenContextRelay does not update an incoming access token (Zuul does renew expired access tokens by the refresh token).
The OAuth2RestTemplate#getAccessToken will never acquire a new access token as the isExpired on the access token stored by the AccessTokenContextRelay drops the validity and refresh token information.
How can this by solved?

Resources