OAuth2 retrieve client_id in CustomUserDetailsService - spring-boot

I created a Spring Boot application configured to be my Authorization Server oauth2, have other applications Spring boot that are configured as Client oauth2.
In my server I implemented CustomUserDetailsService I would like in the method loadUserByUsername to retrieve the client_id of the project that is being authenticated.
I do not know if this is possible, but I would like to recover this to validate the client_id because I have several applications that use the same authorizer and by client_id I will check if the user has access to that application.
Does anyone know how to do this?
application.yml client
security:
basic:
enabled: false
oauth2:
sso:
loginPath: /login
client:
clientId: web_app
clientSecret: secret
accessTokenUri: https://localhost/uaa/oauth/token
userAuthorizationUri: https://localhost/uaa/oauth/authorize
CustomUserDetailsService
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
//I would like to recover the client_id in this location
User domainUser = userRepository.findByUsername(username);
Have to validate any server because I have a single login for all applications:
Auth server authentication is done by user and password and application.
Imagine that you are logging into "https://accounts.google.com/" google checks user and password and displays your account with shortcuts to "PRODUCTS".
Now if you access "https://mail.google.com" google checks user and password and displays the gmail interface. In my case it can come from the URL https://application.domain.com, https://application2.domain.com, https://application3.domain.com or "https://accounts.domain.com.
All URLs it uses user and password, but it can have access to application1 or all or none.
I would like to do the server side, because it returns the access token only for applications that have permission (credit). If the user and password are correct it does not mean that the user has access to all the applications.

In order to access the client_id you need to Implement a different interface. The CustomUserDetailsService will only give you access to User object.
I don't the exact way to do this in Spring Boot but, in XML-based configuration you will have to do something as follow
public class CustomJdbcClientDetailsService implements
ClientDetailsService, ClientRegistrationService {
#Override
public CustomClientDetails loadClientByClientId(String clientId)
throws InvalidClientException {
//Do something with clientId
}
}
In short the method loadClientByClientId gives you access to the client id. There are other ways that you could intercept different Spring OAuth2 classes and access the client_id but, this is the cleanest and recommended approach.
You might also be able to access the client_id by adding a Filter to intercept the request and check the body.
You need to do some research on how to implement ClientDetailsService and ClientRegistrationService in Spring Boot. There gotta be a ton of tutorials about it.
It's best to start with Googling how to setup ClientDetailsServiceConfigurer with Spring Boot
For more on ClientDetailsServiceConfigurer See Spring OAuth Java Doc
A good tutorial and this one

final SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request, response);
if (null != savedRequest) {
final String redirectUrl = savedRequest.getRedirectUrl();
final MultiValueMap parameters = UriComponentsBuilder.fromUriString(redirectUrl).build().getQueryParams();
if (parameters.containsKey("client_id")) {
final String clientId = parameters.get("client_id").get(0);
}
}

Related

How do I extract the Oauth2 access token in a Spring application using code grant flow?

I have followed this guide.
I have a simple Oauth2 webapp using code grant authorization flow. I have a #GetMapping endpoint (as per the tutorial) that returns user information retrieved from a service provider I'm using. Since this correctly returns my information to the webapp, I can tell the code grant flow has worked. Code for working endpoint:
#GetMapping("/user")
public Map<String, Object> user(#AuthenticationPrincipal OAuth2User principal) {
return principal.getAttributes();
}
Now I want to create a new #GetMapping where I use the access token to query the API of the service provider that just authorized us.
The problem is that the example above somehow magically makes the request for me based solely on configuration and only returns user info. Now I want to get the access token for this session that Spring stores somewhere to access the API however I like "manually".
How can I extract the access token in my new #GetMapping?
I have been searching a lot, but I am new to both Oauth2 and Spring, and there is so much spring classes and tutorials that I "can't see the forest because of all the trees".
I appriciate any help.
Cheers.
I solved it.
Add the following:
#Autowired
private OAuth2AuthorizedClientService authorizedClientService;
private String getSessionBearerToken() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
OAuth2AuthorizedClient client =
authorizedClientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(),
oauthToken.getName());
return client.getAccessToken().getTokenValue();
}
This method will get you the access token when you call it.

#PreAuthorize without username password and UserDetailsService interface

So here is the scenario.
I have login API and it return jwt token to client, this API has annotation #PermitAll.
Then I have another API that is secured and can only be accessed after verifying jwt token. The request to secured API is sent in such a manner that Authorization parameter is added in the header with the value of jwt token and the request is passed to the secured API. Please note that authorization type for this API is No Auth. I wrote a class JwtRequestFilter extends OncePerRequestFilterin which i intercepted the request, extracted the token, verified it, returned appropriate response, it all works well. But there is a problem, and the problem is that there is #PreAuthorize("hasAuthority('RANDOM_PRIV_1')") annotation at the top of the method in the controller class and it is not verified by the JwtRequestFilter and practically it shouldn't be since this is not its job to do that.
I know list of GrantedAuthorities are passed to the implementation of UserDetailsService and it gets verified by method loadUserByUsername. But I cannot use this interface's implementation, reason being, its implementation is called only when authorization type is Basic Auth and I am not using this type of Authentication.
So what I am looking for is an interface/class with I can verify GrantedAuthorities; the way it is verified by UserDetailsService.
If there is not such functionality then I have second working option of extending HandlerInterceptorAdapter and in the preHandle, I verify token as well as verify grantedAuthorities associated with user and verify(manually) them against the PreAuthorize annotation present on the method. This is working for me, but still I am looking for much cleaner solution.
P.S: I went through this link as well but didn't find any useful information.
Spring boot basic authentication without UserDetailsService method.
Any help would be really appreciated.
If you are wanting to use JWTs to authenticate requests, then you can use Spring Security's built-in support for it.
You'll still need to create the JWT yourself, but for processing the JWT and turning it into Granted Authorities, you can do:
http
.authorizeRequests(...
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> {})
);
This tells Spring Security to stand up a filter (BearerTokenAuthenticationFilter) that extracts the token and processes it, probably a lot like your JwtRequestFilter.
Since you are creating the JWT locally (instead of using an Authorization Server, for example), you'll need to tell Spring Security how to verify the token. This is usually done with a symmetric key:
#Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withSecretKey(...).build();
}
And finally, to your question about granted authorities. Assuming, for example, that the authority you mentioned (RANDOM_PRIV_1) is listed as a scope in your JWT, you can construct a JwtAuthenticationConverter bean:
JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
Collection<String> scopes = jwt.getClaim("scope");
return scopes.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
});
return converter;
}
And then add that to the DSL:
http
.authorizeRequests(...
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(jwtAuthenticationConverter())
)
);
Then, when a JWT is presented in the request, Spring Security will process it, extract authorities, and verify that they match the one using the #PreAuthorize annotation.

Is there any advantage using UserDetailsService of Spring Security, when setting membership with JWT?

I'm applying JWT to authenticate the requests. Parsing and Validating works in my Spring Cloud Gateway.
I made a custom filter on SecurityWebFilterChain, which parse and validate the JWT in request header.
I will add this custom filter to ServerHttpSecurity using ServerHttpSecurity.addFilterBefore(myCustomJwtRequestFilter, UsernamePasswordAuthenticationFilter.class).
I want to use
SecurityContextHolder.getContext().setAuthentication(authentication) of Spring Security to authenticate the request.
I found that most of examples of it use UserDetails to make Authentication class.
Most of examples I found use UsernamePasswordAuthenticationToken, and I found that it requires UserDetails. To build UserDetails, it essentially requires username, password, roles.
But in my case, I do not want to validate my jwt with User DB every time I got requests. Also, I do not need the password of user since I will not validate it once I generated Token. I want to use only Username and Roles in JWT payload itself.
In summary, I want to make Authentication class only with username and roles and set it authenticated if parsed jwt is validated with my custom method.
It works well with custom userDetails:
UserDetails userDetails = User.builder().username(String.valueOf(parsedInfo.get("username")))
.authorities(parsedInfo.get("roles")).password("dummypassword").build();
But I have to set Dummy password into it, which I do not need.
I think my solution is not properly applying spring security.
But if I won't use UserDetails, is there benefit to use spring security?
Is there any better solution for my case?
If you just need to validate the JWT token then you can use Spring AOP for that.
#Aspect
#Component
public class JwtAspect {
#Before("execution(* com.yourpackageName.* (..))")
public void checkJwtToken(JoinPoint joinPoint) {
String jwtTOken = request.getToken();
if (null == jwtToken) {
throw new Exception("Token Not Found. ");
}
parseToken(jwtToken);
joinPoint.proceed();
}
}
If you get the token, parse it and also check the expiry. If above everything works fine, you can proceed your JoinPoint.

