How to make the refresh token life long valid and issue a new refresh token each time a new refresh_token grant_type comes in spring security oauth2 - spring

I am using spring security oauth2 for authentication for my android application clients.When the client request comes with grant_type as password the server issues the access token and refresh token.If the access token expires i can issue a new access token by sending a request with grant_type as refresh_token.Now what will i do if my refresh token expires?I dont want to prompt the users to authenticate again using his credentials.So is there a way to issue a new refresh token along with the new access token? or is there any provision to issue a refresh token with infinite validity or by sending a refresh token with single time use only and refresh the refresh token in each refresh_token grant_type request.Below is my configuration file for spring security oauth2.
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">
<!-- This is default url to get a token from OAuth -->
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request
parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<!-- This is where we tells spring security what URL should be protected
and what roles have access to them -->
<http pattern="/protected/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/protected/**" access="ROLE_APP" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test" />
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"
xmlns="http://www.springframework.org/schema/beans">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<authentication-manager id="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<authentication-manager alias="authenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="userService">
</authentication-provider>
</authentication-manager>
<bean id="userService"
class="com.example.myproject.ser.UserService">
</bean>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<!-- This defined token store, we have used inmemory tokenstore for now
but this can be changed to a user defined one -->
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
<!-- This is where we defined token based configurations, token validity
and other things -->
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="accessTokenValiditySeconds" value="120" /> <!-- 2 hour 3600 -->
<property name="refreshTokenValiditySeconds" value="420"></property> <!-- 2 month 5270400 -->
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="userApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
<property name="tokenServices" ref="tokenServices" />
</bean>
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices"
user-approval-handler-ref="userApprovalHandler">
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter"
resource-id="test" token-services-ref="tokenServices" />
<bean id="clientDetails"
class="com.example.myproject.ser.ClientService">
</bean>
<sec:global-method-security
pre-post-annotations="enabled" proxy-target-class="true">
<!--you could also wire in the expression handler up at the layer of the
http filters. See https://jira.springsource.org/browse/SEC-1452 -->
<sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
</beans>
In my android application i have the provision to authenticate the same user from multiple devices.That is one can authenticate in any device if already he is authenticated in other device.So the solution don't affect this case.

You can set validity period for the refresh token either at the client level (see org.springframework.security.oauth2.provider.ClientDetails and org.springframework.security.oauth2.provider.ClientDetailsService).
You'll need to set this on the client as it's loaded by the client details service.
public classs MyClientDetailsService implements ClientDetailsService {
#Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
BaseClientDetails client = new BaseClientDetails();
client.setRefreshTokenValiditySeconds(Integer.MAX_VALUE);
...
return client;
}
}
Alternatively, you can set a default validity on org.springframework.security.oauth2.provider.token.DefaultTokenServices (assuming that is the implementation that you are using in your server) in your authorisation server configuration. You can do this by adding the following method to your authorisation server configuration class.
#Bean
public AuthorizationServerTokenServices authorizationServerTokenServices() throws Exception {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setRefreshTokenValiditySeconds(Integer.MAX_VALUE);
return tokenServices;
}
Once that refresh token has expired though, I believe the only way to obtain a new one is for the user to re-authenticate.

According to the source code for spring-security-oauth in the DefaultTokenServices passing a value less or equal to zero as the validity of the refresh token should be enough to make it last forever. Check it out here.
Then the code in the authorisation server configuration should be like this:
#Bean
fun tokenServices(): DefaultTokenServices {
val defaultTokenServices = DefaultTokenServices()
defaultTokenServices.setTokenStore(tokenStore())
defaultTokenServices.setRefreshTokenValiditySeconds(0)
return defaultTokenServices
}
Or if you have a JdbcClientDetailsService you can set the refresh token expiry in the oauth_client_detailstable.

Related

Spring Boot OAuth2 SSO

