I've been working the last couple days to get an implementation of spring boot / spring security / and java configuration working with spring-security-oauth2. I've managed to work through most of the difficulties, but am stumped as to what is going wrong now.
I am completing the following steps successfully:
?code=asdfa&state=asdfasf
in the query stringAt this point, I believe whatever is using the AuthorizationCodeResourceDetails
should be exchanging the authorization code and client app credentials for an access token. This is where the process is failing with the following stack trace.
Caused by: org.springframework.security.oauth2.common.exceptions.InvalidRequestException: Possible CSRF detected - state parameter was present but no state could be found
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.getParametersForTokenRequest(AuthorizationCodeAccessTokenProvider.java:246)
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:198)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:142)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:564)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:529)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:261)
at com.pvr.apps.admin.user.UserServiceImpl.getAllUsers(UserServiceImpl.java:51)
at com.pvr.apps.admin.web.IndexController.serveUserList(IndexController.java:35)
Things on the client look like (I also have an @EnableOAuth2Client
annotation on the main config).
@Component
public class UserServiceImpl implements UserService {
@Resource
@Qualifier("accessTokenRequest")
private AccessTokenRequest accessTokenRequest;
public OAuth2ProtectedResourceDetails createResource() {
AuthorizationCodeResourceDetails resourceDetails = new AuthorizationCodeResourceDetails();
resourceDetails.setScope(Lists.newArrayList("read", "write"));
resourceDetails.setClientId("admin");
resourceDetails.setClientSecret("password");
resourceDetails.setAuthenticationScheme(AuthenticationScheme.query);
resourceDetails.setAccessTokenUri("http://provider.com:8080/oauth/token");
resourceDetails.setUserAuthorizationUri("http://provider.com:8080/oauth/authorize");
return resourceDetails;
}
@Override
public List<User> getAllUsers() {
RestTemplate template = new OAuth2RestTemplate(createResource(), new DefaultOAuth2ClientContext(accessTokenRequest));
ResponseEntity<User[]> responseEntity = template.getForEntity("http://provider.com:8080/users/", User[].class);
return Lists.newArrayList(responseEntity.getBody());
}
}
And on the provider side of things:
The authorization server config:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter{
@Autowired
private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.authenticationEntryPoint(authenticationEntryPoint)
.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
endpoints
.authenticationManager(authenticationManager)
.accessTokenConverter(converter)
.tokenStore(new JwtTokenStore(converter));
}
// TODO: this should read from a db
public void configure(ClientDetailsServiceConfigurer clientConfigurer) throws Exception {
clientConfigurer.inMemory()
.withClient("admin").secret("password")
.authorizedGrantTypes(
GrantType.PASSWORD.type,
GrantType.AUTHORIZATION_CODE.type,
GrantType.IMPLICIT.type,
GrantType.REFRESH_TOKEN.type
)
.authorities("ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(60);
}
}
and the resource server config:
@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailService;
@Autowired
AuthenticationProvider authenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login.do")
.usernameParameter("uid")
.passwordParameter("pwd")
.loginPage("/login")
.failureUrl("/login?error=true")
.and()
.userDetailsService(userDetailService);
}
}
Deprecated. See the OAuth 2.0 Migration Guide for Spring Security 5. Rest template that is able to make OAuth2-authenticated REST requests with the credentials of the provided resource.
The ClientRegistrationRepository serves as a repository for OAuth 2.0 / OpenID Connect 1.0 ClientRegistration (s). Note. Client registration information is ultimately stored and owned by the associated Authorization Server.
The main goal of the OAuth2RestTemplate is to reduce the code needed to make OAuth2-based API calls. It basically meets two needs for our application: Handles the OAuth2 authentication flow. Extends Spring RestTemplate for making API calls.
The state it is looking for would be in the OAuth2ClientContext
but since you have just created a new one it is out of scope when it is needed. If you inject the one that comes from @EnableOAuth2Client
instead it will be in @Scope("session")
so it will be able to resolve the state for you. All the samples in GitHub work that way. Or you can manage the persistence yourself, I guess.
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