-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Validate Asp.Net Identity Security Stamp #1203
Comments
Asp.Net (both Core and earlier versions) has always relied on encrypted cookies as the default session management mechanism. The advantages is that it requires no state on the server and works seamlessly across different nodes in a web farm. The disadvantage is that without any records on the server side for what sessions are active, there is no easy way to invalidate sessions if needed. The SecurityStamp was added as a way to solve this and works by tapping into the session cookie validation and renewal. OpenID Connect contains a session management feature relying on iframes that allows a client application to detect when the parent SSO session on the OIDC Provider is ended. With recent security best practices that technique should no longer be used however. The advice currently is to use server side session and OIDC handling for all web clients and rely on refresh tokens for token renewal. We have added features in IdentityServer to handle session lifetime synchronization in these cases. When session lifetime coordination is enabled, any use of a refresh token will renew the corresponding server side session. When the server side session ends, the refresh token is revoked. The client application can then detect that token renewal fails and logout the local session. These two features were developed independently and their feature set partly overlaps. The security stamp feature of Asp.Net Identity was intended for handling local cookie-based sessions and to avoid the need to set up server side sessions. For an OpenID Provider such as IdentityServer this however not enough. That is why we have implemented server side sessions for these kind of scenarios. Once server side sessions are available, there is no longer any need to use the security stamp feature. If there is a need to invalidate a session it is much easier to just revoke the session on the server side. In your scenario, I would recommend:
|
I'm trying to make a start on this, but there's a few concepts I still find confusing. I'll start by noting that I was looking at Server Side Sessions before, and had an issue logged here #778 but we never really resolved it - we've had them enabled, along with refresh tokens, but we don't have the cookie handler checking for terminated sessions on the clients.
|
|
Thanks for the responses. I got the sliding expiration working - what I had done wrong was that I had set the SlidingRefreshTokenLifetime, but I'd never set RefreshTokenExpiration = TokenExpiration.Sliding, meaning the lifetime i set was ignored. For 3 and 4... so I understand that the clients shouldn't have direct access to the Identity DB, but if the Identity Server is using server side sessions in the SQL Server store, and the client is using cookies, shouldn't there be a way to force it to check if the access token/refresh tokens have expired? i.e. I was thinking something like this:
This doesn't work because GetAccessTokenAsync hits the cookie validation, so its just an infinte loop, but hopefully my intent is made clear - basically, if I've got a page that doesn't hit the API but has an [Authorize] attribute, I'd like to check if the user has a valid access token (or gain new one via refresh token), and make them sign in again if they don't. I feel like this should be doable and that I'm just missing something simple. |
That is an interesting approach and it can be done. However as you have found out the helper libraries do call back into the cookie handler so they cannot be used directly. Before going into details on the solution I want to point out that this can be achieved by implementing single logout (possibly via back channel). Single Logout is a push model for expired/logged out sessions while checking for a valid access token is a pull model. I think that the easiest way to implement this check in the Storing is done by updating the properties in |
I added DuendeSoftware/foss#52 as a feature idea to our access token management library. |
Thanks again - I'll take a look at this Monday and get back to you. My other dev is working on Back Channel Logout, but figuring this out is important for me in fully understand the different concepts and seeing what their limitations may be. I guess my question for now.. continuing with the scenario where I've got a page that doesn't hit an API , but instead hits a local controller with authorize attribute... In this proposed solution, if the auth token is expired and so has the refresh token due to sliding expiration, when we try to get a new token we'd find out we cant and invalidate the session... But if all we do is implement back channel logout and the tokens are expired and we arent hitting an api, what would trigger the back channel logout? I thought backchannel logout would only trigger on user initiated logout, ending a session from session management or when hitting an api with an expired token. |
Still having no luck with this - I was looking at calling the endpoint manually, but when i noticed how much validation the AccessTokenManagement library was doing I was starting to think this wasn't a good idea. this started getting messy as I had my own copies of GetAccessTokenAsync, RefreshUserAccessTokenAsync, GetTokenAsync, GetTokenValue, and a bunch of helper methods,, and I couldn't quite get it to work anyways. I went back to looking at just calling the library directly and just using refelction so that those methods don't create an infinite loop, i.e.
This works nicely while the token is valid, but when the token needs refreshing, it never returns from UserTokenRequestSynchronization.SynchronizeAsync, and I can't figure out why. |
I don't think that using Duende.AccessTokenManagement as it currently is implemented from the If you want to implement something in the I also opened the mentioned issue in the Duende.AccessTokenManagement repository to discuss if this is a feature we want to add to that library. |
I've managed to get back-channel logout working, and in the process I wanted to test the ExpiredSessionsTriggerBackchannelLogout feature which prompted me to think about the length of our server-side sessions. I noticed that the session length defaulted to 14 days, and so to test the session expiration I set the CookieAuthenticationOptions.ExpireTimeSpan to a shorter length of time and it appears to have triggered a back-channel logout after this time elapsed and the expired sessions were cleaned up. But that got me thinking about our client application which doesn't use an API, and another client app on which some pages make API calls and others are just marked with the Authorize attribute. Say this app has access token lifetime of 5 minutes, sliding refresh token lifetime of 10 minutes, absolute refresh token lifetime of a few days, and the default authentication ticket lifetime of 14 days. If my access and refresh token lifetimes lapse and I no longer have access to the API, I could still have access to pages which are not API-protected for a few days, which is probably not what we would want. We've discussed here already adding our own cookie validation event handler which we could use in this case to sign the user out if they don't have valid tokens, but it made me wonder:
|
I started to call the endpoint manually, so I've ended up with this for now:
It obviously doesn't have all the checks that access token management has, but I'm unsure if it needs to. If I'm satisfied with the solution @carnahanliam puts in place for back channel I may be able to scrap this - but from what I could see when he was showing me his work in progress today, any pages that didn't hit the API would not extend their session, meaning the expired sessions would trigger back channel logout and the users would lose their work, so I feel i may have to still do this even with back channel logout. |
No, there is no assumption, but regularly refreshing access tokens have the side effect to keep the sessions in sync.
In most scenarios I would recommend to have the refresh token working for the duration of the application sessions, i.e. the refresh token should not expire before the application session expires. @MH61Aus Yes, if you are signed right back in through SSO it's probably because your IdentityServer session is still working. If you follow the flow in browser development tools you should be able to see what's happening. |
Things are coming together and starting to make sense to me now. Thank you @AndersAbel! |
I just wanted to continue this, as we still are trying to figure out our solution. We've just left the ticket idle for days as we've gone back through documentation/pluralsight courses/etc as a refresher on all the concepts as we've started second guessing ourselves a little. I think we're on the right track, but I think there's still a few bits where I get confused, or where the documentation can be confusing. One doc I've been leaning on here is the Inactivity Timeout doc at https://docs.duendesoftware.com/identityserver/v7/ui/server_side_sessions/inactivity_timeout/ - we're in healthcare, which is why this requirement is pertinent to us. I think one of the biggest points of confusion for us was the absolute lifetime of the refresh token vs the cookie/session lifetime , which I think we've mostly figured out now after a heap of trial and error. So to have an inactivity timeout when screen is idle between 5-10 mins, we're finding that things work when with a configuration something like: From my experiments/understanding this would log the user out if they are idle for 10 mins. One thing I'm confused about is with the CoordinateLifetimeWithUserSession. I've that enabled along with RemoveExpiredSessions. So then when the job rans and hits GetAndRemoveExpiredSessionsAsyc, there's no expired session for it to find, meaning it doesn't know to delete the refresh token from the store. I don't think this is necessarily a problem since the session is ended and we'll be forcing signout, but I thought I'd mention it. |
I will have to investigate this, will get back to you once I've looked into it. |
This one was certainly not trivial, but it looks like there is a bug in IdentityServer. With "normal" production-level timeouts the expired sessions job will be the one removing the session in almost all cases. In your testing setup with shorter lifetimes the cookie handler detects and handles the timedout session in most cases. It doesn't handle the revocation correctly. It should of course work even with short lifetimes so I've filed a bug at DuendeSoftware/IdentityServer#1552. I'm closing this issue now and we're tracking the bug fix in the linked issue in the IdentityServer repo. |
Which version of Duende IdentityServer are you using?
7
Which version of .NET are you using?
8
Describe the bug
Question - I've got an implementation using Asp.Net Identity Integration.
Asp.Net Identity uses a SecurityStamp in its store. when changing something like your password, the security stamp is updated. if your session has an old security stamp, it'll be terminated.
I'm not really seeing any support for this in Duende clients. but I feel like its something we'd want.
To Reproduce
In samples repo, open IdentityServer\v7\AspNetIdentity\IdentityServerAspNetIdentity
in the startup on line 34, after DefaultIdentity has been added, add the following line:
services.Configure<SecurityStampValidatorOptions>(o => o.ValidationInterval = TimeSpan.FromSeconds(30));
Login in and navigate to the user profile pages in identity.
Throw a breakpoint on
Microsoft.AspNetCore.Identity.SecurityStampValidator.ValidateAsync
Open a second window in incognito - Login with the same user, change password.
In the original window, click any link or part of the profile page. the request will be intercepted, and the validator will run (it might skip the validate check if 30 seconds hasn't passed). The validator will determine that the security stamp is invalid and log the user out.
However, doing anything in the client isn't going to trigger this validation. if instead of clicking around the profile page, you'd clicked around the client instead, you'd just stay logged in, and it there's nothing checking that your security stamp is invalid.
We're looking at doing something like the following on each client:
Add a cookie event handler that hits the User Info endpoint. The SecurityStamp would have to be part of the claims. somehow here we'd have to check if the value has changed, and the cookie handler would sign the user out.
So I guess the quesiton is - is there a recommended way to do this? any gotchas or specific reasons you haven't implemented it already?
related - we never got back channel logout working, but if we could get this working, I figure we could just update the security stamp on logout.
The text was updated successfully, but these errors were encountered: