I have created an application using Spring integrated Oauth2. I have my own custom login and authorize templates. After successfully authenticated it is redirecting to authorize .html where it asks for the user approval. The issue is that when I click even Approve or Deny button action is always DENIED like as shown in my table below
Also How can we enable REST based authentication and authorization using oauth2. I tried disabling the csrf for enabling users to do authentication and authorization but still didn't work.
Can anyone please help me on this.
You can download and see the Complete Application from here (Updated as per the last suggestion 19/11/2017)
As per the suggestion from @fateddy I have used Option 3 using the ApprovalStoreUserApprovalHandler. I have used the exact authorize.html given.
Lets say I am having two clients (client123 and client789) in my database.
Client client123 which does not have auto-approve enabled and the client client789 with auto-approve option enabled for openid scope.
Now the problem is that I am getting the below exception for client123 when I click the approve button.
error="invalid_client", error_description="Bad client credentials"
OAuth2Config.java is as given below
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Bean
public UserApprovalHandler userApprovalHandler() {
ApprovalStoreUserApprovalHandler userApprovalHandler= new ApprovalStoreUserApprovalHandler();
userApprovalHandler.setApprovalStore(approvalStore());
userApprovalHandler.setClientDetailsService(clientDetailsService());
userApprovalHandler.setRequestFactory(requestFactory());
return userApprovalHandler;
}
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Bean
public DefaultOAuth2RequestFactory requestFactory(){
return new DefaultOAuth2RequestFactory(clientDetailsService());
}
@Bean
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
@Bean
public ApprovalStore approvalStore() {
return new JdbcApprovalStore(dataSource);
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//endpoints.tokenStore(tokenStore());
// endpoints.approvalStore(approvalStore());
endpoints.userApprovalHandler(userApprovalHandler());
endpoints.authorizationCodeServices(authorizationCodeServices());
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer authorizationServerSecurityConfigurer) throws Exception {
authorizationServerSecurityConfigurer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
}
authorize.html
<html>
<head>
</head>
<body>
<div class="container">
<h2>Please Confirm</h2>
<p>
Do you authorize "${authorizationRequest.clientId}" at "${authorizationRequest.redirectUri}" to access your protected resources
with scope ${authorizationRequest.scope?join(", ")}.
</p>
<form id="confirmationForm" name="confirmationForm" action="/auth/oauth/authorize" method="post">
<input name="scope.openid" value="true" type="checkbox" /> Read<br>
<button class="btn btn-primary" type="submit">Approve</button>
</form>
</div>
</body>
</html>
Reference is the provided project, git-commit 972b85
.
At the end you have several options. But let's have a look at the current project state.
The authorize-endpoint (/oauth/authorize
) let's the user decide whether to authorize or deny access (by displaying a form). The UserApprovalHandler
then decides whether to grant authorization or not.
The existing UserAppovalHandler
-implementations require different request-params in order to be able to make a decision - that also means that this has an impact on what the /oauth/authorize
-view has to look like.
The customized /oauth/authorize
-view contains <input name="user_oauth_approval" value="true" />
which requires a UserApprovalHandler
that picks up said parameter to make a decision. Using the DefaultUserApprovalHandler
(which does not remember any decisions) will work. Here's what the configuration might look like. An Approval-Store is not needed in this case.
<form id="confirmationForm" name="confirmationForm"
action="/auth/oauth/authorize" method="post">
<input name="user_oauth_approval" value="true" type="hidden" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Approve</button>
</form>
The user_oauth_approval=true
request-parameter is only picked up if the DefaultUserUserApprovalHandler
is used:
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Bean
UserApprovalHandler userApprovalHandler() {
return new DefaultUserApprovalHandler();
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// ...
endpoints.userApprovalHandler(userApprovalHandler());
}
}
by following Option 1 but in this case by providing a custom UserApprovalHandler
that remembers any decisions.
Sticking with the ApprovalStoreUserApprovalHandler
(which uses a TokenStore underneath) requires some adaptions to the form:
<form id="confirmationForm" name="confirmationForm" action="/auth/oauth/authorize" method="post">
<!--
The ApprovalStoreUserApprovalHandler tests scopes by testing request-params prefixed with `scope.*`
For dynamic input-element rendering one might iterate over
${authorizationRequest.scope}
Provides access to the scope=openid whenever the user checks the checkbox:
-->
<input name="scope.openid" value="true" type="checkbox" /> OpenID<br>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button class="btn btn-primary" type="submit">Approve</button>
</form>
The Auth-Server-Config:
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// ...
// registering an ApprovalStore automaticaly bootstraps `ApprovalStoreUserApprovalHandler`
endpoints.approvalStore(approvalStore());
}
}
There might be other options that might be a good fit - but that depends on your requirements.
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