4

I'm using password grant flow, with asp.net Identity.

I want to kill all refresh token for a user, every time a login its executed. I need this to kill its "session" even when he sign in with a different device, like other pc, or smartphone.

So, how can I do it?

Can I just do a UserManager.UpdateSecurityStampAsync(user.Id);, or I need something else?

Thank you so much for your help!

Kévin Chalet
  • 39,509
  • 7
  • 121
  • 131
Jedi31
  • 735
  • 1
  • 6
  • 22

1 Answers1

5

Can I just do a UserManager.UpdateSecurityStampAsync(user.Id); or I need something else?

This is definitely possible. For that, simply tweak your token endpoint to ask Identity to validate the security stamp before returning a valid token response. Here's an example:

[HttpPost("~/connect/token"), Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request) {
    // ...

    if (request.IsRefreshTokenGrantType()) {
        // Retrieve the claims principal stored in the refresh token.
        var info = await HttpContext.Authentication.GetAuthenticateInfoAsync(
            OpenIdConnectServerDefaults.AuthenticationScheme);

        // Retrieve the user profile and validate the
        // security stamp stored in the refresh token.
        var user = await _signInManager.ValidateSecurityStampAsync(info.Principal);
        if (user == null) {
            return BadRequest(new OpenIdConnectResponse {
                Error = OpenIdConnectConstants.Errors.InvalidGrant,
                ErrorDescription = "The refresh token is no longer valid."
            });
        }

        // Ensure the user is still allowed to sign in.
        if (!await _signInManager.CanSignInAsync(user)) {
            return BadRequest(new OpenIdConnectResponse {
                Error = OpenIdConnectConstants.Errors.InvalidGrant,
                ErrorDescription = "The user is no longer allowed to sign in."
            });
        }

        // Create a new authentication ticket, but reuse the properties stored
        // in the refresh token, including the scopes originally granted.
        var ticket = await CreateTicketAsync(request, user, info.Properties);

        return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
    }

    // ...
}

Alternatively, you can also use OpenIddictTokenManager to revoke all the refresh tokens associated with a user:

foreach (var token in await manager.FindBySubjectAsync("[userid]", cancellationToken)) {
    await manager.RevokeAsync(token, cancellationToken);
}
Kévin Chalet
  • 39,509
  • 7
  • 121
  • 131
  • It' resolved... But my code is little different, and I want to understand why... You have mentioned the `request.IsRefreshTokenGrantType()` but i put my code into `request.IsPasswordGrantType()` using `OpenIddictTokenManager`, because I think I have to clean all tokens when a user does a new login. It's correct, or I'm doing it in the wrong way? – Jedi31 Jan 30 '17 at 23:47
  • The first snippet is for the case where you don't revoke the refresh tokens but consider them as invalid because the security stamp changed. To achieve what you want, the second snippet can be indeed placed in the password token handling code. – Kévin Chalet Jan 31 '17 at 00:09