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

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.

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.

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

OAuth2 retrieve client_id in CustomUserDetailsService

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

Spring boot, oAuth2 sign up with facebook

I have been following #Dave Syers' excellent tutorial on Spring boot and oAuth2
I have been able to create a log in function, so that protected resources need a login to facebook before they can be accessed.
But now I am trying to create a "sign up" page. On stackoverflow, for example, there is an option to sign up with facebook, so your details are sent to Stackoverflow.com from facebook. How can this be performed with oAuth2? I was able to do this with spring-social, but I cannot wrap my head around how to do this with a direct oauth2 approach.
Please help?
The answer was simpler than I expected. All I needed to do was add my custom AuthenticationSuccessHandler to the filter:
All I had to do was add an AuthenticationSuccessHandler handle to the method that returns a Filter ssoFilter()
#Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
private Filter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/facebook");
OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
facebookFilter.setRestTemplate(facebookTemplate);
facebookFilter.setTokenServices(new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()));
facebookFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
return facebookFilter;
}
And my CustomAuthenticationSuccessHandler was just a component that extended AuthenticationSuccessHandler
#Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//implementation
}
}
So in my sign up page, I could simply use the same login action, but in the success handler I created the User and stored her in the DB
Make a integrated jwt-oauth2-signup-login is difficult. There are some easy way:
1, to use satellizer-spring-boot, or satellizer.
2,to use spring social.
3, add jwt to spring oauth2 as separate provider:
This is how to do with 3:
I have not use signup+oauth2 yet(Because I like spring social and it can do same function), but in theory it can be done in a very easy and can be done as follow:
First, when user login (Register on facebook will also lead to login page) form facebook, just import the user's information and write the information to user model. It is can be done with a controller and a view.
On front page, it is easy to make user choose to login, or register a new account: As Spring boot support multiple filter and multiple AuthenticationProvider,That means you can use two filters, one for oauth2,and another (jwt local server) filter for local server register.
1,download a standard spring boot jwtFilter.java file and put it in your config directory.
2,Make a controller for register new user.
3, make a /login to return jwt token.
3, make two filter, one for oauth2, one for local jwt.
4, make a Sign up link to /register. and a login tag link to /login.
ps: you can copy all the lines form a standard spring boot jwt project, here is one: https://github.com/mrmodise/senepe

Resources