httpsession lifecycle events not working after using my custom UserDetailsService - spring

I've created my own UserDetailsService and UserDetails implementations and hooked it up. I can create users, and login as users. However, if I login, logout, and then login again, I'm getting a redirect to a timeout error page. This is because I'm preventing concurrent logins, but it's not working - it used to with the "hello world" auth examples, but now with my own implementations that piece has stopped working correctly for some reason. Spring basically thinks there are 2 sessions when I login, logout, and login again.
Now - I thought this was all handled automatically ....perhaps using your own UserDetailsService means you actually have to implement session management somewhere else as well? I'm sort of blown away that's not mentioned in the docs or in the book Spring Security 3.1 so I'm assuming I'm missing something.
This is in my web.xml bit for listening to session life cycle events
<!-- This listener updates spring-security on httpsession lifecycle events,
in this case to ensure each user can have only 1 session at a time. -->
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
and this is in my security.xml to prevent concurrent logins
<!-- This prevents the user from logging in more than once simultaneously -->
<security:session-management
invalid-session-url="/timeout.htm">
<security:concurrency-control
max-sessions="1" error-if-maximum-exceeded="true" />
</security:session-management>
My logout in the security context file is
<security:logout logout-url="/logout"
invalidate-session="true" delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE"
logout-success-url="/login.htm?logout" />
I've tried a few permutations of that. None seem to work. invalidate-session="true" is the default value, so I shouldn't even have to specify this. But it doesn't seem to be happening.
O.k., I just reverted everything to try and do in-memory auth and I'm getting the same errors. Meaning, I'm not using my custom implementations anymore. Sorry - I clearly have something wrong somewhere...and this is proving extremely difficult to find. I might have to start from scratch.
Do I have to do something special on logout with my custom UserDetailsService?
Any feedback or guidance is much appreciated.

To my understanding the error-if-maximum-exceeded attribute should be false. Settings the value to false will cause the original session to expire instead of throwing a exception as explained in http://docs.spring.io/spring-security/site/docs/3.0.x/reference/appendix-namespace.html#d0e7768

I discovered it was a conflict between using <session-management> in my configuration and my servlet container. I'm using STS 3.5 (custom Eclipse for Spring projects) with vFabric server that runs in the IDE. The Reference documentation did not refer to this in the actual Session Management section (Section 8). However, buried in Section 4 on auth is this little gem:
Note that if you use this mechanism to detect session timeouts, it may falsely report an error if the user logs out and then logs back in without closing the browser. This is because the session cookie is not cleared when you invalidate the session and will be resubmitted even if the user has logged out. You may be able to explicitly delete the JSESSIONID cookie on logging out, for example by using the following syntax in the logout handler:
<http>
<logout delete-cookies="JSESSIONID" />
</http>
Unfortunately this can’t be guaranteed to work with every servlet container, so you will need to test it in your environment
Well, apparently it doesn't work in STS 3.5
At first I tried to eliminate sections of my <session-management> tag so I could just control concurrency (i.e. have the user only able to log in with one session at a time). However, I just kept getting errors.
So, at this point I've removed the session management stuff altogether and will come back to it when ready to deploy.

Related

Problems redirecting to access token entry point Oauth Token

