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

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"}

Related

Spring OAuth2 authorization requests always returning the same access token

I’m using Spring 3.2.11.RELEASE with OAuth 2.0.7.RELEASE. I have configured my authorization server to use a JDBC token store (the org.springframework.security.oauth2.provider.token.store.JdbcTokenStore class) . However, repeated requests with the same client ID using the grant_type=client_credentials return the same access token, even after server restarts. The token is valid (it has different expiration dates), but this seems like a security flaw. How do I make repeated valid requests return different access tokens? Below is my Spring configuration …
<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 pattern="/oauth/(users|clients)/.*" request-matcher="regex"
create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint"
use-expressions="true" xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/oauth/users/([^/].*?)/tokens/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')"
method="DELETE" />
<intercept-url pattern="/oauth/users/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')"
method="GET" />
<intercept-url pattern="/oauth/clients/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')"
method="GET" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<expression-handler ref="oauthWebExpressionHandler" />
</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 pattern="/me/**" 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="/me" access="ROLE_USER,SCOPE_READ" />
<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="sparklr2" />
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="sparklr2/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 id="userDetailsService">
<user name="marissa" password="koala" authorities="ROLE_USER" />
<user name="paul" password="emu" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<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="tokenEnhancer" ref="tokenEnhancer" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="tokenEnhancer"
class="org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter" />
<bean id="requestFactory"
class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
<constructor-arg name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="approvalStore"
class="org.springframework.security.oauth2.provider.approval.TokenApprovalStore">
<property name="tokenStore" ref="tokenStore" />
</bean>
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:client-credentials />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" entry-point-ref="entry"
resource-id="myprojectAssignment" token-services-ref="tokenServices" />
<bean id="entry" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/myprojectassignment" />
</bean>
<oauth:client-details-service id="clientDetails">
<oauth:client client-id=“client”
authorized-grant-types="client_credentials" authorities="ROLE_CLIENT"
scope="read,write" secret=“password” />
</oauth:client-details-service>
<mvc:default-servlet-handler />
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
<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="/**"
access="IS_AUTHENTICATED_FULLY"/>
<custom-filter ref="resourceServerFilter"
before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
Create your own AuthenticationKeyGenerator for your tokenStore as mentioned here.

In Spring OAuth2, why is my resource still unprotected even though I have marked it in my resource server?

