'Spring Security OAuth2 - No Granted Authorities after authorization on Custom Auth Server
I have User with authorities USER and ADMIN. So after authorization on Auth Server I need to get my User granted authorities + Scopes (ROLE_USER, ROLE_ADMIN, SCOPE_openid). But I always have only ROLE_USER and SCOPE_openid without authority ROLE_ADMIN, which I need.
How can I get all user's granted authorities in Client after authorization?
My Custom UserDetailsService (on Auth Server side). When I retrieve User from DB, I have all authorities (ADMIN, USER):
@Service
@AllArgsConstructor
public class ApplicationUserDetailsService implements UserDetailsService {
private final ApplicationUserRepository applicationUserRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
ApplicationUser user = applicationUserRepository.findByEmail(email)
.orElseThrow(()-> new UsernameNotFoundException(
String.format("User with email = '%s' not found", email)));
return new User(
user.getEmail(),
user.getPassword(),
true,
true,
true,
true,
getAuthorities(user.getRoles())
);
}
private Collection<? extends GrantedAuthority> getAuthorities(List<Role> roles) {
return roles
.stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
}
}
Then in Client after successful Authorization on Auth Server I try to get all authorities from Authorization, but now I have the only authrority (USER):
@RestController
@RequestMapping("/api/v1/users")
public class ApplicationUserController {
//Using WebClient due to OAuthRestTemplate is deprecated
private final WebClient webClient;
@GetMapping("/test")
public ResponseEntity<String[]> getAllUsersString(
@RegisteredOAuth2AuthorizedClient("api-client-authorization-code")
OAuth2AuthorizedClient client){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth.isAuthenticated());
System.out.println(auth.getAuthorities());
System.out.println(auth.getPrincipal());
return ResponseEntity
.status(HttpStatus.OK)
.body(webClient
.get()
.uri("http://localhost:8090/api/v1/users/test")
.attributes(oauth2AuthorizedClient(client))
.retrieve()
.bodyToMono(String[].class)
.block()
);
}
OUTPUT:
true
[ROLE_USER, SCOPE_openid]
Name: [[email protected]], Granted Authorities: [[ROLE_USER, SCOPE_openid]], User Attributes: [{[email protected], aud=[api-client], azp=api-client, iss=http://localhost:9000, exp=2022-04-08T09:59:06Z, iat=2022-04-08T09:29:06Z, nonce=C7kk8irS0YzUtWVbwK78wTmQYjTBEh-tS59519K01DA}]
OUTPUT I need:
true
[ROLE_USER, ROLE_ADMIN, SCOPE_openid]
Name: [[email protected]], Granted Authorities: [[ROLE_USER, ROLE_ADMIN, SCOPE_openid]], User Attributes: [{[email protected], aud=[api-client], azp=api-client, iss=http://localhost:9000, exp=2022-04-08T09:59:06Z, iat=2022-04-08T09:29:06Z, nonce=C7kk8irS0YzUtWVbwK78wTmQYjTBEh-tS59519K01DA}]
Client Security Config:
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] WHITE_LIST_URLS = {
"/hello",
"/register",
"/verifyRegistration*",
"/resendVerifyToken*"
};
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.authorizeHttpRequests()
.antMatchers(WHITE_LIST_URLS).permitAll()
.antMatchers("/api/**").authenticated()
.and()
.oauth2Login()
.loginPage("/oauth2/authorization/api-client-oidc")
.and()
.oauth2Client(Customizer.withDefaults());
}
}
Client application.yml:
spring:
security:
oauth2:
client:
registration:
api-client-oidc:
provider: spring
client-id: api-client
client-secret: secret
authorization-grant-type: authorization_code
redirect-uri: "http://127.0.0.1:8080/login/oauth2/code/{registrationId}"
scope: openid
client-name: api-client-oidc
api-client-authorization-code:
provider: spring
client-id: api-client
client-secret: secret
authorization-grant-type: authorization_code
redirect-uri: "http://127.0.0.1:8080/authorized"
scope:
- api.read
- ROLE_ADMIN
- ROLE_USER
client-name: api-client-authorization-code
provider:
spring:
issuer-uri: http://localhost:9000
Authorization Server Config:
AllArgsConstructor
@Configuration(proxyBeanMethods = false)
public class AuthenticationServerConfig {
private final PasswordEncoder passwordEncoder;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults()).build();
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("api-client")
.clientSecret(passwordEncoder.encode("secret"))
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/api-client-oidc")
.redirectUri("http://127.0.0.1:8080/authorized")
.scope(OidcScopes.OPENID)
.scope("api.read")
.scope("ROLE_ADMIN")
.scope("ROLE_USER")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
private static RSAKey generateRsa() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder()
.issuer("http://localhost:9000")
.build();
}
}
Can someone help me to solve this problem or maybe someone have similar example app OAuth2 + Spring Boot with User's Authorities?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
