TL;DR: How to assign users custom roles/authorities on Resource server side (that means without JWT) based on their access_token?
The whole story: I have a working Auth server and a client (which is SPA), which can obtain access_token from the Auth server. With that access_token the client can request data on my Resource server (which is separated from Auth server). The Resource server can get username from Auth server using the access_token.
I can access the username in code by injection Authentication
object into method like this:
@RequestMapping("/ping")
fun pingPong(auth: Authentication): String = "pong, " + auth.name
My question is how to add my custom roles or authorities (auth.authorities
- there is only USER_ROLE) to this object which would be managed on the Resource server, not Auth server, based on the username.
I have tried several ways to do it but none has helped. The most promising was this:
@Configuration
@EnableWebSecurity
@EnableResourceServer
class ResourceServerConfigurer(val userDetailsService: MyUserDetailsService) : ResourceServerConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.userDetailsService(userDetailsService) // userDetailsService is autowired
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests()
.antMatchers("/", "/index.html").permitAll()
.anyRequest().authenticated()
}
}
And my custom UserDetailsService:
@Service
class UserDetailsService : org.springframework.security.core.userdetails.UserDetailsService {
override fun loadUserByUsername(username: String): UserDetails {
return org.springframework.security.core.userdetails.User(username, "password", getAuthorities(username))
}
private fun getAuthorities(user: String): Set<GrantedAuthority> {
val authorities = HashSet<GrantedAuthority>()
authorities.addAll(listOf(
SimpleGrantedAuthority("ROLE_ONE"), //let's grant some roles to everyone
SimpleGrantedAuthority("ROLE_TWO")))
return authorities
}
}
Everything worked (I mean I was successfully authenticated) except that I still had only ROLE_USER. Next what I tried was providing a custom implementation of AbstractUserDetailsAuthenticationProvider:
@Bean
fun authenticationProvider(): AbstractUserDetailsAuthenticationProvider {
return object : AbstractUserDetailsAuthenticationProvider() {
override fun retrieveUser(username: String, authentication: UsernamePasswordAuthenticationToken): UserDetails {
return User(username, "password", getAuthorities(username))
}
private fun getAuthorities(user: String): Set<GrantedAuthority> {
val authorities = HashSet<GrantedAuthority>()
authorities.addAll(listOf(
SimpleGrantedAuthority("ROLE_ONE"),
SimpleGrantedAuthority("ROLE_TWO")))
return authorities
}
override fun additionalAuthenticationChecks(userDetails: UserDetails, authentication: UsernamePasswordAuthenticationToken?) {
}
}
}
with same result, only the ROLE_USER was present.
I would really appreciate any ideas from you guys how add some roles to the Authentication object after the access_token was validated and username obtained from Auth server.
Solution by OP.
First of all I needed to provide custom PrincipalExtractor
and AuthoritiesExtractor
implementations. But to make Spring use them it is necessary in configuration NOT to use security.oauth2.resource.token-info-uri
but security.oauth2.resource.user-info-uri
instead (I really didn't expect this to be one of the roots of my problem).
Finally the security config must be done in ResourceServerConfigurerAdapter
, not in WebSecurityConfigurerAdapter
.
The final code looks like this:
@SpringBootApplication
@RestController
class MyApplication {
@RequestMapping("/ping")
fun pingPong(user: Authentication): String {
return "pong, " + user.name + " - " + user.authorities.joinToString()
}
}
@Configuration
@EnableWebSecurity
@EnableResourceServer
class ResourceServerConfigurer : ResourceServerConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests()
.antMatchers("/", "/index.html").permitAll()
.anyRequest().authenticated()
}
@Bean
fun principalExtractor() = PrincipalExtractor {
return@PrincipalExtractor it["name"]
}
@Bean
fun authoritiesExtractor() = AuthoritiesExtractor {
return@AuthoritiesExtractor AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ONE,ROLE_TWO")
}
}
fun main(args: Array<String>) {
SpringApplication.run(MyApplication::class.java, *args)
}
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