i,
I’m using Spring 3.2.11.RELEASE with spring-security-oauth2 v 2.0.7.RELEASE. I want to set up a resource server such that if a client properly submits their id and secret with a grant type of client_credentials, they can use the resulting token to POST data to a URL. However, I can’t figure out how to secure that URL (I can POST to that URL even if the token is incorrect). Below is how I’m trying to configure my resource server
<oauth:resource-server id="resourceServerFilter" entry-point-ref="entry"
resource-id="writeUrl" token-services-ref="tokenServices" />
<bean id="entry" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/writeUrl" />
</bean>
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="my-client-with-secret"
authorized-grant-types="client_credentials" authorities="ROLE_CLIENT"
scope="read,write" secret="secret" />
</oauth:client-details-service>
But, as I said, POSTing data to “/writeUrl” whether or not I have a valid token is successful and I wish to lock down that URL. How do I do that? My complete OAuth2 application context configuration is below …
<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 pattern="/oauth/(users|clients)/.*" request-matcher="regex"
create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint"
use-expressions="true" xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/oauth/users/([^/].*?)/tokens/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')"
method="DELETE" />
<intercept-url pattern="/oauth/users/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')"
method="GET" />
<intercept-url pattern="/oauth/clients/.*"
access="#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')"
method="GET" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<expression-handler ref="oauthWebExpressionHandler" />
</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 pattern="/me/**" 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="/me" access="ROLE_USER,SCOPE_READ" />
<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="sparklr2" />
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="sparklr2/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 id="userDetailsService">
<user name="marissa" password="koala" authorities="ROLE_USER" />
<user name="paul" password="emu" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<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="tokenEnhancer" ref="tokenEnhancer" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="tokenEnhancer"
class="org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter" />
<bean id="requestFactory"
class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
<constructor-arg name="clientDetailsService" ref="clientDetails" />
</bean>
<bean id="approvalStore"
class="org.springframework.security.oauth2.provider.approval.TokenApprovalStore">
<property name="tokenStore" ref="tokenStore" />
</bean>
<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>
<oauth:resource-server id="resourceServerFilter" entry-point-ref="entry"
resource-id="writeUrl" token-services-ref="tokenServices" />
<bean id="entry" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/writeUrl" />
</bean>
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="my-client-with-secret"
authorized-grant-types="client_credentials" authorities="ROLE_CLIENT"
scope="read,write" secret="secret" />
</oauth:client-details-service>
<mvc:default-servlet-handler />
<oauth:expression-handler id="oauthExpressionHandler" />
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
There should be an intercept-url in the resource endpoint configuration with access restricted to authorized users (I'm guessing your url could match the following pattern)
<security:http pattern="/writeUrl/**"
create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager">
<security:anonymous enabled="false" />
<security:intercept-url pattern="/**"
access="IS_AUTHENTICATED_FULLY"/>
<security:custom-filter ref="resourceServerFilter"
before="PRE_AUTH_FILTER" />
<security:access-denied-handler ref="oauthAccessDeniedHandler" />
</security:http>

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" />

Adding Remember Me functionality in already configured spring OUTH and usernamepassword token authentication system

I have the following spring security configuration.
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- For S2OAuth endpoints -->
<http pattern="/oauth/token"
create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="oauthAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
</http>
<http use-expressions="true">
<!-- Authentication policy -->
<form-login login-page="/signin" login-processing-url="/signin/authenticate" authentication-failure-url="/signin?error=1" />
<logout logout-url="/signout" delete-cookies="JSESSIONID" />
<!-- Remember Me -->
<remember-me services-ref="rememberMeServices" key="myRememberMeKey" />
<!-- Authorization policy definition: TODO consider replacing with #Secured on #Controllers -->
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/favicon.ico" access="permitAll" />
<intercept-url pattern="/members/**" access="permitAll" />
<intercept-url pattern="/groups/**" access="permitAll" />
<intercept-url pattern="/pubsub/**" access="permitAll" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/signup" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<intercept-url pattern="/signin" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<intercept-url pattern="/signin/*" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<intercept-url pattern="/reset" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<!-- TODO this would probably be better mapped to simply /invite?token={token} but not able to vary security policy here based on presence of a request parameter. Consider #Secured on #Controller. -->
<intercept-url pattern="/invite/accept" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<!-- TODO this should be restricted to admin users only -->
<intercept-url pattern="/admin/**" access="permitAll" />
<intercept-url pattern="/**" access="isAuthenticated()" requires-channel="#{environment['application.secureChannel']}" />
<custom-filter ref="resourceServerFilter" before="EXCEPTION_TRANSLATION_FILTER" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="usernamePasswordAuthenticationProvider" />
</authentication-manager>
<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>
<bean id="jdbcRememberMeRepository" class="com.springsource.greenhouse.rememberme.JdbcRememberMeRepository" xmlns="http://www.springframework.org/schema/beans"/>
<bean id="coreUserDetailsService" class="com.springsource.greenhouse.rememberme.RememberMeUserDetailsService" xmlns="http://www.springframework.org/schema/beans"/>
<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices" xmlns="http://www.springframework.org/schema/beans">
<property name="tokenRepository" ref="jdbcRememberMeRepository" />
<property name="userDetailsService" ref="coreUserDetailsService" />
<property name="key" value="myRememberMeKey" />
<property name="alwaysRemember" value="true" />
</bean>
<!-- For S2OAuth endpoints -->
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<beans:bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<beans:constructor-arg ref="clientDetails" />
</beans:bean>
<beans:bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<beans:property name="realmName" value="greenhouseApi" />
</beans:bean>
<beans:import resource="security-oauth-provider.xml" />
</beans:beans>
When I tick the remember-me checkbox , I see my remember-me database is populated as shown in the snapshot. Now I close the browser and try to access the url which needs sign-in. I am able to see the page. Now here I am confused about whether I am able to see the page because of login or because of remember-me. Secondly I see in the remember-me database table the last date is not updated. What can be the reasons for this?
Restarting your browser is not enough. To test remember me functionality you need to be sure that your session is expired. If lastUsed was not updated then it means that remember me functionality was not used. In your case HTTP session was active. You need to deactivate it and there are multiple options to do it:
wait for session expiration. Hint: you can set minimal session timeout, for example set 1 minute in your web.xml.
or remove session cockie (do not remove all coockies for your domain, remember me use coockie too)
or stop your application server then clean up a directory where it persist session data and start it again. For tomcat it is tomcat_root/work.
Setting up session timeout value to 1 minute in web.xml:
<session-config>
<session-timeout>1</session-timeout>
</session-config>

Adding Remember Me functionality throws exception

I have the following spring security configuration.
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- For S2OAuth endpoints -->
<http pattern="/oauth/token"
create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="oauthAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
</http>
<http use-expressions="true">
<!-- Authentication policy -->
<form-login login-page="/signin" login-processing-url="/signin/authenticate" authentication-failure-url="/signin?error=1" />
<logout logout-url="/signout" delete-cookies="JSESSIONID" />
<!-- Authorization policy definition: TODO consider replacing with #Secured on #Controllers -->
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/favicon.ico" access="permitAll" />
<intercept-url pattern="/members/**" access="permitAll" />
<intercept-url pattern="/groups/**" access="permitAll" />
<intercept-url pattern="/pubsub/**" access="permitAll" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/signup" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<intercept-url pattern="/signin" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<intercept-url pattern="/signin/*" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<intercept-url pattern="/reset" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<!-- TODO this would probably be better mapped to simply /invite?token={token} but not able to vary security policy here based on presence of a request parameter. Consider #Secured on #Controller. -->
<intercept-url pattern="/invite/accept" access="permitAll" requires-channel="#{environment['application.secureChannel']}" />
<!-- TODO this should be restricted to admin users only -->
<intercept-url pattern="/admin/**" access="permitAll" />
<intercept-url pattern="/**" access="isAuthenticated()" requires-channel="#{environment['application.secureChannel']}" />
<custom-filter ref="resourceServerFilter" before="EXCEPTION_TRANSLATION_FILTER" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="usernamePasswordAuthenticationProvider" />
</authentication-manager>
<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>
<!-- Remember Me -->
<http>
<remember-me services-ref="rememberMeServices" key="myRememberMeKey" />
</http>
<bean id="jdbcRememberMeRepository" class="com.springsource.greenhouse.rememberme.JdbcRememberMeRepository" xmlns="http://www.springframework.org/schema/beans"/>
<bean id="coreUserDetailsService" class="com.springsource.greenhouse.rememberme.CoreUserDetailsService" xmlns="http://www.springframework.org/schema/beans"/>
<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices" xmlns="http://www.springframework.org/schema/beans">
<property name="tokenRepository" ref="jdbcRememberMeRepository" />
<property name="userDetailsService" ref="coreUserDetailsService" />
<property name="key" value="myRememberMeKey" />
<property name="alwaysRemember" value="true" />
</bean>
<!-- For S2OAuth endpoints -->
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<beans:bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<beans:constructor-arg ref="clientDetails" />
</beans:bean>
<beans:bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<beans:property name="realmName" value="greenhouseApi" />
</beans:bean>
<beans:import resource="security-oauth-provider.xml" />
</beans:beans>
I am getting the following exception.
Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: No AuthenticationEntryPoint could be established. Please make sure you have a login mechanism configured through the namespace (such as form-login) or specify a custom AuthenticationEntryPoint with the 'entry-point-ref' attribute
Offending resource: class path resource [com/springsource/greenhouse/config/security.xml]
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)
...
I am new to spring security. I don't know the cause of this exception.
You cannot add your remember-me declaration into new empty http element. Use some other existed http element where authentication entry point is already configured (for example via form-login element):
<http use-expressions="true">
<!-- Authentication policy -->
<form-login login-page="/signin" login-processing-url="/signin/authenticate" authentication-failure-url="/signin?error=1" />
<remember-me services-ref="rememberMeServices" key="myRememberMeKey" />
...
<http/>

Resources