Attach OAuth2 login to existing users in a Spring Boot application

I have a Spring Boot application that is "invitation only". Ie. users are sent a signup link and there is no "Sign up" functionality. This works fine and users log on with their username and password.
I would like to allow logon with FaceBook and Google using OAuth2 as a supplementary logon method. This would involve mapping the existing users to their social account in some way. The users and their passwords are stored in a MySQL database. I have found a number of articles on OAuth2 and Spring Boot, but none that address this exact use-case.
I can create the Google OAuth2 token/client secret etc, but how do I design the flow to allow only the existing users to logon with their social accounts?
The usernames have been chosen by the users themselves, and are therefore not necessarily the same as their email.
Do I need to create a custom authentication flow in this case?
And do I need to change the authentication mechanism from cookies to JWT tokens?
I found myself in a similar situation, where I needed to know if the OAuth request that I'm receiving is coming from an already authenticated user or not.
Although in my case I needed to know that because I want users to be able to "link" their existing account to social ones.
What I ended up doing was implementing an OAuth2UserService which would have a single method:
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException{
// this will be null if the OAuth2UserRequest is not from an authenticated user
// otherwise, it would contain the current user's principle which you can use to check if the OAuth request should be handled or not
Authentication currentAuth = SecurityContextHolder.getContext().getAuthentication();
// for example:
// if(currentAuth == null)
// throw new OAuth2AuthenticationException(OAuth2ErrorCodes.ACCESS_DENIED);
// Use the default service to load the user
DefaultOAuth2UserService defaultService = new DefaultOAuth2UserService();
OAuth2User defaultOAuthUser = defaultService.loadUser(userRequest);
// here you might have extra logic to map the defaultOAuthUser's info to the existing user
// and if you're implementing a custom OAuth2User you should also connect them here and return the custom OAuth2User
return defaultOAuthUser;
}
then just register the custom OAuth2UserService in your security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// your existing config
.oauth2Login()
.userInfoEndpoint()
.userService(oauthUserService())
;
}
#Bean
public OAuth2UserService oauthUserService(){
return new MyCustomOAuth2UserService();
}

Spring Security: How to use a UserDetailsService with JwtAuthenticationProvider?

I have a REST service, written using Spring MVC. The server is an OAuth2 resource server and I am using the JwtAuthenticationProvider to have the JWT parsed and turned into the Principal. This all works fine.
However, what I really want to do is to load user details from a database, using the username provided from a Claim in the JWT. Then that new Principal should replace or (ideally) wrap the Jwt so that it is available directly from the SecurityContext.
I am really struggling to see how to do this. The JwtAuthenticationProvider does not seem to work with a UserDetailsService. I also looked at doing this with a Converter - but it is not easy to extend JwtAuthenticationConverter because the convert method is final (why?).
So to be very clear, here is what I ideally want to happen:
Bearer token is presented to service.
Parse Jwt and extract claims
Use one of these claims as a key to my user database, where I can look up attributes, entitlements etc
Turn these into a new Principal object which is available in the SecurityContext's Authentication object.
The configure method in my WebSecurityConfigurerAdapter has this:
http.authorizeRequests().antMatchers("/api/*").authenticated().and().oauth2ResourceServer().jwt();
I cannot be the only person who wants to use a user database along with OAuth2, so I must be missing something fundamental? I am using Spring Security 5.2.0.
The JwtAuthenticationProvider does not support an UserDetailsService because in theory you are not supposed to have UserDetails in an application that does not manage credentials. I'm not saying that you cannot have any type of user, but the UserDetailsService will not be used or autowired by Spring Security.
You could register a bean of type JwtAuthenticationConverter, do whatever you need in the convert method and return your custom authentication token, like so:
#Component
public class JwtAuthenticationConverterAdapter implements Converter<Jwt, AbstractAuthenticationToken> {
private JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
#Override
public AbstractAuthenticationToken convert(Jwt jwt) {
var token = this.jwtAuthenticationConverter.convert(jwt);
// build your custom token with the properties from the token above
return customToken;
}
}
I agree with your concerns - and I have found it useful to override Spring's default processing. There is a claims extensibility pattern I use with some providers, where JWT handling is only one part.
I have a Spring Boot code sample that you can run - it uses a custom filter and Connect2Id classes - OAuth integration is described here. Happy to answer any follow up questions if it helps

Resources