23

I'm working with ASP.NET Core Web API application. I'm trying to implement Jwt Token Based Authentication on top of ASP.NET Identity( built in with database tables).

I have implemented all scenarios like register user, login etc but now trying to implement refresh token flow( where access token get expired, client need to get replaced access token using refresh token) . I have seen people are creating new table (refreshToken) to store refresh token so it can be validated with access token and new access and refresh tokens will be generated

https://www.blinkingcaret.com/2018/05/30/refresh-tokens-in-asp-net-core-web-api/

https://www.c-sharpcorner.com/article/handle-refresh-token-using-asp-net-core-2-0-and-json-web-token/

I have created new table(refreshToken) to store refresh token and verify it to generate access token, It works fine but I wanted to see if i can use existing AspNetUserTokens table to handle same scenario. I understand that AspNetUserTokens table is used to confirmation email, forgot password etc.

My question is: if someone has used AspNetUserTokens to store refreshtoken, Please share idea as usermanager class does not expose direct token model(AspNetUserTokens) and not sure if i use IdentityDbContext, what are the pron and cons ? I have implemented IdentityDbContext but i dont see built in class in Microsoft.AspNetCore.Identity to store token in AspNetUserTokens

Would be very grateful for some guidance.

Thank you

d_f
  • 3,735
  • 2
  • 15
  • 25
Ankit Patel
  • 483
  • 1
  • 5
  • 9

1 Answers1

28

I’ll answer your question directly then propose an alternative. You can Remove, Set, Get, and Validate tokens with the AspNetUserTokens table. However, you can probably skip the db and I'll describe that below.

The following methods of the UserManager will generate and store:

await _userManager.RemoveAuthenticationTokenAsync(user, "MyApp", "RefreshToken");
var newRefreshToken = await _userManager.GenerateUserTokenAsync(user, "MyApp", "RefreshToken");
await _userManager.SetAuthenticationTokenAsync(user, "MyApp", "RefreshToken", newRefreshToken);

The following methods of the UserManager will get and validate:

var refreshToken = await _userManager.GetAuthenticationTokenAsync(user, "MyApp", "RefreshToken");
var isValid = await _userManager.VerifyUserTokenAsync(user, "MyApp", "RefreshToken", refreshToken );

You will need to set up a provider like this using the IdentityBuilder in Startup.

identity.AddTokenProvider("MyApp", typeof(DataProtectorTokenProvider<User>)

As an alternative to storing these tokens in the database, you can use the following to invalidate all tokens as needed. You might do this as a part of Logout.

_userManager.UpdateSecurityStampAsync(user);
Der_Meister
  • 4,413
  • 2
  • 38
  • 47
Chris Schoon
  • 1,935
  • 1
  • 15
  • 11
  • 2
    There is something poetic in this. Being a DB guy, I don't know exactly what. :) – Metaphor Dec 07 '18 at 15:57
  • @Chris Schoon could you please tell me what are the last two parameters for RemoveAuthenticationTokenAsync method. I'm using default AddDefaultTokenProviders inside Startup.cs class and I need to clear the code which was generated using GenerateChangePhoneNumberTokenAsync method. I'm using .net core 1.1 – janitheshan Jan 22 '19 at 10:50
  • 1
    Why do we need to call RemoveAuthenticationTokenAsync? – Der_Meister Apr 01 '19 at 10:38
  • 1
    How would you remove the access token tied together with the refresh token or would you just have a low access token expiration date. I'm thinking about extending this table to keep track of an access token linked to a refresh token. – lastlink Apr 18 '19 at 23:07
  • 1
    @lastlink you're more than welcome to do so. Identity is flexible to meet any of your needs... I don't see any problem in doing so. – Mosia Thabo Jan 17 '20 at 23:27
  • Most best practices suggest to have low access token exp date. Check this tutorial https://fullstackmark.com/post/21/user-authentication-and-identity-with-angular-aspnet-core-and-identityserver. The guy has another table "User" that differs from "IdentityUser" table and "RefreshToken" which differs from "SecurityToken" table. In "User" he safe last modified date which points to the new create refresh token data. The relation User->RefreshToken one->many. – Александър К. Feb 28 '20 at 12:49
  • 1
    I'm wondering how do you get userid from token? VerifyToken method expects to receive user object, but you don't have UserId when someone tries to exchange refresh token to access token – Bogdan Mart Sep 11 '20 at 13:54
  • @BogdanMart you should have an expired access token before trying to exchange refresh token. I would suggest to add the userid to a claim in the access token, so even if it is expired, you should be able to get your userId from there. You should put the userId in the 'sub' claim. – Urielzen Dec 01 '20 at 17:36
  • @Urielzen but you don't have to pass access token to ouath/token endpoint along with refresh_token according to oauth2 spec. – Bogdan Mart Dec 01 '20 at 18:02
  • And if you pass it in Authorization header, it would be rejected by auth middleware – Bogdan Mart Dec 01 '20 at 18:02
  • @BogdanMart The way I am handling that scenario is that my client is calling the get new refreshToken endpoint before the accessToken expires, so I am passing the bearer accessToken in the header, get the userId from there and respond with both, new refreshToken, and new accessToken. If the accessToken is already expired by the time the user tries to get a new refreshToken, what I am currently doing is logging out the user and asking them to sign in again. I am not sure if this logic follows oauth2 spec, but this is what was recommended to me in other posts and it works like a charm for me – Urielzen Dec 01 '20 at 19:34
  • I don't get this answer. It says that you can skip storing the token in the database but then the first three blocks of code end up storing the token in the user tokens table in the database? – bit May 04 '21 at 18:04