I'm not an expert in Spring Security..
I have one single angularjs app, which has OAuth2 based authentication and authorization.
Now I need to add additional app, but users and passwords should be the same for both apps.
So.. I need to have one sso server and two resource servers.
I want to implement that with modern spring boot.
Currently I authenticate users against /oauth/token with grant_type=password
I wonder if there are some samples of SSO and Resource servers that would allow me to authenticate users against http://sso.host/oauth/token?username=someuser&password=somepass&grant_type=password
so.. I will get my acccess and refresh tokens as the result of success authentication.
And access token would be good to use against http://resource.host/rest/someresource
while refresh token would be good to get access_tokens at http://sso.host/oauth/token?grant_type=refresh
I don't want my apps to be redirected to any urls..
I don't need any login forms, just json responses.
Here is my current spting-security-oauth.xml config:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:context="http://www.springframework.org/schema/context" xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd "><beans>
<authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" xmlns="http://www.springframework.org/schema/security/oauth2">
<refresh-token />
<client-credentials />
<password authentication-manager-ref="userAuthenticationManager" disabled="false"/>
</authorization-server>
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<authentication-manager alias="userAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider>
<user-service>
<user name="username" password="password" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<oauth:resource-server id="resourceServerFilter" resource-id="rest_server" token-services-ref="tokenServices" />
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="the_client" secret="" scope="read,write" authorized-grant-types="password,refresh_token,client_credentials" authorities="ROLE_USER"/>
</oauth:client-details-service>
<http pattern="/rest/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint" access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/rest/**" access="ROLE_USER" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore" />
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
<property name="accessTokenValiditySeconds" value="300" />
<property name="refreshTokenValiditySeconds" value="3600" />
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test" />
</bean>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="theRealm/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
<sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
I don't think you can naturally do SSO with the password grant type. You're probably best with the authorization code flow. Oauth2 sso happens because both client apps check for the authenticated session on the authorization server and, if one has already logged in, the second app doesn't need to provide credentials again.
https://spring.io/blog/2015/02/03/sso-with-oauth2-angular-js-and-spring-security-part-v is a good example with angularjs and spring boot.
That is, if you want SSO, I think you'll need redirection to the authorization server for initial login.

How to get access token in spring oauth2

Below is the security.xml file, which I am using to incorporate spring-security-oauth2.
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">
<!-- #author Nagesh.Chauhan(neel4soft#gmail.com) -->
<!-- This is default url to get a token from OAuth -->
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request
parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<!-- This is where we tells spring security what URL should be protected
and what roles have access to them -->
<http pattern="/api/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/api/**" access="ROLE_APP" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test" />
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="test/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"
xmlns="http://www.springframework.org/schema/beans">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<authentication-manager id="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<!-- This is simple authentication manager, with a hardcoded user/password
combination. We can replace this with a user defined service to get few users
credentials from DB -->
<authentication-manager alias="authenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider>
<user-service>
<user name="beingjavaguys" password="spring#java" authorities="ROLE_APP" />
</user-service>
</authentication-provider>
</authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<!-- This defined token store, we have used inmemory tokenstore for now
but this can be changed to a user defined one -->
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
<!-- This is where we defined token based configurations, token validity
and other things -->
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="accessTokenValiditySeconds" value="120" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="userApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
<property name="tokenServices" ref="tokenServices" />
</bean>
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices"
user-approval-handler-ref="userApprovalHandler">
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter"
resource-id="test" token-services-ref="tokenServices" />
<oauth:client-details-service id="clientDetails">
<!-- client -->
<oauth:client client-id="restapp"
authorized-grant-types="authorization_code,client_credentials"
authorities="ROLE_APP" scope="read,write,trust" secret="secret" />
<oauth:client client-id="restapp"
authorized-grant-types="password,authorization_code,refresh_token,implicit"
secret="restapp" authorities="ROLE_APP" />
</oauth:client-details-service>
<sec:global-method-security
pre-post-annotations="enabled" proxy-target-class="true">
<!--you could also wire in the expression handler up at the layer of the
http filters. See https://jira.springsource.org/browse/SEC-1452 -->
<sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
As of now for getting the token I have to make the request in below format:
[http://localhost:8080/SpringRestSecurityOauth/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=beingjavaguys&password=spring#java]
Now I want the query paramters to be sent in json format like
{
"grant_type":"password",
"client_id":"restapp",
"client_secret":"restapp",
"username":"beingjavaguys",
"password":"spring#java"
}
What changes I have to do in spring-security.xml file so that I send query paramters in json?
AFAIK that is not supported out of the box. To do that you would have to write your own REST service interface.
However the format you suggest also seems less 'REST-like' then the supported version (a GET request should be used for retrieving information). At least if you see it as retrieving and not creating a token.

Spring Security OAuth2 /oauth/token endpoint after Basic auth success then no access token

I am adding spring security oauth2 to my spring boot application. I have followed a few of the examples on the web and in github from the spring team (had to make some mods for my use case), but I am still not able to return an oauth2 access token from the /oauth/token endpoint. I have been working on this for several days and started off trying to do it in JavaConfig, but then switched to xml config and was able to make some progress. I should note the only config I am doing in xml is the security config and related security beans, all other config is done via JavaConfig.
I am able to successfully authenticate my user (using username & password) using Basic auth, but when it comes to the next step to generate and return the bearer token, this is not happening. Note, I am using a database for storing my user credentials and to store my access tokens (once they get created).
I am making a POST request using grant_type=password with a client id and client secret.
I am under the impression spring oauth2 would handle creating the access token for me and returning it, but maybe this isn't correct or my xml config maybe wrong? Many thanks for any assistance!
Here is my security config xml below:
<http pattern="/oauth/token"
create-session="stateless"
authentication-manager-ref="authenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/**" method="GET" access="ROLE_DENY" />
<intercept-url pattern="/**" method="PUT" access="ROLE_DENY" />
<intercept-url pattern="/**" method="DELETE" access="ROLE_DENY" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request
parameters>
<custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER" /-->
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<http pattern="/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<!-- authentication-manager-ref="authenticationManager"
access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security"-->
<anonymous enabled="false" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<!-- property name="realmName" value="f2rRealm" /-->
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="f2r/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<authentication-manager alias="authenticationManager"
xmlns="http://www.springframework.org/schema/security" >
<!-- authentication-provider user-service-ref="clientDetailsUserService" /-->
<authentication-provider ref="authenticationProvider" />
</authentication-manager>
<bean id="clientDetails" class="com.f2r.security.oauth2.F2RJdbcClientDetailsService" >
<constructor-arg ref="dataSource" />
<property name="clientId" value="f2r" />
<property name="clientSecret" value="f2rsecret" />
<property name="passwordEncoder" ref="passwordEncoder" />
</bean>
<!-- bean id="clientDetailsService"
class="com.f2r.security.oauth2.F2RJdbcClientDetailsService">
<constructor-arg ref="dataSource" />
<property name="clientDetails" ref="clientDetails" />
<property name="passwordEncoder" ref="passwordEncoder" />
</bean-->
<bean id="clientDetailsUserService"
class="com.f2r.security.oauth2.F2RClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
<property name="passwordEncoder" ref="passwordEncoder" />
</bean>
<authentication-manager id="userAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<!-- authentication-provider ref="customUserAuthenticationProvider" /-->
<authentication-provider ref="authenticationProvider" />
</authentication-manager>
<!-- bean id="customUserAuthenticationProvider" class="com.f2r.security.F2RAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService" />
</bean-->
<bean id="authenticationProvider" class="com.f2r.security.F2RAuthenticationProvider">
<!-- property name="userDetailsService" ref="userDetailsService" /-->
<property name="userDetailsService" ref="clientDetailsUserService" />
</bean>
<bean id="userDetailsService" class="com.f2r.security.F2RUserDetailsService" />
<bean id="userApprovalHandler"
class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
<property name="tokenStore" ref="tokenStore" />
<property name="clientDetailsService" ref="clientDetails" />
<property name="requestFactory" ref="requestFactory" />
</bean>
<bean id="requestFactory"
class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
<constructor-arg ref="clientDetails" />
</bean>
<oauth:authorization-server
client-details-service-ref="clientDetails"
token-services-ref="tokenServices"
token-endpoint-url="/oauth/token"
authorization-endpoint-url="/oauth/authorize" >
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<!-- oauth:password authentication-manager-ref="userAuthenticationManager" /-->
<oauth:password />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter"
token-services-ref="tokenServices" />
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore" >
<constructor-arg ref="dataSource"/>
</bean>
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<mvc:annotation-driven />
<mvc:default-servlet-handler />
<sec:global-method-security
pre-post-annotations="enabled" proxy-target-class="true">
<!--you could also wire in the expression handler up at the layer of the
http filters. See https://jira.springsource.org/browse/SEC-1452 -->
<sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />

Redirect all tabs to login page after logout in one tab with Spring Security

Currently, I am having a problem with Spring Security as when I open two tabs and I logout in one tab, the session is sure to be destroyed but I can still make some action on the other tab, which is supposed to redirect me to login page instead of letting me make any other action so far.
Not until I refresh the page, it will redirect to the login page as no valid session any more.
I am trying to find a solution to force the other tabs to redirect to logout page without letting the user doing any thing else as they logout in the other tab.
I wonder if we could achieve such a thing with Spring Security configuration?
Below is my spring-security.xml
<!-- Secure token end point -->
<http pattern="/api/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<custom-filter ref="clientCredentialsTokenEndpointFilter"
before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<!-- secure api service -->
<http pattern="/api/service/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint" xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/api/service/**" method="GET"
access="IS_AUTHENTICATED_FULLY" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<http auto-config="true" use-expressions="true"
xmlns="http://www.springframework.org/schema/security"
authentication-manager-ref="userAuthenticationManager">
<access-denied-handler error-page="/accessDenied" />
<intercept-url pattern="/home/**" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')" />
<intercept-url pattern="/index" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')" />
<intercept-url pattern="/pages/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/overviewOfferLetter" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/importOldCandidate" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/RecruitmentEvent" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/questionnaireResultId**" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_INTERVIEWER')"/>
<intercept-url pattern="/evaluation**" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_INTERVIEWER')"/>
<intercept-url pattern="/questionnaireResult" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_CV_SCREENER') or hasRole('ROLE_MANAGER')"/>
<intercept-url pattern="/activityLogs" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/oldCandidate" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/advancedSearch" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/listApplicants" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/duplicatedEmail" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/createApplicant" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')"/>
<intercept-url pattern="/jobHistory" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')" />
<intercept-url pattern="/importOldCandidate" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')" />
<intercept-url pattern="/oldCandidate" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')" />
<intercept-url pattern="/importApplicant" access="hasRole('ROLE_ADMIN') or hasRole('ROLE_HR')" />
<form-login login-page="/login"
default-target-url="/index"
always-use-default-target="true"
authentication-failure-url="/login?error=incorrect"
authentication-success-handler-ref="customAuthenticationSuccessHandler"
username-parameter="username"
password-parameter="password"
/>
<logout logout-success-url="/login?logout" delete-cookies="JSESSIONID" />
</http>
<bean id="customAuthenticationSuccessHandler" class="com.axonactive.security.CustomAuthenticationSuccessHandler" />
<authentication-manager id="userAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider ref="authenticationProvider">
</authentication-provider>
</authentication-manager>
<bean id="authenticationProvider" class="com.axonactive.security.CustomAuthenticationProvider"></bean>
<global-method-security secured-annotations="enabled"
pre-post-annotations="enabled" proxy-target-class="true"
xmlns="http://www.springframework.org/schema/security">
<expression-handler ref="expressionHandler" />
</global-method-security>
<bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator">
<bean id="permissionEvaluator" class="com.axonactive.security.AccountPermissionEvaluator" />
</property>
</bean>
<bean id="webExpressionHandler"
class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="permissionEvaluator">
<bean id="permissionEvaluator" class="com.axonactive.security.AccountPermissionEvaluator" />
</property>
</bean>
<!-- Define LDAP Service -->
<bean id="ldapService" class="com.axonactive.service.implement.LdapService">
<property name="contextFactory" value="${ldap.contextFactory}" />
<property name="url" value="${ldap.url}" />
<property name="securityAuthentication" value="${ldap.securityAuthentication}" />
<property name="username" value="${ldap.username}" />
<property name="password" value="${ldap.password}" />
<property name="searchBase" value="${ldap.searchBase}" />
<property name="searchName" value="${ldap.searchName}" />
<property name="distinguishedName" value="${ldap.distinguishedName}" />
</bean>
<!-- End LDAP Service -->
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
</bean>
<!-- End point filter for client -->
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
<!-- Client credential authentication manager -->
<authentication-manager alias="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<!-- Declare client service -->
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetailsService" />
</bean>
<!-- Declare client list -->
<oauth:client-details-service id="clientDetailsService">
<oauth:client client-id="testSystem"
secret="9346336818f9d382a22ac5d4486fa5ee" scope="read"
authorized-grant-types="client_credentials" />
</oauth:client-details-service>
<!-- Config oauth server -->
<oauth:authorization-server
client-details-service-ref="clientDetailsService" token-services-ref="tokenServices">
<oauth:client-credentials />
</oauth:authorization-server>
<!-- Declare resource server, where the token token are store -->
<oauth:resource-server id="resourceServerFilter"
resource-id="springsec" token-services-ref="tokenServices" />
<!-- Store token in memory -->
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.JdbcTokenStore">
<constructor-arg ref="dataSource" />
</bean>
<!-- Configuration token service , expire in one day, don't support refresh
token -->
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="false" />
<property name="accessTokenValiditySeconds" value="86400"></property>
<property name="clientDetailsService" ref="clientDetailsService" />
</bean>
If there is any other files or information you guys need to see, please tell me.
As #kryger said, your client-side code would need to check if your session is still authenticated. We have a similar problem with our client app, in that Spring wants to redirect unauthorized request, but our client app is communicating with our server in a "rest-ful" way, so the client app just fails silently to the end user.
We solve this issue by having a second AuthenticationEntryPoint, specifically with Http403ForbiddenEntryPoint. By using the Http403ForbiddenEntryPoint.html, all unauthorized request by our client app get a HTTP 403 error vs a traditional Spring security redirect. Then our client app is configured with an interceptor to listen for any 403 errors and prompt the user with a signin.
Here's an example Spring Security configuration using Http403ForbiddenEntryPoint from our WebSecurityConfigurerAdapter config.
/**
* Configures all the AuthenticationEntryPoints for our app
*/
protected DelegatingAuthenticationEntryPoint delegatingAuthenticationEntryPoint() {
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints =
new LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>();
// entry point for unauthenticated client apps
entryPoints.put(new SecuredApiRequestMatcher(), new Http403ForbiddenEntryPoint());
// entry point for our normal website
DelegatingAuthenticationEntryPoint delegatingEntryPoint =
new DelegatingAuthenticationEntryPoint(entryPoints);
delegatingEntryPoint.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint(SIGNIN_URL));
return delegatingEntryPoint;
}
/**
* {#link RequestMatcher} that checks if requested url matches a secured api path.
*/
final class SecuredApiRequestMatcher implements RequestMatcher {
final Map<String, String[]> securedMethodToUrlMap;
public SecuredApiRequestMatcher() {
securedMethodToUrlMap = new HashMap<String, String[]>();
securedMethodToUrlMap.put(GET.name(), SECURED_GET_URLS);
securedMethodToUrlMap.put(PUT.name(), SECURED_PUT_URLS);
securedMethodToUrlMap.put(POST.name(), SECURED_POST_URLS);
securedMethodToUrlMap.put(DELETE.name(), SECURED_DELETE_URLS);
}
#Override
public boolean matches(HttpServletRequest request) {
String url = UrlUtils.buildRequestUrl(request);
String method = request.getMethod();
String[] securedUrls = securedMethodToUrlMap.get(method);
if(securedUrls != null) {
for(String securedUrl : securedUrls) {
if(url.startsWith(securedUrl))
return true;
}
}
return false;
}
}
Finally, here's the response from the Http403ForbiddenEntryPoint
{"timestamp":1430938979916,"status":403,"error":"Forbidden","message":"Access
Denied","path":"/api/secured/url"}

Token URL fails with spring security oauth2

I'm trying to get a oauth2 configuration for securing my webapp and only allowing trusted clients with provided user credentials to access the app.
This is what I have so far
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oauth="http://www.springframework.org/schema/security/oauth2" xmlns:sec="http://www.springframework.org/schema/security"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<!-- The OAuth2 protected resources are separated out into their own block so we can deal with authorization and error handling
separately. This isn't mandatory, but it makes it easier to control the behaviour. -->
<http request-matcher="regex" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<!-- <anonymous enabled="false" /> -->
<intercept-url pattern="/api/register/.*" access="ROLE_CLIENT" />
<intercept-url pattern="/api/.*" access="ROLE_USER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<expression-handler ref="oauthWebExpressionHandler" />
</http>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="qeep" />
</bean>
<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="qeep/client" />
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="qeepUserDetailsService" />
</authentication-manager>
<bean id="myUserDetailsService" class="com.example.core.web.rest.auth.QeepUserDetailsService"/>
<bean id="tokenStore" class="com.example.core.web.rest.auth.QeepTokenStore" />
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="my-trusted-client-with-secret" authorized-grant-types="password,authorization_code,refresh_token,implicit"
secret="somesecret" authorities="ROLE_CLIENT, ROLE_TRUSTED_CLIENT" />
</oauth:client-details-service>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password />
</oauth:authorization-server>
<sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
<!--you could also wire in the expression handler up at the layer of the http filters. See https://jira.springsource.org/browse/SEC-1452 -->
<sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
</beans>
If I access /oauth/token using curl, I get a client authorization request which I get through using the configured client credentials. But after this /oauth/token only returns 404 - Not found. I've tried different stuff for the last hours without any success.
I extracted the configuration from the sparklr/tonr sample of oauth2 1.0.5 which I used because we are still on spring 3.2.
The same test works fine in the sparklr-Sample-webapp.
EDIT
The actual curl url is the following:
curl -v -H "Authorization: Basic bXktdHJ1c3RlZC1jbGllbnQtd2l0aC1zZWNyZXQ6c29tZXNlY3JldA==" "http://localhost:8084/core/oauth/token"
Without the Authorization-Header I get a 401 asking for the client authentication as configured ("my-trusted-client-with-secret" and "somesecret") but with the added authorization header I just get the 404 - Not found. If I test the same with the sparklr-Sample I get an error asking for the grant-type after adding the Basic-Auth-Header like above which is what I would expect.
I hope this makes a bit clearer.
Any ideas what's wrong with my configuration?
The DispatcherServlet was mapped to the wrong url, I broke it while trying to fix an issue which I had first. The Dispatcher-Servlet is mapped to core/api and the token service first was mapped to core/api/oauth/token which I later changed to core/oauth/token but I forgot to change the DispatcherServlet. Thanks, Dave for the hint!

Resources