Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IdentityServer4 Revoke all reference tokens for a client, except the current one

I have a single page application which is protected by IdentityServer4 reference tokens.

I expect users to login to from multiple computers/devices.

In the settings area of the app, the user can change their password. To do so, they must enter their current password, as well as the new password.

I also wish to give the user the option to "Logout all other devices and computers".

If the user ticks this option, I want to invalidate any other reference tokens that exist for this client and this user, but I do NOT want to invalidate the reference token the user is currently using.

I only want it to logout other devices and computers. The user should stay logged in on the computer they are using.

For the life of me, I cannot see a way to do this with IdentityServer4. I was thinking I could simply run a delete on the PersistedGrants table, however I have no way of knowing which of the persisted grants in this table is the one the user is currently using.

Please help!

like image 489
NoPyGod Avatar asked Nov 29 '25 18:11

NoPyGod


1 Answers

I was finally able to solve this. Make sure you're using the latest version of IdentityServer, as it includes a session_id column on the PersistedGrants table. With that, the solution is clear.

When user changes password:

        if (model.EndSessions)
        {

            var currentSessionId = User.FindFirst(JwtClaimTypes.SessionId).Value;

            foreach (var grant in db.PersistedGrants.Where(pg => pg.ClientId == "the-client-name" && pg.SubjectId == user.Id.ToString() && pg.SessionId != currentSessionId).ToList())
            {
                db.PersistedGrants.Remove(grant);
            }

            db.SaveChanges();

            await userManager.UpdateSecurityStampAsync(user);

        }

The user's other tokens are now revoked.

However, the user (on their other computer/devices) will likely still have an authentication cookie, so if they were to go to the authorization endpoint they would be granted a new token without having to login again.

To prevent that, we intercept the request for a new token with a CustomProfileService, like so -

    public override async Task IsActiveAsync(IsActiveContext context)
    {

        //only run check for cookie authentication
        if (context.Subject.Identity.AuthenticationType == IdentityConstants.ApplicationScheme)
        {

            var validationResponse = await signInManager.ValidateSecurityStampAsync(context.Subject);

            if (validationResponse == null)
            {
                context.IsActive = false;
                return;
            }

            var user = await userManager.GetUserAsync(context.Subject);

            context.IsActive = user.IsActive;

        }


    }
like image 88
NoPyGod Avatar answered Dec 04 '25 18:12

NoPyGod



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!