I have a resource server configured with @EnableResourceServer
annotation and it refers to authorization server via user-info-uri
parameter as follows:
security: oauth2: resource: user-info-uri: http://localhost:9001/user
Authorization server /user endpoint returns an extension of org.springframework.security.core.userdetails.User
which has e.g. an email:
{ "password":null, "username":"myuser", ... "email":"[email protected]" }
Whenever some resource server endpoint is accessed Spring verifies the access token behind the scenes by calling the authorization server's /user
endpoint and it actually gets back the enriched user info (which contains e.g. email info, I've verified that with Wireshark).
So the question is how do I get this custom user info without an explicit second call to the authorization server's /user
endpoint. Does Spring store it somewhere locally on the resource server after authorization or what is the best way to implement this kind of user info storing if there's nothing available out of the box?
user_info_uri: defined in openid-connect. It returns the profile information(for example email address and birthday). It is mainly used in SSO login.It can be post or get method. It is a resource server endpoint.
After you add the authorization profile, you need to get access token from the server. In this tutorial, we get it by using the Authorization Code grant method: Click Get Token. In the subsequent dialog, enter Client Identification and Secret, Authorization URI, Access Token URI and Redirect URI.
The UserInfo endpoint is an OAuth 2.0 protected resource of the Connect2id server where client applications can retrieve consented claims, or assertions, about the logged in end-user. The claims are typically packaged in a JSON object where the sub member denotes the subject (end-user) identifier.
The /oauth/token endpoint is used by the application in order to get an access token or a refresh token. It is used by all flows except for the Implicit Flow because in that case an access token is issued directly.
The solution is the implementation of a custom UserInfoTokenServices
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServices.java
Just Provide your custom implementation as a Bean and it will be used instead of the default one.
Inside this UserInfoTokenServices you can build the principal
like you want to.
This UserInfoTokenServices is used to extract the UserDetails out of the response of the /users
endpoint of your authorization server. As you can see in
private Object getPrincipal(Map<String, Object> map) { for (String key : PRINCIPAL_KEYS) { if (map.containsKey(key)) { return map.get(key); } } return "unknown"; }
Only the properties specified in PRINCIPAL_KEYS
are extracted by default. And thats exactly your problem. You have to extract more than just the username or whatever your property is named. So look for more keys.
private Object getPrincipal(Map<String, Object> map) { MyUserDetails myUserDetails = new myUserDetails(); for (String key : PRINCIPAL_KEYS) { if (map.containsKey(key)) { myUserDetails.setUserName(map.get(key)); } } if( map.containsKey("email") { myUserDetails.setEmail(map.get("email")); } //and so on.. return myUserDetails; }
Wiring:
@Autowired private ResourceServerProperties sso; @Bean public ResourceServerTokenServices myUserInfoTokenServices() { return new MyUserInfoTokenServices(sso.getUserInfoUri(), sso.getClientId()); }
!!UPDATE with Spring Boot 1.4 things are getting easier!!
With Spring Boot 1.4.0 a PrincipalExtractor was introduced. This class should be implemented to extract a custom principal (see Spring Boot 1.4 Release Notes).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With