Spring Security OAuth2 accessToken - spring

In my Spring Boot application I need to programmatically create a new user and obtain OAuth2 access/refresh tokens for him from internal(part of this application) OAuth2 Authorization Server.
Then, I plan to send these access/refresh tokens to some external (client) application that will interact with my first application on behalf of this user.
Is it possible to programmatically obtain OAuth2 access/refresh tokens for this user without providing password(during the programmatic creation of this user I don't want to deal with password, only username).

Yes you can, take a look at the code below
#Autowired
private TokenEndpoint tokenEndpoint;
public ResponseEntity<?> createToken(User user) {
Principal principal = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword(), user.getAuthorities());
HashMap<String, String> parameters = new HashMap<String, String>();
parameters.put("client_id", "XXX");
parameters.put("client_secret", "XXX");
parameters.put("grant_type", "password");
parameters.put("password", user.getPassword());
parameters.put("scope", "XXX");
parameters.put("username", user.getUserName());
return tokenEndpoint.getAccessToken(principal, parameters);
}
but you are violating the OAuth2 spec. Authorization should be performed by Resource Owner.

Related

Extending a Jhipster JWT (Spring) monolith application to support impersonation

I have generated a jhipster angular/java application that is using JWT authentication.
I now want to extend the application to support impersonation.
I am interested in achieving the following:
Impersonation by admin: Allowing the admin user to login as any other user
Impersonation granted to user: Allowing another user that has been granted the right to impersonate a user (granted by the user itself) to login as that other user.
Audit - recording changes (audit function) - the audit trail must be able to distinguish between the actual user and an impersonated user and record this in the audit trail.
I see that Spring supports impersonation but it is unclear to me how I can implement it properly in my Jhipster application given that JWT is used. I am not sure if the Spring route is appropriate for JHipster-JWT-Monolith application - I am of the opinion that it not the right approach.
While there are some incomplete information on various other posts, after an extensive search I have been unable to find a post that can provide clear step by step guide on this. If somebody can do that for me it would be greatly appreciated. I expect others would also find such an answer very useful.
Thanks in advance.
Fergal
You just need to add below method in UserJwtController.java
#PostMapping("/authenticate-externalnodes")
public ResponseEntity<JWTToken> authenticateExternalnodes(#Valid #RequestBody LoginVM loginVM) {
// Get Roles for user via username
Set<Authority> authorities = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()).get()
.getAuthorities();
// Create Granted Authority Rules
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Authority authority : authorities) {
grantedAuthorities.add(new SimpleGrantedAuthority(authority.getName()));
}
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginVM.getUsername(), "", grantedAuthorities);
Authentication authentication = authenticationToken;
SecurityContextHolder.getContext().setAuthentication(authentication);
boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
String jwt = tokenProvider.createToken(authentication, rememberMe);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}

Authentication of users by authenticationProvider from spring security through ReST API Call

I am now exploring that authentication of users in microservice. For that I am created my authentication service - checkUserAuthentication. Also providing Microservice also. this is already deployed in cloud.
Now I am creating new service with specific business logic. In this service , need to authenticate and check authorization of user to access this end-point by using authenticationProvider from spring security.
For this I am reading and exploring the following tutorials,
https://dzone.com/articles/spring-security-custom
http://roshanonjava.blogspot.in/2017/04/spring-security-custom-authentication.html
http://javasampleapproach.com/spring-framework/spring-security/spring-security-customize-authentication-provider
http://www.baeldung.com/spring-security-authentication-provider
In here they are implements AuthenticationProvider in class CustomAuthenticationProvider.
and in method they are receiving username and password is like following,
public Authentication authenticate(Authentication authentication) throws
AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
Optional<User> optionalUser = users.stream().filter(u -> u.index(name,
password)).findFirst();
if (!optionalUser.isPresent()) {
logger.error("Authentication failed for user = " + name);
throw new BadCredentialsException("Authentication failed for user = " + name);
}
// find out the exited users
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new SimpleGrantedAuthority(optionalUser.get().role));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(name, password,
grantedAuthorities);
logger.info("Succesful Authentication with user = " + name);
return auth;
}
These are codes from documentation. Instead of this method, I need to do in different way. Here I am adding my requirements:
My requirement: I need to receive username and password from API Request.And For checking this username and password, I need to call my deployed APIs checkUserAuthentication and checkUserAuthorization.
My doubts on this:
Can I directly call these API within "public Authentication authenticate(Authentication authentication)" method ?
How I receive username and password from the received request ?
Why we are using UsernamePasswordAuthenticationToken ? , If we are sending JWT token instead of username and password, then which class will use for providing reply?
Since I only started with Spring Security, I am new to security world.
Can I directly call these API within "public Authentication authenticate(Authentication authentication)" method ?
Yes.
How I receive username and password from the received request ?
Same as they are doing in authenticate method.
Why we are using UsernamePasswordAuthenticationToken ? , If we are sending JWT token instead of username and passowrd, then which class
will use for providing reply?
UsernamePasswordAuthenticationToken is used internally by spring security. This
comes into the picture when you create a session in spring. it contains the user information (eg. email etc.) and authorities (role).For example, when you receive a JWT token in your application, you will validate the JWT token (signature etc. ) and upon successfull validation of JWT, you will create an object of UsernamePasswordAuthenticationToken and spring will save it in session. For each incoming request, spring will call boolean isAuthenticated() method on this object to find if user can access the required resource.
Now when you have got all your answers, my recommendation is to go with Oauth2 for your boot microservices. there are plenty of example how to implement it and customize it for your requirement. (Basically, you have to implement your Authorization server which will authenticate the user with your service checkUserAuthentication and generate the accesstoken. Each consumer of your microservice needs to send this accesstoken which they have got from Authorization server and you need to validate it in your microservice. So your microservice will act as Resource Server).
Hope it will help.

Spring OAuth2 JWT additional information and scopes

I have 2 questions about spring jwt token?
The first one is related to the additional informations of the JWT token:
- Is there any way to hide the additional informations from the oauth2 jwt token because they are in plain text and the same informations are duplicated in the JWT access token or payload
public class CustomTokenEnhancer extends JwtAccessTokenConverter {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> additionalInfo = new HashMap<>();
User user = (User) authentication.getPrincipal();
additionalInfo.put("organization", user.getOwnerId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
}
The second one concerns the mapping of my user permissions to access token scopes, in fact, when i add the scopes as additional informations, which represent for my case the different permissions for a given user, and when I want to test this in my WS by #PreAuthorize("hasRole('ROLE_USER') and #oauth2.hasScope('XXXXX')") annotation. It does not work because the checking is based on client scopes rather than user access token scopes? Is there a way, for using access token scopes (which represents my permissions user) rather than client scopes by using the #oauth2.hasScope('XXXXX') annotation? how can i do that?
thanks.

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?

Not getting Client Authority/Role while using RemoteTokenService

I am using Spring-Security-OAuth2 for implementing my own oauth server and resource server. I am using RemoteTokenService as my ResourceServerTokenService on my ResourceServer which will authenticate any accessToken using the CheckTokenEndpoint (/oauth/check_token) on OAuth Server.
I have added a antMatcher for an api url e.g. /data/list which will need client application Role / Authority: "ROLE_ADMIN" like this .antMatcher('/data/list').access("#oauth2.clientHasRole('ROLE_ADMIN')")
but it is not working.
I have done some trial and error on this end point and what I get is following :::
When oauth grant is client only i.e. client_credential grant.
what we get from /oauth/check_token
{
"scope":["read"],
"exp":1412955393,
"client_id":"sample_test_client_app"
}
we dont get any client authority. so how can spring security will perform above authorization check of "#oauth2.clientHasRole('ROLE_ADMIN')"
When oauth grant is user + client i.e. Authorization_code grant
what we get from /oauth/check_token
{
"aud":["resource_id"],
"exp":1412957540,
"user_name":"developer",
"authorities":["ROLE_USER"],
"client_id":"sample_test_client_app",
"scope":["read"]
}
and for authorization_code grnat we are still not getting client authority/role. so can any one tell me how can we perform clientHasRole authentication on any api url?
For "#oauth2.clientHasRole('ROLE_ADMIN')" to work we have to implemented our AccessTokenConverter and inject it into auth server and resource server.
so create a new class which extends DefaultAccessTokenConverter and override convertAccessToken and extractAuthentication methods.
In convertAccessToken method just add
Map<String, Object> response = (Map<String, Object>) super.convertAccessToken(token, authentication);
OAuth2Request clientToken = authentication.getOAuth2Request();
response.put("clientAuthorities", clientToken.getAuthorities());
and in extractAuthentication method add
Collection<HashMap<String, String>> clientAuthorities = (Collection<HashMap<String, String>>) map.get("client_authority");
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for (HashMap<String, String> grantedAuthority : clientAuthorities) {
for (String authority : grantedAuthority.values()) {
grantedAuthorities.add(new SimpleGrantedAuthority(authority));
}
}
Set<String> resourceIds = new LinkedHashSet<String>(map.containsKey(AUD) ? (Collection<String>) map.get(AUD) : Collections.<String> emptySet());
OAuth2Request request = new OAuth2Request(parameters, clientId, grantedAuthorities, true, scope, resourceIds, null, null, null);
At auth server :
set this class in AuthorizationServerEndpointsConfigurer
At resource server :
set this class in RemoteTokenServices

Resources