I am using SpringBoot 2.0.5 and Spring Security OAuth to implement an OAuth 2.0 server and a set of client microservices.
In the AuthServer:
I have implemented the UserDetailsService so I can provide my custom enriched principal.
For the userInfoUri controller endpoint, I return user (my principal) and authorities as a map.
In the Client:
I have implemented PrincipalExtractor to extract and create my custom principal.
For each of the methods I require the principal, I use the following notation:
public List<Message> listMessages(#AuthenticationPrincipal MyPrincipal user)
This works (and I hope it's the right way) but now I'm having an issue to secure methods using scopes.
For example, if I want to have a controller method which is only accessible by another server (using client_credentials), I mark the method with the following annotation:
#PreAuthorize("#oauth2.hasScope('trust')")
But this results in an access error as I think the scope is not being transferred. I have added the scope to the userInfoUri endpoint but am unsure what I need to do on the client side so the scope is picked up.
Any pointers or example code would be very much appreciated.
Related
I'm building an OAuth2 authenticated app using Spring Boot, following this tutorial: https://spring.io/guides/tutorials/spring-boot-oauth2/
At one point, the endpoint /user sends back the currently logged in user.
The guide warns by saying:
"It’s not a great idea to return a whole OAuth2User in an endpoint since it might contain information you would rather not reveal to a browser client."
But it doesn't give any more information - what type of information should I not be revealing to a browser client?
Thanks!
In Spring Security 5.x, the OAuth2User is a specific OAuth2AuthenticatedPrincipal (very similar to a UserDetails but without any notion of a password). Even without a password, exposing it can (and often will) leak sensitive information, implementation details of your authentication scheme, etc. You can expose it if you choose, but the warning in the guide is suggesting that care should be taken so as not to expose anything considered sensitive, and you should consider alternatives before exposing it directly.
For example, you might consider creating a CustomUser class that is populated from claims on the OAuth2User using a custom OAuth2UserService (various examples in the advanced configuration section of the docs). You can also take various steps to decouple the representation of an oauth2 user in Spring Security from the representation of a user in your application (e.g. by using #AuthenticationPrincipal to resolve your own custom user or access claims). If the application itself does not need a custom user, you can simply map claims of the OAuth2User to a response in your custom endpoint, as demonstrated in the guide.
Finally, you can combine all of these techniques to make your /user endpoint a "one liner" again, as in:
#Target({ElementType.PARAMETER, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#AuthenticationPrincipal(expression = "customUser")
public #interface CurrentUser {}
#GetMapping("/user")
public CustomUser user(#CurrentUser CustomUser customUser) {
return customUser;
}
We are running an API on Sping Boot 2.2 and are consequently using Sping Security 5.2. In securing this API with OAuth, we are using the new features built into Spring Security (since the Spring Security OAuth project is now deprecated). We are using opaque tokens and (as indicated by the documentation) have a security config of the following form:
#Configuration
public static class OAuthWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().mvcMatchers("/path/to/api/**").hasAuthority(CUSTOM_SCOPE)
.oauth2ResourceServer().opaqueToken().introspector(opaqueTokenIntrospector());
}
}
Here opaqueTokenIntrospector() is a bean which performs the following tasks:
Send a request to the introspection endpoint to get the full token.
Also send a request to the userinfo endpoint to get additional info about the user from the IDP.
Map some of this additional info into custom spring roles and add these roles to the authenticated user.
The way this configuration is set up, each request to the API comes with two additional requests: one to the introspection endpoint and one to the userinfo endpoint. It would be better to save on some of these if a user performs successive requests to the API.
Is it possible to save the result of the opaqueTokenIntrospector() in the session of the user? This way the whole flow of the bean need only be done once per user, saving on redundant requests.
This is a common requirement when you get beyond basic APIs. Use a claims caching solution, which is an API gateway pattern:
When a token is first received do the lookups and save them into an object:
Token claims
User info claims
App specific claims
Then cache the results against the token, so that subsequent requests with the same token are fast:
Use a thread safe cache - my preference is to use an in memory one
Use the SHA256 hash of the access token as the key
Use the serialized claims as the value
This probably goes beyond Spring's default behaviour, but you can customise it.
I have a Java sample that does this - here are a couple of links, though my sample is quite a complex one:
Custom Authorizer Class
Caching Class
Java Write Up
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
Im new on spring security and I had some research on authentication ,I saw two options there are some guys posted.First one Jdbc authentication or In memory authentication ,and there are also loadUserByName(UserDetailService).
what is difference between them ,and also what is use case of loadUserByName (UserDetailService)
This is the official reference https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-authentication
For In Memory Authentication, you have a set of username-password pair hard-coded in your xml/java config class.
In jdbc authentication, you can have a direct database contact to fetch users and authorities, provided you have configured a datasource
You can define custom authentication by exposing a custom UserDetailsService as a bean. You can do whatever functionality to return an instance of UserDetails in loadUserByUsername(). This method is called implicitly to authenticate a user, when creating an authentication.
I want to implement the login/logout (authentication/authorization) system of my Spring 4 MVC application with Spring Security.
Currently I use a very simple hand-made implementation which basically does nothing more than comparing the entered username and MD5 hashed password with the database values by looking up the user by the username using a custom service method and comparing the encrypted passwords.
If the passwords match, the username of the logged in member is saved in the session and a ControllerAdvice looks up the Member object for the user using the username in the session prior to each request. The checkLogin method returns true is username and password match:
#Service("loginService")
#Transactional
public class LoginServiceImpl implements LoginService {
private MemberDao dao;
//more methods
#Override
public boolean checkLogin(String username, String password) {
String hashedPassword = getPasswordHash(password);
return dao.checkLogin(username, hashedPassword);
}
}
This does work but is not a very elegant solution, does not handle different roles and is probably not very secure. Besides I want to become familiar with Spring Security.
Reading the official tutorial for Spring Security (http://docs.spring.io/spring-security/site/docs/4.0.4.RELEASE/reference/htmlsingle/#tech-userdetailsservice) the way to go to authenticate against the Login service method does not become clear to me.
The tutorial discusses authentication direct against the database but I cannot find anything about using a Service method to perform the authentication and in my layered architecture, the database is hidden behind the Servoce and Dao (Hibernate) layers.
Also most examples in the tutorial use XML based instead of Java based configuration which I use for my application.
After having search a lot with search engines, I still have not found a tutorial which implements Spring Security in a Spring MVC application using a familiar layered structure using a Service and Dao layer.
Do I need to bypass Service and DAO/Hibernate layers and authenticate directory against the database? Or write a custom authentication-provider implementing UserDetailsService as described in this post?
Spring Security 3 database authentication with Hibernate
And is configuring Spring Security possible with Java based configuration only? I am a bit lost with this issue so I hope for some hints...