After my previous Token Based Authentication post I’ve received many requests to add OAuth Refresh Tokens to the OAuth Resource Owner Password Credentials flow which I’m currently using in the previous tutorial. To be honest adding support for refresh tokens adds a noticeable level of complexity to your Authorization Server. As well most of the available resources on the net don’t provide the full picture of how to implement this by introducing clients nor how to persist the refresh tokens into database.
So I’ve decided to write a detailed post with live demo application which resumes what we’ve built in the previous posts, so I recommend you to read part 1 at least to follow along with this post. This detailed post will cover adding Clients, persisting refresh tokens, dynamically configuring refresh tokens expiry dates, and revoking refresh tokens.
- Token Based Authentication using ASP.NET Web API 2, Owin, and Identity – Part 1.
- AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity – Part 2.
- ASP.NET Web API 2 external logins with Facebook and Google in AngularJS app – Part 4.
- Decouple OWIN Authorization Server from Resource Server – Part 5.
You can check the demo application, play with the back-end API for learning purposes (http://ngauthenticationapi.azurewebsites.net), and check the source code on Github.
Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin
Before start into the implementation I would like to discuss when and how refresh tokens should be used, and what is the database structure needed to implement a complete solution.
Using Refresh Tokens
The idea of using refresh token is to issue short lived access token at the first place then use the refresh token to obtain new access token and so on, so the user needs to authenticate him self by providing username and password along with client info (we’ll talk about clients later in this post), and if the information provided is valid a response contains a short lived access token is obtained along with long lived refresh token (This is not an access token, it is just identifier to the refresh token). Now once the access token expires we can use the refresh token identifier to try to obtain another short lived access token and so on.
But why we are adding this complexity, why not to issue long lived access tokens from the first place?
In my own opinion there are three main benefits to use refresh tokens which they are:
- Updating access token content: as you know the access tokens are self contained tokens, they contain all the claims (Information) about the authenticated user once they are generated, now if we issue a long lived token (1 month for example) for a user named “Alex” and enrolled him in role “Users” then this information get contained on the token which the Authorization server generated. If you decided later on (2 days after he obtained the token) to add him to the “Admin” role then there is no way to update this information contained in the token generated, you need to ask him to re-authenticate him self again so the Authorization server add this information to this newly generated access token, and this not feasible on most of the cases. You might not be able to reach users who obtained long lived access tokens. So to overcome this issue we need to issue short lived access tokens (30 minutes for example) and use the refresh token to obtain new access token, once you obtain the new access token, the Authorization Server will be able to add new claim for user “Alex” which assigns him to “Admin” role once the new access token being generated.
- Revoking access from authenticated users: Once the user obtains long lived access token he’ll be able to access the server resources as long as his access token is not expired, there is no standard way to revoke access tokens unless the Authorization Server implements custom logic which forces you to store generated access token in database and do database checks with each request. But with refresh tokens, a system admin can revoke access by simply deleting the refresh token identifier from the database so once the system requests new access token using the deleted refresh token, the Authorization Server will reject this request because the refresh token is no longer available (we’ll come into this with more details).
- No need to store or ask for username and password: Using refresh tokens allows you to ask the user for his username and password only one time once he authenticates for the first time, then Authorization Server can issue very long lived refresh token (1 year for example) and the user will stay logged in all this period unless system admin tries to revoke the refresh token. You can think of this as a way to do offline access to server resources, this can be useful if you are building an API which will be consumed by front end application where it is not feasible to keep asking for username/password frequently.
Refresh Tokens and Clients
In order to use refresh tokens we need to bound the refresh token with a Client, a Client means the application the is attempting communicate with the back-end API, so you can think of it as the software which is used to obtain the token. Each Client should have Client Id and Secret, usually we can obtain the Client Id/Secret once we register the application with the back-end API.
The Client Id is a unique public information which identifies your application among other apps using the same back-end API. The client id can be included in the source code of your application, but the client secret must stay confidential so in case we are building JavaScript apps there is no need to include the secret in the source code because there is no straight way to keep this secret confidential on JavaScript application. In this case we’ll be using the client Id only for identifying which client is requesting the refresh token so it can be bound to this client.
In our case I’ve identified clients to two types (JavaScript – Nonconfidential) and (Native-Confidential) which means that for confidential clients we can store the client secret in confidential way (valid for desktop apps, mobile apps, server side web apps) so any request coming from this client asking for access token should include the client id and secret.
Bounding the refresh token to a client is very important, we do not want any refresh token generated from our Authorization Server to be used in another client to obtain access token. Later we’ll see how we will make sure that refresh token is bounded to the same client once it used to generate new access token.
Database structure needed to support OAuth Refresh Tokens
It is obvious that we need to store clients which will communicate with our back-end API in persistent medium, the schema for Clients table will be as the image below:
The Secret column is hashed so anyone has an access to the database will not be able to see the secrets, the Application Type column with value (1) means it is Native – Confidential client which should send the secret once the access token is requested.
The Active column is very useful; if the system admin decided to deactivate this client, so any new requests asking for access token from this deactivated client will be rejected. The Refresh Token Life Time column is used to set when the refresh token (not the access token) will expire in minutes so for the first client it will expire in 10 days, it is nice feature because now you can control the expiry for refresh tokens for each client.
Lastly the Allowed Origin column is used configure CORS and to set “Access-Control-Allow-Origin” on the back-end API. It is only useful for JavaScript applications using XHR requests, so in my case I’ m setting the allowed origin for client id “ngAuthApp” to origin “http://ngauthenticationweb.azurewebsites.net/” and this turned out to be very useful, so if any malicious user obtained my client id from my JavaScript app which is very trivial to do, he will not be able to use this client to build another JavaScript application using the same client id because all preflighted requests will fail and return 405 HTTP status (Method not allowed) All XHR requests coming for his JavaScript app will be from different domain. This is valid for JavaScript application types only, for other application types you can set this to “*”.
Note: For testing the API the secret for client id “consoleApp” is “123@abc”.
Now we need to store the refresh tokens, this is important to facilitate the management for refresh tokens, the schema for Refresh Tokens table will be as the image below:
The Id column contains hashed value of the refresh token id, the API consumer will receive and send the plain refresh token Id. the Subject column indicates to which user this refresh token belongs, and the same applied for Client Id column, by having this columns we can revoke the refresh token for a certain user on certain client and keep the other refresh tokens for the same user obtained by different clients available.
The Issued UTC and Expires UTC columns are for displaying purpose only, I’m not building my refresh tokens expiration logic based on these values.
Lastly the Protected Ticket column contains magical signed string which contains a serialized representation for the ticket for specific user, in other words it contains all the claims and ticket properties for this user. The Owin middle-ware will use this string to build the new access token auto-magically (We’ll see how this take place later in this post).
Now I’ll walk you through implementing the refresh tokens, as I stated before you can read the previous post to be able to follow along with me:
Step 1: Add the new Database Entities
Add new folder named “Entities”, inside the folder you need to define 2 classes named “Client” and “RefreshToken”, the definition for classes as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class Client { [Key] public string Id { get; set; } [Required] public string Secret { get; set; } [Required] [MaxLength(100)] public string Name { get; set; } public ApplicationTypes ApplicationType { get; set; } public bool Active { get; set; } public int RefreshTokenLifeTime { get; set; } [MaxLength(100)] public string AllowedOrigin { get; set; } } public class RefreshToken { [Key] public string Id { get; set; } [Required] [MaxLength(50)] public string Subject { get; set; } [Required] [MaxLength(50)] public string ClientId { get; set; } public DateTime IssuedUtc { get; set; } public DateTime ExpiresUtc { get; set; } [Required] public string ProtectedTicket { get; set; } } |
Then we need to add simple Enum which defined the Application Type, so add class named “Enum” inside the “Models” folder as the code below:
1 2 3 4 5 |
public enum ApplicationTypes { JavaScript = 0, NativeConfidential = 1 }; |
To make this entities available on the DbContext, open file “AuthContext” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 |
public class AuthContext : IdentityDbContext<IdentityUser> { public AuthContext() : base("AuthContext") { } public DbSet<Client> Clients { get; set; } public DbSet<RefreshToken> RefreshTokens { get; set; } } |
Step 2: Add new methods to repository class
The methods I’ll add now will add support for manipulating the tables we’ve added, they are self explanatory methods and there is nothing special about them, so open file “AuthRepository” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
public Client FindClient(string clientId) { var client = _ctx.Clients.Find(clientId); return client; } public async Task<bool> AddRefreshToken(RefreshToken token) { var existingToken = _ctx.RefreshTokens.Where(r => r.Subject == token.Subject && r.ClientId == token.ClientId).SingleOrDefault(); if (existingToken != null) { var result = await RemoveRefreshToken(existingToken); } _ctx.RefreshTokens.Add(token); return await _ctx.SaveChangesAsync() > 0; } public async Task<bool> RemoveRefreshToken(string refreshTokenId) { var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId); if (refreshToken != null) { _ctx.RefreshTokens.Remove(refreshToken); return await _ctx.SaveChangesAsync() > 0; } return false; } public async Task<bool> RemoveRefreshToken(RefreshToken refreshToken) { _ctx.RefreshTokens.Remove(refreshToken); return await _ctx.SaveChangesAsync() > 0; } public async Task<RefreshToken> FindRefreshToken(string refreshTokenId) { var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId); return refreshToken; } public List<RefreshToken> GetAllRefreshTokens() { return _ctx.RefreshTokens.ToList(); } |
Step 3: Validating the Client Information
Now we need to implement the logic responsible to validate the client information sent one the application requests an access token or uses a refresh token to obtain new access token, so open file “SimpleAuthorizationServerProvider” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientId = string.Empty; string clientSecret = string.Empty; Client client = null; if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) { context.TryGetFormCredentials(out clientId, out clientSecret); } if (context.ClientId == null) { //Remove the comments from the below line context.SetError, and invalidate context //if you want to force sending clientId/secrects once obtain access tokens. context.Validated(); //context.SetError("invalid_clientId", "ClientId should be sent."); return Task.FromResult<object>(null); } using (AuthRepository _repo = new AuthRepository()) { client = _repo.FindClient(context.ClientId); } if (client == null) { context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId)); return Task.FromResult<object>(null); } if (client.ApplicationType == Models.ApplicationTypes.NativeConfidential) { if (string.IsNullOrWhiteSpace(clientSecret)) { context.SetError("invalid_clientId", "Client secret should be sent."); return Task.FromResult<object>(null); } else { if (client.Secret != Helper.GetHash(clientSecret)) { context.SetError("invalid_clientId", "Client secret is invalid."); return Task.FromResult<object>(null); } } } if (!client.Active) { context.SetError("invalid_clientId", "Client is inactive."); return Task.FromResult<object>(null); } context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin); context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString()); context.Validated(); return Task.FromResult<object>(null); } |
By looking at the code above you will notice that we are doing the following validation steps:
- We are trying to get the Client id and secret from the authorization header using a basic scheme so one way to send the client_id/client_secret is to base64 encode the (client_id:client_secret) and send it in the Authorization header. The other way is to sent the client_id/client_secret as “x-www-form-urlencoded”. In my case I’m supporting the both approaches so client can set those values using any of the two available options.
- We are checking if the consumer didn’t set client information at all, so if you want to enforce setting the client id always then you need to invalidate the context. In my case I’m allowing to send requests without client id for the sake of keeping old post and demo working correctly.
- After we receive the client id we need to check our database if the client is already registered with our back-end API, if it is not registered we’ll invalidate the context and reject the request.
- If the client is registered we need to check his application type, so if it was “JavaScript – Non Confidential” client we’ll not check or ask for the secret. If it is Native – Confidential app then the client secret is mandatory and it will be validated against the secret stored in the database.
- Then we’ll check if the client is active, if it is not the case then we’ll invalidate the request.
- Lastly we need to store the client allowed origin and refresh token life time value on the Owin context so it will be available once we generate the refresh token and set its expiry life time.
- If all is valid we mark the context as valid context which means that client check has passed and the code flow can proceed to the next step.
Step 4: Validating the Resource Owner Credentials
Now we need to modify the method “GrantResourceOwnerCredentials” to validate that resource owner username/password is correct and bound the client id to the access token generated, so open file “SimpleAuthorizationServerProvider” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin"); if (allowedOrigin == null) allowedOrigin = "*"; context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin }); using (AuthRepository _repo = new AuthRepository()) { IdentityUser user = await _repo.FindUser(context.UserName, context.Password); if (user == null) { context.SetError("invalid_grant", "The user name or password is incorrect."); return; } } var identity = new ClaimsIdentity(context.Options.AuthenticationType); identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); identity.AddClaim(new Claim("sub", context.UserName)); identity.AddClaim(new Claim("role", "user")); var props = new AuthenticationProperties(new Dictionary<string, string> { { "as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId }, { "userName", context.UserName } }); var ticket = new AuthenticationTicket(identity, props); context.Validated(ticket); } public override Task TokenEndpoint(OAuthTokenEndpointContext context) { foreach (KeyValuePair<string, string> property in context.Properties.Dictionary) { context.AdditionalResponseParameters.Add(property.Key, property.Value); } return Task.FromResult<object>(null); } |
By looking at the code above you will notice that we are doing the following:
- Reading the allowed origin value for this client from the Owin context, then we use this value to add the header “Access-Control-Allow-Origin” to Owin context response, by doing this and for any JavaScript application we’ll prevent using the same client id to build another JavaScript application hosted on another domain; because the origin for all requests coming from this app will be from a different domain and the back-end API will return 405 status.
- We’ll check the username/password for the resource owner if it is valid, and if this is the case we’ll generate set of claims for this user along with authentication properties which contains the client id and userName, those properties are needed for the next steps.
- Now the access token will be generated behind the scenes when we call “context.Validated(ticket)”
Step 5: Generating the Refresh Token and Persisting it
Now we need to generate the Refresh Token and Store it in our database inside the table “RefreshTokens”, to do the following we need to add new class named “SimpleRefreshTokenProvider” under folder “Providers” which implements the interface “IAuthenticationTokenProvider”, so add the class and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider { public async Task CreateAsync(AuthenticationTokenCreateContext context) { var clientid = context.Ticket.Properties.Dictionary["as:client_id"]; if (string.IsNullOrEmpty(clientid)) { return; } var refreshTokenId = Guid.NewGuid().ToString("n"); using (AuthRepository _repo = new AuthRepository()) { var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime"); var token = new RefreshToken() { Id = Helper.GetHash(refreshTokenId), ClientId = clientid, Subject = context.Ticket.Identity.Name, IssuedUtc = DateTime.UtcNow, ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime)) }; context.Ticket.Properties.IssuedUtc = token.IssuedUtc; context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc; token.ProtectedTicket = context.SerializeTicket(); var result = await _repo.AddRefreshToken(token); if (result) { context.SetToken(refreshTokenId); } } } } |
As you notice this class implements the interface “IAuthenticationTokenProvider” so we need to add our refresh token generation logic inside method “CreateAsync”, and by looking at the code above we can notice the below:
- We are generating a unique identifier for the refresh token, I’m using Guid here which is enough for this or you can use your own unique string generation algorithm.
- Then we are reading the refresh token life time value from the Owin context where we set this value once we validate the client, this value will be used to determine how long the refresh token will be valid for, this should be in minutes.
- Then we are setting the IssuedUtc, and ExpiresUtc values for the ticket, setting those properties will determine how long the refresh token will be valid for.
- After setting all context properties we are calling method “context.SerializeTicket();” which will be responsible to serialize the ticket content and we’ll be able to store this magical serialized string on the database.
- After this we are building a token record which will be saved in RefreshTokens table, note that I’m checking that the token which will be saved on the database is unique for this Subject (User) and the Client, if it not unique I’ll delete the existing one and store new refresh token. It is better to hash the refresh token identifier before storing it, so if anyone has access to the database he’ll not see the real refresh tokens.
- Lastly we will send back the refresh token id (without hashing it) in the response body.
The “SimpleRefreshTokenProvider” class should be set along with the “OAuthAuthorizationServerOptions”, so open class “Startup” and replace the code used to set “OAuthAuthorizationServerOptions” in method “ConfigureOAuth” with the code below, notice that we are setting the access token life time to a short period now (30 minutes) instead of 24 hours.
1 2 3 4 5 6 7 8 |
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() { AllowInsecureHttp = true, TokenEndpointPath = new PathString("/token"), AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30), Provider = new SimpleAuthorizationServerProvider(), RefreshTokenProvider = new SimpleRefreshTokenProvider() }; |
Once this is done we we can now test obtaining refresh token and storing it in the database, to do so open your favorite REST client and I’ll use PostMan to compose the POST request against the endpoint http://ngauthenticationapi.azurewebsites.net/token the request will be as the image below:
What worth mentioning here that we are setting the client_id parameter in the request body, and once the “/token” end point receives this request it will go through all the validation we’ve implemented in method “ValidateClientAuthentication”, you can check how the validation will take place if we set the client_id to “consoleApp” and how the client_secret is mandatory and should be provided with this request because the application type for this client is “Native-Confidential”.
As well by looking at the response body you will notice that we’ve obtained a “refresh_token” which should be used to obtain new access token (we’ll see this later in this post) this token is bounded to user “Razan” and for Client “ngAuthApp”. Note that the “expires_in” value is related to the access token not the refresh token, this access token will expires in 30 mins.
Step 6: Generating an Access Token using the Refresh Token
Now we need to implement the logic needed once we receive the refresh token so we can generate a new access token, to do so open class “SimpleRefreshTokenProvider” and implement the code below in method “ReceiveAsync”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin"); context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin }); string hashedTokenId = Helper.GetHash(context.Token); using (AuthRepository _repo = new AuthRepository()) { var refreshToken = await _repo.FindRefreshToken(hashedTokenId); if (refreshToken != null ) { //Get protectedTicket from refreshToken class context.DeserializeTicket(refreshToken.ProtectedTicket); var result = await _repo.RemoveRefreshToken(hashedTokenId); } } } |
What we’ve implemented in this method is the below:
- We need to set the “Access-Control-Allow-Origin” header by getting the value from Owin Context, I’ve spent more than 1 hour figuring out why my requests to issue access token using a refresh token returns 405 status code and it turned out that we need to set this header in this method because the method “GrantResourceOwnerCredentials” where we set this header is never get executed once we request access token using refresh tokens (grant_type=refresh_token).
- We get the refresh token id from the request, then hash this id and look for this token using the hashed refresh token id in table “RefreshTokens”, if the refresh token is found, we will use the magical signed string which contains a serialized representation for the ticket to build the ticket and identities for the user mapped to this refresh token.
- We’ll remove the existing refresh token from tables “RefreshTokens” because in our logic we are allowing only one refresh token per user and client.
Now the request context contains all the claims stored previously for this user, we need to add logic which allows us to issue new claims or updating existing claims and contain them into the new access token generated before sending it to the user, to do so open class “SimpleAuthorizationServerProvider” and implement method “GrantRefreshToken” using the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context) { var originalClient = context.Ticket.Properties.Dictionary["as:client_id"]; var currentClient = context.ClientId; if (originalClient != currentClient) { context.SetError("invalid_clientId", "Refresh token is issued to a different clientId."); return Task.FromResult<object>(null); } // Change auth ticket for refresh token requests var newIdentity = new ClaimsIdentity(context.Ticket.Identity); newIdentity.AddClaim(new Claim("newClaim", "newValue")); var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties); context.Validated(newTicket); return Task.FromResult<object>(null); } |
What we’ve implement above is simple and can be explained in the points below:
- We are reading the client id value from the original ticket, this is the client id which get stored in the magical signed string, then we compare this client id against the client id sent with the request, if they are different we’ll reject this request because we need to make sure that the refresh token used here is bound to the same client when it was generated.
- We have the chance now to add new claims or remove existing claims, this was not achievable without refresh tokens, then we call “context.Validated(newTicket)” which will generate new access token and return it in the response body.
- Lastly after this method executes successfully, the flow for the code will hit method “CreateAsync” in class “SimpleRefreshTokenProvider” and a new refresh token is generated and returned in the response along with the new access token.
To test this out we need to issue HTTP POST request to the endpoint http://ngauthenticationapi.azurewebsites.net/token the request will be as the image below:
Notice how we set the “grant_type” to “refresh_token” and passed the “refresh_token” value and client id with the request, if all went successfully we’ll receive a new access token and refresh token.
Step 7: Revoking Refresh Tokens
The idea here is simple, all you need to do is to delete the refresh token record from table “RefreshTokens” and once the user tries to request new access token using the deleted refresh token it will fail and he needs to authenticate again using his username/password in order to obtain new access token and refresh token. By having this feature your system admin can have control on how to revoke access from logged in users.
To do this we need to add new controller named “RefreshTokensController” under folder “Controllers” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
[RoutePrefix("api/RefreshTokens")] public class RefreshTokensController : ApiController { private AuthRepository _repo = null; public RefreshTokensController() { _repo = new AuthRepository(); } [Authorize(Users="Admin")] [Route("")] public IHttpActionResult Get() { return Ok(_repo.GetAllRefreshTokens()); } //[Authorize(Users = "Admin")] [AllowAnonymous] [Route("")] public async Task<IHttpActionResult> Delete(string tokenId) { var result = await _repo.RemoveRefreshToken(tokenId); if (result) { return Ok(); } return BadRequest("Token Id does not exist"); } protected override void Dispose(bool disposing) { if (disposing) { _repo.Dispose(); } base.Dispose(disposing); } } |
Nothing special implemented here, we only have 2 actions which lists all the stored refresh tokens on the database, and another method which accepts a query string named “tokeId”, this should contains the hashed value of the refresh token, this method will be used to delete/revoke refresh tokens.
For the sake of the demo, I’ve authorized only user named “Admin” to execute the “Get” method and obtain all refresh tokens, as well I’ve allowed anonymous access for the “Delete” method so you can try to revoke your own refresh tokens, on production you need to secure this end point.
To hash your refresh tokens you have to use the helper class below, so add new class named “Helper” and paste the code below:
1 2 3 4 5 6 7 8 9 10 |
public static string GetHash(string input) { HashAlgorithm hashAlgorithm = new SHA256CryptoServiceProvider(); byte[] byteValue = System.Text.Encoding.UTF8.GetBytes(input); byte[] byteHash = hashAlgorithm.ComputeHash(byteValue); return Convert.ToBase64String(byteHash); } |
So to test this out and revoke a refresh token we need to issue a DELETE request to the following end point “http://ngauthenticationapi.azurewebsites.net/api/refreshtokens“, the request will be as the image below:
Once the refresh token has been removed, if the user tries to use it he will fail obtaining new access token until he authenticate again using username/password.
Step 8: Updating the front-end AngularJS Application
I’ve updated the live front-end application to support using refresh tokens along with Resource Owner Password Credentials flow, so once you log in, and if you want to user refresh tokens you need to check the check box “Use Refresh Tokens” as the image below:
One you log in successfully, you will find new tab named “Refresh Tokens” on the top right corner, this tab will provide you with a view which allows you to obtain a new access token using the refresh token you obtained on log in, the view will look as the image below:
Update (11-08-2014) Thanks to Nikolaj for forking the repo and add support for seamless refresh token requests, you can check it here.
Lastly I’ve added new view named “/tokens” which is accessible only by username “Admin”. This view will list all available refresh tokens along with refresh token issue and expiry date. This view will allow the admin only to revoke a refresh tokens, the view will look as the below image:
I will write another post which shows how I’ve implemented this in the AngularJS application, for now you can check the code on my GitHub repo.
Conclusion
Security is really hard! You need to think about all the ins and outs, this post turned out to be too long and took way longer to compile and write than I expected, but hopefully it will be useful for anyone looking to implement refresh tokens. I would like to hear your feedback and comments if there is something missing or there is something we can enhance on this post.
You can check the demo application, play with the back-end API for learning purposes (http://ngauthenticationapi.azurewebsites.net), and check the source code on Github.
Follow me on Twitter @tjoudeh
References
- Special thanks goes to Dominick Baier for his detailed posts about OAuth, especially this post was very useful. As well I highly recommend checking the Thinktecture.IdentityServer.
- Great post by Andrew Timney on persisting refresh tokens.
Thanks for writing sach a nice post.
on each refresh after the accesstoken is expired. I get new refresh and access token.
I am not understand only access token is expired not refresh token.
then why it genereting new refresh token.
Is it realy required to generate new refresh token on each request.
Please guide me, is there any thing i am doing wrong.
Hi Taiseer,
I am facing problem with calling refresh token. I mean not understanding that when and how to call refresh token from UI (User Interface), Its better if you have any idea.
Thanks in adavance
Sharad,
I would recommend taking a look at Taiseer’s source for his Angular front end. Essentially though what you want to do is detect that your normal post to your server has returned a 401 and if you have a refresh token stored then call the API to re-authenticate using the refresh token. As a user, you shouldn’t really notice any difference apart from now having to re-enter your username / password each time your token expires.
To help with this I have just used the following interceptor module which detects this, allows you to re-authenticate and then continues with the requests.
https://github.com/witoldsz/angular-http-auth
Greg
This is a great resource, it has helped me out immensely. I do have an issue now though, I have tried to implement the GrantRefreshToken but it never get’s invoked. My ValidateClientAuthentication method is slightly different to yours because it is async and instead of return Task.FromResult(null); I use return; (like in GrantResourceOwnerCredentials). But when I try to use the refresh_token to authenticate I get an invalid_grant error message.
I have posted my problem on stackoverflow: http://stackoverflow.com/questions/38992799/grant-type-refresh-token-invalid-grant
I would be grateful if you could help.
I believe the problem is caused by the expiration time of the refresh token. In the original code by Taiseer, the expiration is set to 0 by default (or simply never changed). You have to update this in your own configuration file. Once you update it with a correct time value (aka >0), it should work. That solved the problem for me. Hope it works for you as well.
Expired refresh tokens are not handled here. You are able to generate new tokens from an expired one. Thats an security issue.
Code helps to understand behavior and flow. BUT the most important, you are in the middle of an infinite loop( refresh-token based)
Hi @Danny,
it exactly happens with me, do you have any clue for that.
What’s the difference between a refresh token and remember me options?
In a SPA you can think or remember me option as creating a refresh token and store it securely in a HTTP cookie so you can use it to obtain new access token when it expires and the user stays logged in.
I am trying to implement refresh tokens in WebApi but am facing problem in Step 2. – I implemented the simple token based authentication without much change in code that is available in asp.net’s basic template.
Now, in step 2 it mentions “AuthRepository” file. I haven’t implemented such a file. Stuck here, really appreciate if you help. Thanks
Hello Shyamal,
The AuthRepository is a class used to encapsulate all the methods needed to access the database, you need just to add the file and paste the code as instructed. Here is the complete implementation of this class.
Hope this helps!
First of all thank you much for providing OAuth in such details. It is working as expected only thing that I would like to ask is, When I try to get access_token using refresh_token, refresh_token also changes. Is there any way to restrict It. I mean I would get access_token without changing refresh_token and once refresh token expires user need to login again with actual credentials.
Hi Joytish,
Yes you can do this, but once the refresh token change, you will benefit from the sliding expiration feature so yo can keep the user signed in as long as he keeps using the app. If you do not want this feature you need to change the code which delete then insert new token with each refresh_token grant.
Could you please share more details where this should be done?
The benefit of not changing the refresh token seems to be that the concept is more resistant to multiple request at the same time. A a typical API is data-driven, so on a page load, multiple calls might get executed (e.g. retrieving some order details from multiple calls/sources).. Since these can be handled at different threads, you are likely to get a race condition on the insert/delete of the refresh tokens, which can lead to undesired results.
Can you give an example of how to do this? Anything I seem to change in the recieve method breaks it. And in the CreateAsync method, the token is always null (I assume because context.DeserializeToken() removes the token from the context)
First, thanks for your detailed explanation.
In the beginning of your article you talked about the “self contained tokens” and that it would be “nice” that one could update the “contained data” if (for example) the user role changed from “user” to “admin”. Therefore you pass an access_token with a short expiration date and a refresh_token with a long expiration date.
I implemented your approach in my server, but the “GrantResourceOwnerCredentials”-Method only got called when I first create the access_token (empty grant_type in the request). When I access the token endpoint with grant_type == refresh_token and pass the refresh_token the “GrantResourceOwnerCredentials”-Method doesn’t get called. Therefore the AccessToken has the same information like in the first request (because of the line “context.DeserializeTicket(refreshToken.ProtectedTicket)”).
Did I misunderstood you or did I do something wrong?
Thanks for your comment. Well, the “GrantResourceOwnerCrdentials” will be called only once when you set the “grant_type” to “Password”. The method “GrantRefreshToken” will be called when you set the grant type to “refresh_token” and you will have the chance to update the access token and set new claim inside it.
Hope this answers your question.
Just a little more detail on Taiseer’s response:
Taiseer’s example uses a copy-constructor to create a new identity. This copy still has all of the original roles.
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
On the next line after the copy-ctor he shows how you can add a new role to that list. Instead of adding a new role you need to remove all the old roles and repopulate with the current roles. Or you can rebuild the ClaimsIdentity like you did in the GrantResourceOwnerCredentials” method.
Good evening. Could you tell me, please, when do we have to add Clients to DB and how to do that correctly? It seems, like it is not done in the code, shown at this post and it does not work without that. Thank you.
Furthermore, I do not understand the situation. It seems like the client field cannot be created withou refresh token and, vice versa, the refresh token cannot be created without client
Your assumption is correct, please read the paragraph titled “Refresh Tokens and Clients” to get a better understanding of the relation between OAuth clients and refresh tokens. The clients are added in the code, check how they added here.
Thank you very much for your answer and for such a great tutorial. Could you, please, answer one more question?
Is it ok, when an unauthorized user tries to view orders page (by entering /#/orders path), he getts the static context of orders.html for a second and only after that getts redirected to the logIn page? I have the same problem both with orders page and with logout button when pressing f5.
Is there any way to fix this problem or that is not problem at all?
Could you please share the database scripts or tables used in this complete article? I’m trying to create some table but that doesn’t help me to develop Web API part.
As this is database first approach so I don’t want to create and imaginary database.
Hello Dharm,
I’m using Code First approach so you can run the example at your end and get the DB script needed, unfortunately I do not have any scripts to generate the tables.
thanks for all of this.
1 question
how can i test with consoleApp?
is this support client credentials grant?
I’m had a problem when a put the application in a host with load balance, when i’m trying to refresh the token and my request go into a server that is not the same who gave to me the access-key, it returns to me “invalid grant”, only works if the load-balance gives to me the same server. Did you know how solve this?
Hi Lucas, check this post, should understand what happening in your case.
I am getting this error
{
“error”: “invalid_clientId”,
“error_description”: “Client secret should be sent.”
}
when I tried running the app for the first time after all this code and supplied these values
grant_type password
username Taiseer
password SuperPass
client_id ngAuthApp
and what values should be there in the Clients and RefreshToken table in the database.
Hope you will reply this time……………..
The error is obvious pass the client secret 🙂
For anyone facing the same mistake, please add client_secret as the last line.
userName:a@a.a
Password:test01!
grant_type:password
client_id:consoleApp
client_secret:123@abc
{
“error”: “invalid_clientId”,
“error_description”: “Client ‘consoleApp’ is not registered in the system.”
}
I’m getting this error and No data is stored in tables of Clients & RefreshTokens. Pls help
Hey Man
This is because you need to run Enable-Migrations and then Update-Database . This will update DB and create clients .
Great Article! However I had a question. You are using Local Storage to store the credentials which will remain there until the user logs out. I need to implement Remember Me feature which means will user will be logged out if he has not selected “Remember me” and will remain logged in as long “Remember me” is enabled. Since your implementation does not use cookies, how can this be done ?
I think you have already answered my question here “In a SPA you can think or remember me option as creating a refresh token and store it securely in a HTTP cookie so I you can use it to obtain new access token when it expires and the user stays logged in.”
If we store refresh token in cookie along with local storage, how will we sync between the two when the cookie expires ? Can you elaborate on this ?
First of all great article !! I need to make make a small change in the implementation like I need to extent expiration of the token if the request is within the expiration window. Which mean If I have generated my token for 15 minutes and my request is going on the 10th minute, I want to extent it to another 15 minutes (without changing the token if possible) from the last request has been made.
Hi Paradeep, this is called sliding expiration and you can not do it with access tokens, once you issue an access token you can not update.
Please help! I have followed your code and implemented it in my application. For testing purposes I set the token expiry and refresh expiry to 1 minute.
I have Authorize attribute set over the protected controllers.
I am accessing the claims principal in the constructor of the API controller like below
[Authorize]
public class OrdersController : ApiController
{
public OrdersController()
{
var principal = Thread.CurrentPrincipal as ClaimsPrincipal;
var claims =principal .Claims;
//The below line throws an error since claims is null
var role = claims.First(c => c.Type == “Role”).Value;
}
}
Now if I keep application idle for some time and then try to access the Orders Controller, the authorize check passes but the claims object is null which causes an exception. I also found out that none of Refresh token provider or Authorization Server provider methods are being called. Please help me.
Great Article! i am new in web api i am confused about client id and Client secret what is a client id and where can i get .
Please check the Seed method where I generate the ClientId, I have described the use for ClientId in the post clearly.
Hello, how could I refresh the token automatically so that the user is not prompted to push “Refresh token” buttons or such stuff?
Good question, please check the forks from my GitHub code as there is a solution for your concern.
ok I found the solution
var _responseError = function (rejection) {
/*
if (rejection.status === 401) {
var authService = $injector.get(‘authService’);
var authData = localStorageService.get(‘authorizationData’);
if (authData) {
if (authData.useRefreshTokens) {
$location.path(‘/refresh’);
return $q.reject(rejection);
}
}
authService.logOut();
$location.path(‘/login’);
}
return $q.reject(rejection);
*/
var deferred = $q.defer();
if (rejection.status === 401) {
var authService = $injector.get(‘authService’);
authService.refreshToken().then(function (response) {
_retryHttpRequest(rejection.config, deferred);
}, function () {
authService.logOut();
$location.path(‘/login’);
deferred.reject(rejection);
});
} else {
deferred.reject(rejection);
}
return deferred.promise;
}
var _retryHttpRequest = function (config, deferred) {
$http = $http || $injector.get(‘$http’);
$http(config).then(function (response) {
deferred.resolve(response);
}, function (response) {
deferred.reject(response);
});
}
What’s the purpose of the “Client Secret” on anything other than the Auth Server? Wouldn’t the Client ID be enough for the server to know who is asking for Access/Refresh Tokens, as well as knowing what that client’s secret is to properly sign tokens? I always though a shared secret was only needed by a client if it were signing it’s own requests/tokens/etc, which to me does not seem to be the case with JWTs where clients only reuse valid tokens that have already been signed by the Auth Server when making requests.
I’m sorry, I was doing research on JWTs and Refresh Tokens and came across your article. I thought it was about JWTs as well but that might not be the case so my question might not even apply here.
Ok, another question. Assume we use this whole mechanism for a mobile app. What happens if a user owns two different devices? When he logs in at the second device, the refresh_token of the first gets invalidated. So, when the first client tries to refresh the access token using this invalid refresh_token, he gets “invalid grant”. Any ideas how to overcome this?
Thank you!
Correct, in this case, you need to store a device identifier in the refresh tokens table, I made this in a commercial project and all went as expected.
Hi, my question is also related with same.
we have mobile application which uses Twitter Digits which gives OTP for user and generate client id for that user, now I want to create Web API which will use client Id and want to generate bearer and refresh token to authenticate that user with client id , how can I achieve this? where I can define grants ? for every user there is unique client id so in my case client id is associated with user not application.guide me on this.
Hi Taiseer.
Sorry to bother you with this question, but I have your solution implemented and working perfectly, except I need now to get the device identifier in order to store different refresh token from different devices of the same user. When you made this in your commercial project you pass them over the context (AuthenticationTokenCreateContext and AuthenticationTokenReceiveContext)? And you had no problem on getting the IMEI (if this is what you get to identify the device) on Android and iOS ? Thanks.
One thing that always confuses me is the client secret value. Is that basically how a password functions for users, but only for authorized clients/applications? How can it be valuable if it can be both required (Confidential Apps) and not required (Javascript Apps) when authorizing?
Hello Geo,
You should not use the client secret on non-confidential client, you just use the client Id, to identify the client for the server and obtain redirect URIs, scopes etc, from the redirect URI the agent (web browser) will redirect the resource owner (user) to a page hosted at the AuthZ server where he will provide username and password to authenticate him.
I hope this answers your concern.
Great article, best resource I found. Question, is it a better approach to keep clientAuth table for login and clientProfile table separated or as the same table? This is something I found missing in the article.
For me, keeping separated sounds better as I can separate auth from the rest of the application. However, that also means paying for two tables (instead of one) and having duplicated fields that will need sync.
In my case, client can log through phone number or facebook/google, so I would need that on client auth to confirm during login (e.g. phone, fbId, googId). After login, you can call “/me” to get full profile at any point. If we separate that would mean that phone number update would need to update auth and profile, for example.
Hello Bruno,
Please note that client is not the end user, so be aware not to confuse yourself with this.
Hi Taisser,
I am still wondering why to use access tokens.
Can we achieve the same functionality if we set the access token for short time, let’s say 30 min?
If we do this, does it mean user needs to enter his credentials again and login, therefore gets a new access token, so there is no need for access token.
I am still on the fance what the way of calculating this 30 mins is? Does it count in the background user inactivity or it starts from user’s login. If the answer is the second option, does it mean user will be redirected to the login page during his activity on the page?
Thanks
Why to use refresh token, and there is no need to use refresh token..
mehmedju
Refresh token is useful for sibling time, meaning the period of time when the user is inactive. If you dont use refresh token, the original access_token is valid and it is considered an security issue handle long-live access-token.
On the other hand, you are right. If access_token is expired, you ought to redirect to login page to re-enter credentials and so on.
By using refresh-tokens, this happens behind the scenes.
Regards,
Hi Taiseer,
I just finished this part of your tutorial. Awesome!
By the way: When updating my local angular client by copy-pasting some code from Github, I noticed a small bug in the auth service. You only send the clientId if refresh tokens are used. But in my undertanding the clientId is always needed for authentication.
Thank you again for this great tutorial series.
Hello Taiseer,
I need to void access to the resource service if the client does not take action for a reasonable amount of time. I think I should void the refresh token when the resource service rejects the access token because it has expired, would this be so ?
Hello Dante,
You are correct, You have to send the expired access token to the resource server to get validated, you will get 401, then you will try to obtain a new access token using the stored refresh token, if it expired too then you will get another 401 and you are ready to prompt the user to provide his/her username/password again. Now If you do not want to send the expired access token you need to set some sort of timer at the client and base your checking on it, I do not recommend this approach unless there is a real need for this.
Hi Taiseer Joudeh,
Great post, helped me a lot.
When I request a new access token via the grant_type refresh_token, where sits the code to check if the refresh token is validated and checked if the refresh token has not been expired?
Kind regards,
Bart Kusters
Hi Brat,
Apologies for the late reply, you need to set breakpoint in this LOC
Hope this helps.
Hi Taiseer,
I checked your LOC, but could not find any line where we are validating refresh token’s expiry. Is is checked automatically? I think there must be our external logic to validate it.
Thanks,
Sachin
Amazing post, I’ve consumed Oauth before, but I never implemented from scratch, this is incredibly useful, like a cookie recipe, I scafold my web api with this and I don’t have to worry about the security leveraging between my SPA, mobile apps and my api. Thanks a lot.
Hi Taiseer. Let’s say I want to ask the user every 5 minutes if he is still there. In a SPA a modal would show at 4 min 30 seconds asking if he is still there. If user doesnt respond he is redirected to login page. If user clicks “yes – I am still here” the app posts to token endpoint using refresh token issued at login. Should I set access token and refresh token both to 5 minutes or should refresh token be set to something bigger, so we dont see anything strange happening with refresh token expiring before user is able to refresh? The reason we need to refresh every 5 minutes is because the SPA will be accessed on mobile devices that are shared between users, so we want to trigger/alert users login with their own creds.
Hi Taiseer,
I’m wondering if same thing can be applied to JWT Tokens.
I’ve finished implementing login/logout based on Your blog posts, but now I’m wondering how to do auto logout after 10 minutes of inactivity and reset that timer on each request, both on server and client. I know I can add timer on client-side, but I’d like to do similar thing on server-side. Idea is to have page that has timer on top that will count from 10 minutes down, if it hits zero user will be auto logged out. When user does request that timer is reset again to 10 minutes. User can click on that timer, request is done in background and again that timer is set to 10 minutes.
I was thinking about refresh tokens, but maybe You could suggest something better/easier?
Thanks
Hi Taiseer and thank you for your great job!
I followed your tutos and it works like a charm! And most important thing is that I’ve understood everything!
Altough, I have a question:
I have such case :
User is logged in and is working on the app.
Access token has expired, so the next query will failed and the refresh token will re-authenticate the user.
Great, but is there a way which allows to automatically re-execute the last query which has failed?
I see one way but which is really bad : send a “ping” query before every query and the real query only in the callback.
Otherwise I would inform the user that his session has been extended, but I find that having such message every hour for example is not so nice too.
Any ideas or advices?
(Sorry if my post is duplicated)
Hello Pat, thanks for your message, well what you are trying to achieve is to silently refresh your access token using the refresh token, and I think you can do this using different approaches:
1- When receiving 401, you need manually fire the refresh token flow and obtain a new access token, you can re-execute your original request using the new access token, if you are not using Angular this link will give you a hint
2- Using the library “oidc-client” which uses a hidden iframe to do this.
Hope one will work for you, I recommend checking the second solution
I noticed that the refresh token get’s deleted and a new one is created when you request a new access token via grant_type=refresh_token.
Am I doing something wrong, or is this an expected behavior?
How should I implement “log in as” functionality using jwt?
Will the access token contain the identity of the impersonated user?
That is a good question, well I’m not aware of something built-in to support this, so you might create it by your self and encode the impersonated user identifier in the access token.
Hi Taiseer,
Thank you for your great post.
I followed these three posts and seems great approach. The only concern I have right now is, If the user log in using browser1 and then the same user log in again using browser2, The first refresh token generated and stored in database and browser1 will be deleted from database once the same user log in using browser2. As a result, in the browser1 user will not be able to get new access token using its refresh token. Since it has been deleted from database.
Thanks for helping me figure it out.
Hi Taiseer,
Do you have any idea regarding this issue?
Thanks
How would I implement it with JQuery AJAX request? Will everything stay the same except the Client script which will be JQuery instead of Angular?
Yup, you need to send xhr requests and everything should remain the same.
How do I get the Identity Claims information client side from the access token?
i.e. “newClaim”, “newValue”
I worked through your series using JWT’s and can decode them at https://jwt.io/ but am now implementing refresh tokens. Maybe I need to implement JWTs into this project?
Any help and direction is appreciated and thanks for taking the time to write this awesome series.
Hello Michael,
You need to use one of the libraries which allow you to decode JWT contents in client side, this is an example (https://github.com/auth0/jwt-decode) Refresh tokens will never store any claim in, it is only a reference for an actual token/claims in your DB.
I have a working Auth Server with JWT tokens. I used another walkthrough. “ASP.NET Identity 2.1 with ASP.NET Web API 2.2.”
In this walkthrough, “Token Based Authentication using ASP.NET Web API 2, Owin, and Identity.” I do not know how to decode the access token.
I have been tasked with adding Refresh Token to my existing Auth Server. In this walk through, the access_token is not a JWT. Or at least doesn’t appear to be. There are no periods in it. How to I decode it? As seen here.
https://bitoftech.net/wp-content/uploads/2014/07/GenerateAccessTokenRequest.jpg
Thanks again!
Same problem here as Michael.
The JWT token can not be decoded using https://jwt.io or Auth0 jwt library. It is not formed in three parts separated by dots.
I’m using their https://github.com/auth0/angular2-jwt in my Nativescript + Angular app, and also using https://github.com/leonardobazico/angular2-jwt-refresh to automate the process of token refreshing,
and I’m getting an error: “Error: JWT must have 3 parts”
Thanks a lot.
Hi, TaIseer, In both Grant* methods replace next code:
var allowedOrigin = context.OwinContext.Get(“as:clientAllowedOrigin”);
if (allowedOrigin == null) allowedOrigin = “*”;
context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new[] { allowedOrigin });
to
var allowedOrigin = context.OwinContext.Get(“as:clientAllowedOrigin”);
if (allowedOrigin == null)
{
allowedOrigin = “*”;
context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new[] { allowedOrigin });
}
Reason to avoid double origin entry in header. In case of double entry the cors preflight request will fail.
Why this code worked for you before – in your example you call app.UseCors after ConfigureOAuth(app)
The order affect this, if the entry is present in a header UserCors will not add it again, but in other order your code will fail always.
I’m wondering how I could issue a refresh token from within the AccountsController once I’ve authenticated with a third party. I’m using this to generate an access token:
private JObject GenerateLocalAccessTokenResponse(string userName)
{
var tokenExpiration = TimeSpan.FromDays(1);
ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim(“role”, “user”));
var props = new AuthenticationProperties()
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
};
var ticket = new AuthenticationTicket(identity, props);
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
JObject tokenResponse = new JObject(
new JProperty(“userName”, userName),
new JProperty(“access_token”, accessToken),
new JProperty(“token_type”, “bearer”),
new JProperty(“expires_in”, tokenExpiration.TotalSeconds.ToString()),
new JProperty(“.issued”, ticket.Properties.IssuedUtc.ToString()),
new JProperty(“.expires”, ticket.Properties.ExpiresUtc.ToString())
);
return tokenResponse;
}
But how would I go about getting a refresh token?
Thanks.
Hello Faraji,
Based on the tutorial I built I didn’t use refresh tokens with external identities, it doesn’t make sense to do this when you are logged in to your FB account. Assume that you have changed your FB credentials, then if you are using refresh tokens you will remain logged in while your FB account is not valid. That was my concern when writing this tutorial and thinking of refresh tokens with external accounts.
Hi Taiseer,
Thank you very much for this blog post, it helps me alot!
I have two questions:
1) What values are stored within the AccessTokens?
Are these the value of the context.Ticket?
Which are serialized via context.SerializeTicket and deserialized via context.DeserializeTicket?
2) The diffierent lifetimes of the
a) RefreshToken and
b) AccessToken.
Following your code, I see only one place where a time value is getting updated: That is within SimpleRefreshTokenProvider.CreateAsync, and if I get it right, that ist the lifetime of the AccessToken, not the RefeshToken. I miss somehow where the different lifetimes gets created.
The „as:clientRefreshTokenLifeTime“ value is set during the ValidateClientAuthentication. That value is used within SimpleRefreshTokenProvider.CreateAsync to update „a Token“.
My understandig of your code is:
a) Within SimpleRefreshTokenProvider.CreateAsync you create the AccessToken via the context.Ticket properties and set the refresh token (a simple value without any contained lifetime values) via context.SetToken.
So the client get the AccessToken and RefreshToken. The AccessToken gets stored as ProtectedTicket.
b) Within Step 6: The old AccessToken gets loaded via the RefreshToken and that gets updated via the next call to CreateAsync.
So basically the AccessToken gets stored, read and updated. The RefreshToken is a simple identify to the old AccessToken value and gets created everytime within the SimpleRefreshTokenProvider.CreateAsync.
I get it. The AuthoToken lifetime is defined within the OAuthAuthorizationServerOptions.
During “SimpleRefreshTokenProvider.CreateAsync” the lifetime gets update and you store a snapshot of the current Ticket. The AuthToken is liftime get generated later (not handled within the code) by OWIN based on the OAuthAuthorizationServerOptions.AccessTokenExpireTimeSpan.
Thanks again for this blog post!
You are welcome, sorry for the late reply and happy to hear you solved your issue 🙂
http://stackoverflow.com/questions/26969817/generate-token-in-controller
Please refer to the link above.
private JObject GenerateLocalAccessTokenResponse(string userName)
{
}
How do you generate Refresh Token inside Local Function Call eg. in Controller ?
Please advise. Thank you.
I really need this too.
I have a subset of users who want to login with credentials from another system in our organization. I can verify their username/password with this system but it is not OAuth-compatible so I need to generate tokens manually. I have access token generation working fine from previous comments, but I cannot figure out how to generate a refresh token in the controller.
Please help. Thank you.
Hi,
Thank you for this great article. It has been very helpful.
I was just wondering what is the purpose of the ‘Client’ concept. Is it for security reasons ? I guess not since any third party application (except javascript application because of CORS configuration) willing to consume your api could use ‘ngAuthApp’ as client_id. Or did I miss something ?
Thank you for your help.
Hi,
This is nice article for newer ones like me who wants to implement token based authentication. I implement the same in my application. I have a question on validating access_token in my other requests after login. How can i validate this access token in my other request to check its validation or its tempure or not. I am not using any jwt or custom token. I am using default access_token.
Thanks in advance.
Thanks,
Urvish
Hello Taiseer,
First of all, thanks for the wonderful article.
After implemented refresh token functionality I am trying to make my webAPI SSL enabled and that is causing some issues. Thought to run it by you and see if you have any suggestions.
I enabled SSL for webAPI project using properties window and got a HTTPS url (I am running the application from VS 2017 in debug mode). Also set the value AllowInsecureHttp = false.
But when I call the webAPI using HTTPS url from angularJS client, within “SimpleAuthorizationServerProvider.ValidateClientAuthentication” function, the values for both “clientId” and “clientSecret” are always null. If I disable the SSL, everything works fine.
Any suggestions why following line of code is not working when SSL is enabled:
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
Thanks in advance for your help. Looking forward to your reply.
Balinder
Found the issue, authInterceptor was the culprit.
Hi Balinder,
Apologies for the late reply, I applied SSL on many APIs without facing your issue, were you able to fix this issue?
I really like this post. Helped me a lot with my app. I have tried to signout the user using the Request.GetOwinContext().Authentication.SignOut() method, bu this does not seem to work.
Could you shed some light??
Great post! Thank you for sharing. What I didn’t get is how you are able to find the token in the database. In the CreateAsync method, the TokenId is set with a Guid and stored in the database:
var refreshTokenId = Guid.NewGuid().ToString(“n”);
using (AuthRepository _repo = new AuthRepository())
{
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
But in ReceiveAsync you use the entire Token as the TokenId:
string hashedTokenId = Helper.GetHash(context.Token);
using (AuthRepository _repo = new AuthRepository())
{
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
I’m guessing it won’t match any records and ReceiveAsync will end without setting the token int context.
Could you please help me understand this part?
Unable to invoke GrantRefreshToken() method
This File I am using for generating token and refresh token
public class MyAuthorizationServerProvider: OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated(); //
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
if (context.UserName == “admin” && context.Password == “admin”)
{
identity.AddClaim(new Claim(ClaimTypes.Role, “admin”));
identity.AddClaim(new Claim(“username”, “admin”));
identity.AddClaim(new Claim(ClaimTypes.Name, “admin”));
context.Validated(identity);
}
else if (context.UserName == “dmuser” && context.Password == “dmuser”)
{
identity.AddClaim(new Claim(ClaimTypes.Role, “dmuser”));
identity.AddClaim(new Claim(“username”, “dmuser”));
identity.AddClaim(new Claim(ClaimTypes.Name, “dmuser”));
context.Validated(identity);
}
else
{
context.SetError(“invalid_grant”, “Provided username and password is incorrect”);
return;
}
}
public override async Task GrantRefreshToken( OAuthGrantRefreshTokenContext context)
{
// chance to change authentication ticket for refresh token requests
var newId = new ClaimsIdentity(context.Ticket.Identity);
var newTicket = new AuthenticationTicket(newId, context.Ticket.Properties);
context.Validated(newTicket);
}
}
When I am giving request in postman it is giving me unsupported_grant_type error
Hi,
I’m trying to follow your tutorial, but I’m getting context.ClientId = null in SimpleAuthorizationServerProvider class.
My login method looks like this:
self.login = function(loginData) {
var deferred = $q.defer();
$http.post(self.serviceBase + ‘token’,
{
grant_type: ‘password’,
username: loginData.userName,
password: loginData.password,
client_id: ngAuthSettings.clientId
},
{
headers: {
‘Content-Type’: ‘application/x-www-form-urlencoded’
}
}
).then(function (response) {
if (loginData.useRefreshTokens) {
localStorageService.set(‘authorizationData’, { token: response.access_token, userName: loginData.userName, refreshToken: response.refresh_token, useRefreshTokens: true });
}
else {
localStorageService.set(‘authorizationData’, { token: response.access_token, userName: loginData.userName, refreshToken: “”, useRefreshTokens: false });
}
_authentication.isAuth = true;
_authentication.userName = loginData.userName;
_authentication.useRefreshTokens = loginData.useRefreshTokens;
deferred.resolve(response);
}, function(err) {
self.logOut();
deferred.reject(err);
});
return deferred.promise;
};
If I try to POST like you did, by passing
var data = “grant_type=password&username=” + loginData.userName + “&password=” + loginData.password;
if (loginData.useRefreshTokens) {
data = data + “&client_id=” + ngAuthSettings.clientId;
}
then I’m getting the same problem. That of course causes invalid_clientid error on server side. Any ideas what is wrong?
Hello sir ,
Two simple question :
1) If I have a short Live Access Token(for 1 min) and a refresh token ::: If I log off user (deleting refresh token from database and deleting access token from browser) and after logOff successfully , If I use fiddler with my Access token and RefreshToken to invoke some authenticated action method of some Controller before access token gets expired(before 1 min to expire), will it allow access for the user ?
2) As I am creating an application for “Banking Solution” (with No multiple instance for same user) LogOff should work immediately and even after few seconds , user should not allow to access any information from bank server.
Is Token Based Authentication good for Banking solution ? If not , then what is the alternative for any secure project
Need help!!!
Hi
I’m currently looking at Ionic v2 apps and how to build this security.
When you state that Native is confidential and that native can store secrets. Do you not mean that storing a secret in the code is secure? Or, as I would believe, using the device secure store is safe. Having a secret stored in code I believe will make vulnerable to reverse engineering?
https://cordova.apache.org/docs/en/latest/guide/appdev/security/#do-not-assume-that-your-source-code-is-secure
Hi,
we have mobile application which uses Twitter Digits which gives OTP for user and generate client id for that user, now I want to create Web API which will use client Id and want to generate bearer and refresh token to authenticate that user with client id , how can I achieve this? where I can define grants ? for every user there is unique client id so in my case client id is associated with user not application.guide me on this.
Hi Taiseer,
Great article regarding refresh token implementation. Worked without any issues in local environment but whenever moved to load balanced servers, we are getting few issues. Please check them below.
1. We are frequently getting this issue “System.Data.Entity.Infrastructure.DbUpdateConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions. —> System.Data.Entity.Core.OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions” after which refresh token part is not at all working
2. System.Data.Entity.Core.EntityException: An error occurred while starting a transaction on the provider connection. See the inner exception for details. —> System.InvalidOperationException: SqlConnection does not support parallel transactions.
Also, we are getting few more errors related to storage of refresh tokens in db part. Please suggest how to overcome these type of issues. If in worst case, is it really needed to store refresh tokens in a db table? Instead of sending refresh token as guid, I tried sending protected ticket in encrypted format in the token response and decrypted the protected ticket during grant_type=refresh_token. Is it feasible so that we can avoid above type of errors. Please suggest.
Thanks,
Manideep
Ever figure out what was causing this by chance?
The code bombs on the return statement inside public async Task CreateAsync
What should be returned by the CreateAsync method in IAuthenticationTokenProvider? Nothing is currently being returned and the code will not compile as a result.
Hi! Great arcticle! I have a question regarding the method ReceiveAsync. You coded the search simply by looking at the token id. Would it have any added value to search the token by id “AND” by clientId just in case someone is trying values at the refresh token endpoint?
Hello,
This is very great article, how ever can you please give steps to use cookie authentication and refresh tokens both?
because i’m using MVC with WebAPi
appreciate
Hey, I just would like to say that this article (along with the others) have helped me build some pretty cool applications, so thank you for that.
I do have a question though, the Clients table. How do I actually add new clients to that table? Do I have to do it manually? If so, how do I create the hashes?
Hi Taiseer,
Just a small error in the AddRefreshToken method: on the line where you remove the potential existing token, should be : RemoveRefreshToken(existingToken.Id) instead of RemoveRefreshToken(existingToken)
Cheers
Thank you so much, three years later and still helping lots of people.
Saved my life. Even disabled adblock for your site.
Thanks for your great tutorial, i followed from the part, and then i just using my database and tables have been created automatically, When i run the application, always i getting client_id is null. please help me. i don’t what i need to do. Do i have to database tables. when i signup user created after login in i facing client_id null.
When i signup i am not getting any records in RefreshTokens table,
The problem exit of Angular JS Version Change, when i use older version it works perfectly, when i use 1.6.4 and above we facing error.
Thank you,
if it was “JavaScript – Non Confidential” client we’ll not check or ask for the secret
– Why are we not getting the secret again?
– Is it good to include the secret if it is a Javascript Client?
I think I’m having information overload right now and I’ve no sleep yet so I might have missed a few lines here and there.
Btw, this is a very good tutorial and thanks for creating this one.
Hi,
ReceiveAsync is never executed for me, any clue?
Hi Taiseer,
Great post 🙂
But I have one scenario related to refresh Token.
When my refresh Token is called and at the same time other Api’s are also called(in a queue) which contains old token.
At this time I am getting unauthorized.
How do I handle it?
Thanks in advance.
Rohan
Hey, Great article.
I have a simple question. I don’t want to delete and create the refresh token on every request. Where could I prevent this from happening and are there any security considerations in doing this?
Hi Taiseer.
If I set the Access Token with an expiration of 1000 days and I generate one or several Access Tokens and then modify the duration of the Access Tokens to 1 day. Can I block access to that Access Token that it initially generates?
If I share my source code can someone else generate valid Access Token for my application?
I’m sorry for my English.
Thanks
what is the expiration time of the access token and refresh token that you recommend for a banking application?
Is it necessary to use the refresh token? if I use a short-time access token
Again.. awesome article!
Perhaps I missed something but I ran into a hiccup in the delete refresh token endpoint.
The call to _repo.RemoveRefreshToken(tokenId) would fail because the repo couldn’t find the token passed in.
This is because we’re storing a hashed version of the token. I had to change
_repo.RemoveRefreshToken(tokenId) to _repo.RemoveRefreshToken(Helper.GetHash(tokenId))
Aside from that all good. This series has been very helpful.
if i revoke refresh_token, can i access /order with access_token who refresh_token have been deleted?
Thanks
Hello again Taiseer!
I am trying to implement another flow (Authorization Code), and I have to repeat this:
var allowedOrigin = context.OwinContext.Get (“as: clientAllowedOrigin”);
context.OwinContext.Response.Headers.Add (“Access-Control-Allow-Origin”, new [] {allowedOrigin});
I have a question of how to do it. In your example the implementation is different for the authentication by username and password, and by refresh token.
With username and password you do it in SimpleAuthorizationServerProvider.GrantResourceOwnerCredentials. However with RefreshToken you do it in SimpleRefreshTokenProvider.ReceiveAsync (instead of SimpleAuthorizationServerProvider.GrantRefreshToken). Why?
Also, could you tell me why not add the header directly in SimpleAuthorizationServerProvider.ValidateClientAuthentication, instead of saving it in a variable to add it later?
Thanks for your time.
Cheers.
Hi Taiseer, Such an awesome article. It really made my day.
I have an issue. When i tried to hit /Token for the first time with username, password and grant_Type: password, i am getting access_token with expiry details(access_token expiry details). but not refresh token and client id. But behind its working as expected like its getting inserted into DB, able to generate new Token with Refresh token.
The only concern is, in response message, i am not able to see refresh token and client Id.
That would be great if u can help me here.
Thanks a lot.
Hi Taiseer, i have done a mistake there. i was expecting a model in the response which doesnot include refresh token. so the output of /token was not binded properly to the expected model. there was the error and i foxed it.
Anyways, once again thanks a lot for ur wonderful article.
Hi, great article. The only change I made was when granting a RefreshToken. I simply moved the CORS settings from ‘RefreshTokenProvider.ReceiveAsync’ into ‘SimpleAuthorizationServerProvider.GrantRefreshToken’ for consistency with ‘SimpleAuthorizationServerProvider.GrantResourceOwnerCredentials’. Putting them there still works and groups CORS settings in the same class.
Hi, Thanks for sharing this great article.
i have a mysterious problem in production, everything works while user logs in and grants for refresh token in short periods of time like asking for a new refresh token in 30minutes or 1hour periods, but when user tries to refresh_token after a long time like 10 hours , server returns 400 after calling reciveAsync.
I am having a similar issue with the 400 after ReceiveAsync. Did you find a fix for this?
Hello Amin,
I have the same problem as yours can you please share with me how you solved it?
I’m also experiencing this issue in production, has anybody figured out the cause/fix for this yet?
Don’t suppose anyone found the answer? I’m stuck on this too
Awesome article!!! Hope someone can assist me with this, is there a way to force the use of refresh tokens and do the refresh automagically without user interaction while browsing the angular app after they logged in?
Hi Taiseer , can you show me please how to implement this web api great work with access token and refresh token on an Android App (in android studio) using Volley or Retrofit 2 to consume this web service on Login GUI ….
i am trying to figured it ou but , it seems hard to me.
if you can send me some help on my email : walid.zhani@esprit.tn
and thanks in advance.
Hmm, I don’t understand how your “Allowed Origin” actually comes into play. My understanding says that CORS-preflight is not made for your token endpoints (they are POSTs of x-www-form-urlencoded data, which should not be preflighted)
Or maybe something is wrong with this image?
https://bitoftech.net/wp-content/uploads/2014/07/GenerateAccessTokenRequest.jpg
It says content-type application/json but it looks x-www-form-urlencoded . Thats strange isn’t it?
Thank you so much, Really vice article with detailed explanation. I have learnt a lot.
Hi,
Thank you very much for your articles which helped me a lot in understanding oauth and owin. Trying to implement it on our server a question arised :
From what I understand, if a user authenticates twice (from two different devices for instance), a new refresh token will be generated and the first one will be removed and replaced by the new one. Thereby the first refresh token will not be valid anymore. According to your experience, what would be the best way to handle that case ?
Regards.
I don’t find where we set context.ClientId
Hey, thanks for your blog post. It was of great help so far.
However, I’m having the problem that when I supply the access token to my Web API, the Identity.Claims are empty. No matter what I do, no claims are there 🙁
Have you seen this before? Can you advise?
This article helped me a lot to understand how to implement refresh tokens, so thanks for that! However, I think there are 2 misguided points that I don’t agree with (correct me if I’m wrong):
– You shouldn’t rely on CORS to restrict access to your API. The Origin header is easily spoofed (see https://stackoverflow.com/questions/21058183/whats-to-stop-malicious-code-from-spoofing-the-origin-header-to-exploit-cors) and CORS is only designed to protect users from javascript injection on websites within their browser. A hacker could simply copy the domain the original javascript was talking to and put it in the origin header of his malicious requests manually, so it offers no security, contrary to what you said. This defeats the whole purpose of client authentication.
– You don’t seem to check the expiry of the refresh token in your ReceiveAsync method. From my tests it simply never expires as webapi doesn’t know how and won’t to check against your DB RefreshToken expiry.
Apart from that, very clear and helpful guide.
Hi Taiseer,
When implementing Refreshtoken code. I am getting below error.
Invalid column name ‘Discriminator’.
public async Task FindUser(string userName, string password)
{
ApplicationUser user = await _userManager.FindAsync(userName, password);
return user;
}
public class ApplicationUser : IdentityUser
{
}
Since it said Column not found, i added the column Discriminator in aspnetusers table.
If i add, then the _userManager.FindAsync(userName, password) return null.
user returned is null
thank you for sharing great information. but can you please explain me about this point. when creating token , its id is from hashed id which is generated from Guid, but when refreshing token the id is getting from context.token, so that the previous token is not found out. thank you in advance
var refreshTokenId = Guid.NewGuid().ToString(“n”);
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
};
string hashedTokenId = Helper.GetHash(context.Token);
Very clear and nice tutorial
Thank you very much
Thanks for the great tutorial. I’ve faced a problem while calling DeserializeTicket in ReceiveAsync method which give a result of context.Ticket is null. And also cause the “GrantRefreshToken” not called, also end user get the invalid_grant error with 400 BadRequest.
The root cause for this is that while protecting the ticket the engine use the machine key to serialize the ticket. Then in this case the machine key must be fixed in web.config else the ticket could be serialized using a specific machine key and while the deserializing process a different machine key is used which cause the invalid_grant response. Hope this helps someone.
Thank you! I’ve been suck with this for a while now and it’s stopped me from going crazy!
I needed to add this after deserializing in the ReceiveAsync:
if (context.Ticket != null)
{
context.SetTicket(context.Ticket);
}
Thank you so much, detailed explanation.
Thank you for great article. could you please help for the understanding of ProtectedTicket. I am node js developer
.
Hi Taiseer,
I’m not sure if you’re still replying to this post. First of all, I thank you in advance.
Why do I have to recreate the refresh token every time? If I try to reuse the same refreshtoken more than once it is not found by the following function: FindRefreshToken
Any idea?
Tullio
public async Task FindRefreshToken(string refreshTokenId)
{
var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId);
return refreshToken;
}
Hi, Thanks for your very well explanation, How can i make my old token invalid after request new token, so user has one token valid limit. ?
Hello Taiseer,
how we can generate new access token if old one expired automatically using refresh token without calling refreshtokens, means it should check if access is access token is expired then use refresh token and automatically generate new access token. how this can be done.
Thanks