I am having problems with redirecting to access token entry point /oauth/token which will detail bellow. I am hoping someone could give me some light to it as I took a lot of time
implementing this.
Also, interesting is the fact that I cannot test with with SoapUI 5.0 community edition even following their instructions. It gest the authorization code but fails later as you need to set the redirect URI as "urn:ietf:wg:oauth:2.0:oob".
Since Spring-Security-Oauth2 lacks a lot of good documentation and I had spent lots of time debugging and documenting the work I decided to share
my comments and configuration code here which might also be helpfull to someone else.
I am using the following dependencies versions on my pom:
<org.springframework-version>4.0.5.RELEASE</org.springframework-version>
<org.springframework.security-version>3.2.5.RELEASE</org.springframework.security-version>
<org.springframework.security.oauth2-version>2.0.3.RELEASE</org.springframework.security.oauth2-version>
Now, the idea was to implement all the clientId objects, UserPrincipal, access, nonce and token stores using Cassandra as a persistency store. All those components
are working perfectly and are tested. In fact, it fetches all the authentication/authorization, creates authorization codes.
I've seen a bug recently on testing JDBC stores on Spring Oauth2 github but that was related to testing not the actuall implementation, specially because not
using JDBC.
I have wrote a client webapplication to access a REST resource which resides with the OAuth2 servers and Spring Security for logging in. All goes well until I go request
an access token to /oauth/token.
When I hit the secure Rest service first it properly starting doing redirections and goes tru DefaultOAuth2RequestFactory createAuthorizationRequest() method. Loads
the ClientDetails object perfectly with the secret etc from the store. So it has all the scopes, grants etc for the Oauth2 client. It also validates properly the redirectURIParameter
and resolves the redirect. Then it goes to the TokenStoreUserApprovalHandler and create the OAuth2Request object. Then, of course, tries to find an existing access token which
does not exist yet on the workflow. It creates the authenticationkey from authenticationKeyGenerator and queries the store which properly returns null at this point.
Then it redirects back to /oauth/authorize twice when on the second time it has an authorization code and marks as approved (AuthorizationEndPoint) inside approveOrDeny() method.
The authorizationCodeServices creates the code and stores it properly in my Cassandra store.
At this point it calls (AuthorizationEndPoint) getSuccessfulRedirect() where it adds the state parameter to the template.
Then it calls (OAuth2RestTemplate class) getAccessToken() method. Since the access token is fine and valid it then calls acquireAccessToken() which returns an accessTokenRequest with
{code=[Q19Y6u], state=[1PyzHf]} . Then it calls the accessTokenProvider to obtain an access token at obtainAccessToken(). Then the calls OAuth2AccessTokenSupport is called at
retrieveToken() method. And fails at getRestTemplate. The AuthorizationCodeResourceDetails is created perfectly with grant type authorization_code and it has both authorizationScheme
and clientAuthenticationScheme as header. The clientId is correct as the clientSecret. The id of the AuthorizationCodeResourceDetails is oAuth2ClientBean and the userAuthorizationURI is
http://myhost.com:8080/MyAPI/oauth/authorize. Headers show as
{Authorization=[Basic .....]}
The extractor is org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.
The form is {grant_type=[authorization_code], code=[Xc7yni], redirect_uri=[http://myhost.com:8080/OAuthClient/support]}
And then the application freezes and it shows on the logs:
DEBUG: org.springframework.security.authentication.DefaultAuthenticationEventPublisher - No event was found for the exception org.springframework.security.authentication.InternalAuthenticationServiceException
DEBUG: org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.InternalAuthenticationServiceException
Then I have the following Exception on my client web application:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is error="access_denied", error_description="Error requesting access token."
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
Now, I believe I have a few things wrong on my xml configuration for OAuth2 and Spring Security. So the configuration file follows.
I do have a few questions concerning it so here are the questions first:
1) <oauth2:authorization-server> I am not sure if I had this configured correctly. Please look at the comments I have on my xml file.
I have added the authorization-request-manager-ref parameter which points to my userAuthenticationManager bean, a authentication manager
which takes a authentication-provider user-service-ref="userService"
<oauth2:authorization-server client-details-service-ref="webServiceClientService"
token-services-ref="tokenServices" user-approval-page="/oauth/userapproval"
error-page="/oauth/error" authorization-endpoint-url="/oauth/authorize"
token-endpoint-url="/oauth/token" user-approval-handler-ref="userApprovalHandler"
redirect-resolver-ref="resolver">
<oauth2:authorization-code
authorization-code-services-ref="codes" />
<oauth2:implicit/>
<oauth2:refresh-token/>
<oauth2:client-credentials/>
<oauth2:password authentication-manager-ref="userAuthenticationManager"/>
<!-- <oauth2:custom-grant token-granter-ref=""/> -->
</oauth2:authorization-server>
2) authentication-manager oauthClientAuthenticationManager is used when "/oauth/token" is intercepted.
This is defined as follows:
<sec:authentication-manager id="oauthClientAuthenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService">
<sec:password-encoder ref="passwordEncoder" />
</sec:authentication-provider>
</sec:authentication-manager>
3) I have the following methodSecurityExpressionHandler bean defined which is used at sec:global-method-security.
Not sure if this is correct or not as well.
<beans:bean id="methodSecurityExpressionHandler"
class="org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler" />
4) I also have a bean "clientCredentialsTokenEndpointFilter" which I believe is not recommended.
I use this as a custom-filter for entry point "/oauth/token" but I believe this is wrong.
<beans:bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<beans:property name="authenticationManager" ref="oauthClientAuthenticationManager"/>
</beans:bean>
A filter and authentication endpoint for the OAuth2 Token Endpoint. Allows clients to authenticate using request
parameters if included as a security filter, as permitted by the specification (but not recommended). It is
recommended by the specification that you permit HTTP basic authentication for clients, and not use this filter at
all.
5) Now for the Oauth Token Endpoint:
This is the endpoint /oauth/token as I have many questions here:
This is never reached.
Shall I have a custom-filter like clientCredentialsTokenEndpointFilter to it or not?
Do I have to have a http-basic entry point?
Shall I have an access attribute as in IS_AUTHENTICATED_FULLY or can I use an authority I have defined on my UserPrincipal object such as OAUTH_CLIENT I have added there?
What about the session? Shall I say "stateless" or
"never"
Shall I add the corsFilter to it as well?
Is the entry point correct? Which is the OAuth2AuthenticationEntryPoint class?
Do I have to add the csrf token? I believe not as it will
restrict it more.
Is the expression-handler correct as a org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandle?
The authentication-manager-ref I can change from oauthClientAuthenticationManager to userAuthenticationManager.
<sec:http use-expressions="true" create-session="stateless"
authentication-manager-ref="userAuthenticationManager"
entry-point-ref="oauthAuthenticationEntryPoint" pattern="/oauth/token">
<sec:intercept-url pattern="/oauth/token" access="hasAuthority('OAUTH_CLIENT')" />
<!-- <sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" /> -->
<sec:http-basic entry-point-ref="oauthAuthenticationEntryPoint"/>
<!-- <sec:http-basic/> -->
<sec:anonymous enabled="false" />
<sec:custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
<sec:expression-handler ref="webSecurityExpressionHandler" />
<!-- <sec:custom-filter ref="corsFilter" after="LAST"/> -->
</sec:http>
I would like to add the full config file here but there is a limit.
The /oauth/token endpoint should be secured with client credentials, and it looks like you wired it to a user authentication manager. You didn't show the configuration or implementation of that, but judging by the InternalAuthenticationServiceException it is failing with an exception that isn't classified as a security exception. Fix those two things and you might be in business.
(The #Configuration style is much more convenient by the way, and I would recommend getting started with that and more of the defaults it provides, until you get the hang of it.)

How to Check whether a valid session is still existing at IDP?

I've implemented SSO using Spring Security SAML. Here is what currently working for me:
When I try to access any resource at SP, I'm redirected to my IdP(idp.ssocircle.com in my case) if I'm not logged in already. After successful authentication at IDP, I'm redirected back to SP and authorize the incoming SAML response and create a session for the respective user. Everything is cool till here!
But when I log out from my IDP(by clicking logout from idp.ssocircle.com externally), I shouldn't be able to access my SP which is not happening in my case.
Now what I'm thinking to do is may be write a new filter which checks for a valid session at IDP before processing any request on SP. I've searched a lot but couldn't find any solution to my problem.
Please give inputs on how can I implement this filter or is there any other way of doing this? Any suggestions are appreciated.
Does your IDP support and correctly initialize Single Logout? If so it could be related to this issue, just update to latest Spring SAML version or change property invalidateHttpSession in your logout handler to true:
<bean id="logoutSessionHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="true"/>
</bean>

How to handle requests if no matching spring security <intercept-url>?

I'm using spring 3.1.1 and spring security 3.1.0. I'd like to enforce a policy that all http requests that are not explicitly configured with an <intercept-url pattern="..." access="..."/> entry are handled in a particular way. For requests that match a configured <intercept-url/> I want to use typical role based access decisions. However, for non-matching requests, I want to either respond with a 404 (not found) (or maybe 403/forbidden). I want to do this so that I and other team members are forced to explicitly configure spring security and associated roles for any new endpoints.
I originally thought that I could use <intercept-url pattern="/**" access="denyAll"/> as the last intercept-url and that spring would do what I wanted. This technique works if the user is already authenticated but is a little strange for unauthenticated/anonymous users. For anonymous users, spring detects (in ExceptionTranslationFilter) that the user is anonymous and starts the authentication process when requests like /missingResource are processed. Typically this means that the user is redirected to a login form and, after logging in, is redirected back to /missingResource. So the user has to login in order to see a 404 (not found) page.
I ended up removing the intercept-url pattern="/**" access="denyAll"/> and writing a custom filter that runs after="FILTER_SECURITY_INTERCEPTOR" and responds with 404 for requests that are not matched by the FilterSecurityInterceptor but it seemed a little complicated. Is there a better or simpler way?
you can define a separate http element for intercept url /** with access ="denyAll" and add a custom entry-point-ref to avoid spring to redirect user to login form, you can use existing entryPoint Http403ForbiddenEntryPoint for showing 403 error response or implement your own by implementing AuthenticationEntryPoint.
Hope it helps.

Clear Spring Session on Login?

Update: I'd expected this problem was related to the specific Spring version I was using, but Rob's answer indicates it's probably something specific to my environment. We've found a workaround for this particular issue (a filter that manually clears the session on login), so I've marked Rob's answer as correct.
In my Spring application, my sessions are being persisted between logins. I'd like for the session to be cleared on login.
I am using Spring Security 2.0.4 (don't ask), and my security configuration looks something like this
<security:http ...
session-fixation-protection="newSession">
...
<security:logout invalidate-session="true" logout-success-url="/login.html" />
</security:http>
I was under the impression that session-fixation-protection="newSession" would clear sessions on user logins. Another interesting point is that sessions are being cleared on logout, so invalidate-session="true" has the desired effect.
When testing, I use the following methods:
#RequestMapping(value = "writeSession")
String writeSession(HttpServletRequest request) {
request.getSession().setAttribute("username", MySecurityService.getLoggedInUsername());
...
}
#RequestMapping(value = "readSession")
String readSession(HttpServletRequest request) {
log.info("Current username: " + request.getSession().getAttribute("username");
...
}
Then I:
Login as user1
Visit writeSession (sets session username to "user1")
Visit readSession (log output: Current username: user1)
Login as user2 (without logging out)
Visit readSession (log output: Current username: user1)
Note that if I logout between steps 3 and 4, I get the expected results (Current username: null)
I created a sample that demonstrates that in general this should work. See the the so-clear-spring-session-on-login branch of this git repo for a working example.
There could be a few things that may cause you issues depending on the rest of your configuration.
Can you share the rest of your Spring Security configuration? For example, if you are switching between HTTP and HTTPS it may be switching which session is being used. You might refer to the Spring Security FAQ for more details on what can go wrong with sessions.
What does the implementation of MySecurityService look like?
What does your web.xml look like?

Disable SpringSecurity's SavedRequest storing logic

We are using Spring Security for managing authentication. The issue we are seeing is that when a user's session is timed out between bringing up a GET form and hitting the save button that does a POST, they are sent to the login page but spring is saving the original post information in the session.
Our app does not bring them back to the original URL after login, but instead sends them back to a common starting page. This works fine, but when the user happens to return to the page they had originally tried to POST to (the form GET and POST are the same URLs) Spring tries to resubmit the POST automatically which is not what we want.
Is there a way to completely disable the SavedRequest storing logic in Spring?
I guess this jira issue of spring security describes your problem and how to handle this.
Based on Nathan's comment on Raghuram's answer, with namespaced XML it's something like this:
<security:http>
<security:request-cache ref="nullRequestCache" />
<!-- ... -->
</security:http>
<bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache" />
There are two scenarios:
1) If you want that after relogin, user should always get forwarded to the default target URL instead of the orginal requested URL then put always-use-default-target="true" in your security.xml like
<http auto-config="true">
.....
<form-login login-page="/login" always-use-default-target="true" default-target-url="/xyz"
authentication-failure-url="/login?error=true" login-processing-url="/j_security_check"/>
</http>
1) If you want that on session timeout after relogin, user should forward to the orginal requested URL but you do not want to resubmit the form then put session-fixation-protection="newSession" in your security.xml like
<http auto-config="true">
<session-management session-fixation-protection="newSession"/>
.....
</http>
Please put session-management tag as first line in http configuration.
It looks like the session-fixation-protection="newSession" attribute on (2.0) or (3.0) will also resolve the issue
With Spring 4.2.5 I ran into this too.
My case was almost identical: display GET form, wait for session timeout, then POST the form. In my app after re-authentication a start page is displayed. However, if the user then navigates to this GET form, and POSTs it, then the previous POST parameters are remembered and concatenated to the current request, resulting in comma separated values in the #RequestParam variables.
I dumped the session in my authentication controller and indeed I saw a "SPRING_SECURITY_SAVED_REQUEST" named key.
The spring documentation says that by default a "SavedRequestAwareAuthenticationSuccessHandler" is used for retrieving the saved request data from the session and apply it to the request.
I tried to use a do-nothing successHandler but couldn't make it work.
I also tried applying
http.sessionManagement().sessionFixation().newSession();
to the security config but that didn't help.
However
http.requestCache().requestCache(new NullRequestCache());
solved the issue.

Resources