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.
Hmm. Left an comment earlier, but it didn’t appear up to now.
Will try to repeat:
The current realization has a one to one mapping of refresh token and user. This prevents to successfully use the AuthService from more than one application a time with the same credentials, because each refresh invalidates the refresh token probably already hand out to another instance.
I have made just a few changes to allow for using the refresh token as long as it isn’t expired. By that there are now multiple refresh tokens possible per user and client_id. Every add operation removes outdated refresh tokens. Refresh tokens are reused, if not outdated on every grant_type refresh_token request. Of course they are created from scratch on grant_type password.
I’m not entirely sure, whether this conforms to OAuth2, but I think it matches the intentions of the standard
“from more than one application a time with the same credentials,” — should be read as “from more than one instance of an application”…
Hi, I’ve been following the comments here about a version of the blog with JWT & refresh tokens. I came up with a solution to the Multi browser( or same mobile app on multiple devices:iPhone &iPad) In my implementation we will have resources servers located geographically close to large numbers of user (US, AUS & UK). The Auth server is in US. We use the Client_ID to pass in 3 parts separated by periods, and then for the secret we pass just the client_secret.
I considered adding separate entities to track the devices where the mobile app was installed etc but the solution we came up with solved alot of our problems and kept is simple and I believe secure.
Every Install of the mobile app ( or browser instance) creates an Install GUID that is just a way of identifying it in the refresh token schema. Each Application(SoftwareClient) has a registered Client_ID and Secret in the AUTH server. Each Resource Servers (AKA Audience Server) has its recordID and BaseHash in the AUTH server as well.
So then if MobileAppA wants access to Server001 from iPhoneZ, each of these have a uniqieID so we string them togeather separated by periods. SoftwareClient.AudienceServerID.InstallID
So a real world client_D looks like this.”ios_MyApp_objc.cabc0f1234567890afb99fd6056bb581.DeviceIPad123″
then we pass in the software Clients secret.
The ValidateClientAuthentication method then checks for the 2 periods and parse the results first checking that Client_ID & Secret are correct, then creating a JWT that can be used on the desirable resource server.
The ENTIRE Client_ID that is above is stored as the tokens Client_ID. What this does is that even if a user installs the same mobile app on more than one device or uses the web app from multiple browsers, the last part of the Client_ID make it a unique install. So when any “install” of the app wants a refresh token, its only going to find its and not cross over.
The SoftwareClient then stores this and uses the JWT token for the request to the resource server. When the app requires the auth token to be refreshed it sends the complete client__id.
I do not know if this is inline with Oauth guidelines, but based on my research I could find find a way to have both secure Software Clients as well as Resource servers as the Client_ID gets used for both.
Here is My Code for those interested
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// The Client ID is three Parameters, the SoftwareClientID(period)Audience(resource)serverID(period)InstallID
string clientId = string.Empty;
string clientSecret = string.Empty;
SoftwareClient sClient = null;
ResourceServer rServer = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
context.SetError(“invalid_clientId”, “ClientId should be sent.”);
return Task.FromResult(null);
}
// JJW Support for Both Software clients as Audience/Resource Servers
string softwareClientID = string.Empty;
string resourceServerID = string.Empty;
string originID = string.Empty;
if(context.ClientId.Contains(“.”))
{
string[] arrClientID = context.ClientId.Split(new string[] { “.” }, StringSplitOptions.None);
if (arrClientID.Length == 3)
{
softwareClientID = arrClientID[0];
resourceServerID = arrClientID[1];
originID = arrClientID[2];
}else
{
context.SetError(“invalid_clientId”, string.Format(“Malformed ClientId ‘{0}'”,context.ClientId ));
return Task.FromResult(null);
}
}
else
{
context.SetError(“invalid_clientId”, string.Format(“Malformed ClientId ‘{0}'”, context.ClientId));
return Task.FromResult(null);
}
// The Resource Server
using (ResourceServerStore _repo = new ResourceServerStore())
{
rServer = _repo.FindAudience(resourceServerID);
}
if (rServer == null)
{
context.SetError(“invalid_clientId”, string.Format(“Invalid resource Id ‘{0}'”, resourceServerID));
return Task.FromResult(null);
}
// The software client
using (AuthRepository _repo = new AuthRepository())
{
sClient = _repo.FindClient(softwareClientID);
}
if (sClient == null)
{
context.SetError(“invalid_clientId”, string.Format(“Client ‘{0}’ is not registered in the system.”, softwareClientID));
return Task.FromResult(null);
}
if (sClient.ApplicationType == Models.ApplicationTypes.NativeConfidential)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError(“invalid_clientId”, “Client secret should be sent.”);
return Task.FromResult(null);
}
else
{
if (sClient.Secret != Helper.GetHash(clientSecret))
{
context.SetError(“invalid_clientId”, “Client secret is invalid.”);
return Task.FromResult(null);
}
}
}
if (!sClient.Active)
{
context.SetError(“invalid_clientId”, “Client is inactive.”);
return Task.FromResult(null);
}
context.OwinContext.Set(“as:clientAllowedOrigin”, sClient.AllowedOrigin);
context.OwinContext.Set(“as:clientRefreshTokenLifeTime”, sClient.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult(null);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
// The validity of the client_id and Client Password is checked before this code runs.
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(null);
}
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
// remove the existing role so we can update it.
string roleName = “”;
var oldRoleClaim = newIdentity.Claims.Where(c => c.Type == ClaimTypes.Role).FirstOrDefault();
if (oldRoleClaim != null)
{
newIdentity.RemoveClaim(oldRoleClaim);
}
// Get the Users Role From the Database
using (AuthRepository _repo = new AuthRepository())
{
ApplicationUser user = _repo.FindByUsername(context.Ticket.Identity.Name);
if (user == null)
{
context.SetError(“invalid_grant”, “Username Error”);
}
// not a fan of this code…
IdentityUserRole topmostRole = user.Roles.OrderByDescending(r => r.RoleId).FirstOrDefault();
IdentityRole tmRole = _repo.FindRoleById(topmostRole.RoleId);
roleName = tmRole.Name;
}
newIdentity.AddClaim(new Claim(ClaimTypes.Role, roleName));
// END of role update.
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult(null);
}
Hope that helps someone.
Thanks for sharing this, I will review the code soon and will get back to you if there is any comments.
A question concerning adding the Access-Control-Allow-Origin header in the GrantResourceOwnerCredentials specific for each client:
Prior to adding this bit of code, I already had my Web.config configured to set the Access-Control-Allow-Origin in the section because it seemed to be needed for preflight requests to use HTTP PUT. Upon adding the client-specific origin code in, I found that it lead to duplicate headers. I’m using a custom policy resolver to account for both OWIN and Web-API calls requiring CORS configuration using the code below:
tokenCorsPolicy.Origins.Add(“*”);
var corsOptions = new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = request => Task.FromResult(request.Path.ToString().StartsWith(“/token”) ? tokenCorsPolicy : null)
}
};
In retrospect, I would expect this to lead to duplicate headers after I added the Web.config headers since it seems the custom headers are always applied, but the above code only applies to requests to /token, so I would think /token would end up with both. This isn’t the behavior I observed before adding the client-specific allow origin headers though. At any rate, with the above code in place no allow-origin header exists at the point you add the header in GrantResourceOwnerCredentials, but the client receives two headers (“*”, and my client origin URL), so I guess the one from the Web.config gets tacked on afterwards.
When I replace my custom code with the normal _appBuilder.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll), I’m seeing the value from Web.config already in the headers once I get to GrantResourceOwnerCredientials which causes an exception once you add it again.
I’m having trouble figuring out what the difference is and how to configure the app to accommodate the /token endpoint CORS needs and Web-API CORS needs and the header needed for HTTP PUT preflight requests.
Hi,
“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.”
I don’t really catch the logic of an refresh token, which automatically expires, if it is used in order to get a new access token. This is, what your app does: If you get a new access token using the refresh token, the refresh token automatically invalidates and cannot be used a second time. What about all the comments regarding a “long life time” of a refresh token if it lives exactly as long as an access token lives?
I understand the security concerns implied with a refresh token, which is valid for a year or so (it would need to be stored on the client, difficult to hide in JS), but in your realization the refresh token just adds extra complexity and doesn’t give a bonus in security and operability. Furthermore it doesn’t work at all, if you have one and the same web app open in two or more browser windows: Each of the instances invalidates the refresh tokens of the other instance.
However, btw, your article series gives an excellent starter package to dive into the stuff. It helps a lot. Thanks for sharing it.
Hi Neil,
I believe you didn’t read the post thoroughly and you are giving assumptions for well written post that have been used successfully by the community.
Well the refresh token identifier is deleted once it is used and new one (new refresh token identifier) is generated in the response, so the consumer app needs to store the new refresh token identifier and present it again to the “token” endpoint using grant “refresh_token” and so on. So by doing this you will can keep the user logged in for long time and the refresh token expire keeps moving as long as the user uses the application (Sliding expiration). You can stop deleting the refresh token and keep using the same one, and let’s assume that refresh token expiry is after 1 week, so the user will be forced to login again by providing his username/password when the refresh token expires, there is no sliding expiration in this case.
Hope this makes sense now.
Hi Taiseer,
well, you say it. You believe. I have given you the credits for your great work, at least in my second comment, so no need to get emotional on a design flaw, honestly. I wasn’t indenting to offend you.
In fact I not only have read your great postings, I also have rolled a working implementation using this. And by doing that I found a problem. I will try to explain it again.
All you said is correct, this is how your code works. And it will work if one and the same user is using it from one instance of a web browser (let’s concentrate on the “ngAuth” client). Maybe this is special to my app, but I really have users, who open several browser instances/windows using one and the same credentials. I don’t think this is a very uncommon use case (think of all you Google tabs you have open at a time). In plain old forms authentication this is not a problem: Each session would have it’s own authentication/session cookie. It is also not a problem, if I would use JWT token authentication only, w/o using refresh tokens: Each instance would have to get its access tokens and those would be completely independent from each other. The dependencies now appear by introducing a refresh token, which is held on the server keyed by username and client_id. To make it easy to understand the problem in here think about the following: You open Chrome, surfing your SPA, logging in with your username and with your password and client_id “ngAuth”. You’ll get an access token and a new refresh token. Then you open Safari and do the same. You will get an access token and a new refresh token. You see the point? What happened virtually to the refresh token, held in Chrome now? It is no longer valid because it has been deleted on the server once Safari came up. A subsequent request to get an access token using the stale refresh token would now just lead to “invalid_grant”.
In my first attempts to overcome this I organized for _not_ deleting refresh tokens already hand out, as long as they have not found to be expired: Once a client returned with a refresh_token which was valid he got a new access token from that. Later I found that this is a security whole: Given the fact, that the refresh_token can have a life time of 1 year or so, storing this on a browser storage could compromise the whole security stuff, if the refresh token would be stolen or revealed to others on whatever way. For exactly that year a thief could generate access tokens w/o being asked once for username/password.
So I returned to your OTP realization of a refresh token: Once used invalidated and deleted. This would open the (possible) compromise window just for the life time of the access token. And I combined it with a variation of the solution, which was proposed yesterday by Jeremy Wesley: Having a client_id, which not only describes a group of users (“ngAuth”) but a specific user now. I’m doing it by concatenating the fixed part of the user_id (“ngAuth”) with a GUID, which is produced by the client, separated by a colon.
By that I have a different client Id from each using client, even though one and the same user would work from two different browsers as outlined above. The colon gives the server the chance to retrieve the client details required to generate a new access token. Even though a client generates a different GUID on next startup, the algorithm would work, because used refresh tokens are deleted, w/o disturbing other instances and while adding a new refresh token I also remove _any_ expired refresh token from the database.
I hope it is clear now. Again no offense, keep on doing your great job. I bet I wouldn’t have been able to get all the nitty-gritty details of MS way to do the things (which is rather complicated).
And – while we are talking about it: You can get rid of your attempts to add the CORS headers in your code, if you lift up the “.UseCors(…)” line to be done _before_ OAuth is initialized. I think I commented on an SO post, that I also had problems with CORS (I got 401 on an OPTIONS request to a route, which was handled by NancyFx). All this is gone now, because “.UseCors” is one of the first statements now. You will then have a problem with your add CORS header thing in “GrantResourceOwnerCredentials”, because the stack complains, that there is already an appropriate CORS header. You just need to remove it and add yours, then you’re done. And you can drop the line in “ReceiveAsync” too. Not necessary, all done by MS-CORS.
Kind regards
Hi Neil,
Thanks for you comment, no offense at all, but opening multiple tabs from the same browser has no affect on my solution, all tabs share the same localstorage instance so if you obtain an access token by refreshing it, then other tabs will use the same new refresh token identifier.
The issue happens if you use different browsers, if we are not using refresh token then there is no issue at all here, but with refresh token identifier the issue will happen. You can overcome this easily by storing the browser identifier along with refresh token Id and Subject in table Refresh Tokens, and you delete the refresh token based on the 2 values (Subject + Browser).
Regards,
Taiseer
Hi Taiseer,
yes, of course, you are right with your remarks concerning the tabs, the more that I choose the example of using two different browsers 🙂
Your outlined solution matches my current realisation.
Thanks again for your great work and hope to “read” you again.
I ran into the same issue as Neil has pointed out. I think I’ll try some variation on his suggestion. I will say that I initially liked the way that the refresh token use would disable user access from other clients. I’m using this for an internal app and we have a number of shared computers in the business. I liked that it canceled access for the first computer when a user logged into another because it avoided issues caused be users leaving themselves logged into a shared work station.
However, the design issue did come up, especially for our administrative assistant who has chrome open under 3 or 4 users at a time because she has to check multiple email addresses with gmail. Trying to keep track of which chrome instance is logged into the app is nearly impossible, so she ends up logging in a lot.
This is a fantastic foundation to work from, though. The one piece of feedback I would give to improve this article is that it is not very explicit on how to initialize the Client table with a record. You’ve given credentials for your sandbox app, but when I first implemented this system in my app I struggled to add the first client record, needing to refer to multiple outside sources. I’m trying to add another client for a mobile app, and am finding myself having to reverse-engineer the client model again so that I can add a new record.
Taiseer, thank you for the great post!
Neil, your concerns are also my concerns. I wanted to allow users to login from multiple computers. But I also want to be able to properly logout users by revoking refresh_tokens.
As far as I understand the leaking of refresh_tokens is protected by the fact that the token is bounded to the user, so I have only one per user. Neil wanted to support different browsers and Taisser suggested to add a browser column. If I want to support different machines I would have to add a remote_ip column (although that would be the same external ip if I have 2 machines in the same local network, not a real solution actually).
If I use a GUID generated by the client browser, as Neil suggested, I would be compromising security. Am I correct?
It is too late for you, but maybe help to another one. We have solve your approaching with the following changes:
Remove from AuthRepository.cs:
– var existingToken = _ctx.RefreshTokens.Where(r => r.Subject == token.Subject && r.ClientId == token.ClientId).SingleOrDefault();
–
– if (existingToken != null)
– {
– var result = await RemoveRefreshToken(existingToken);
– }
Change in App in AuthService:
var _logOut = function () {
_authentication.isAuth = false;
_authentication.userName = “”;
_authentication.useRefreshTokens = false;
var authData = localStorageService.get(‘authorizationData’);
if (authData.refreshToken) {
localStorageService.remove(‘authorizationData’);
$http.delete(serviceBase + ‘api/refreshtokens/?tokenid=’ + authData.refreshToken).then(function (results) {
});
}
};
By this way, the refreshToken is still working and the remove token is done from Client side, Althoug, it is sure that with the time remove antique tokens will be necessary.
this is a great article and there are a lot of good comments.
I just have 2 stupid questions
1) where can I configure the lifetime of the access token? the default is 30 minutes, I want to set it shorter so I can test the logic to refresh it
2) how do I determine the access token is expired? if I am having a lot of web api operations including get and post, some of them allow anyone to access, some of them allow only registered users. should I (a) always send the access token when calling those operations, if my operation returns access denied, my client should trigger the refresh client code, or (b) always send both access token and refresh token, automatically refresh if the access token is expired? but still, the question is, how do I tell the access token is expired?
Thank you very much!
Hi Hong, please find the response below:
1) you set on OAuthAuthorizationServerOptions.AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(12)
2) You follow approach a, where when you receive 401 you try to obtain new access token using the refresh token (grant_type = refresh_token), if it failed then thats it, user needs to authenticate again!
Hi Taiseer. This is really fantastic. Thanks for making it available.
Quick question about the refresh token format; why use a GUID? Why not just embed some information about the token (issued, expiry, client ID, etc.), as well as a unique ID, as claims in the ticket and send the entire ticket back to the client. Then when the client tries to redeem the refresh token for an access token, you can verify the claims and ensure that the unique ID is still valid in the DB?
Hi Dys,
The OAuth 2.0 specifications for granting refresh tokens states that it should be a string opaque to the client, and you need to store the encrypted ticket on the DB, I do not see any added value of returning the refresh context to the client and make the client life complicated by finding a proper way to store it, so refresh token should always return a unique string.
Hope this answers your question.
Hi Taiseer. Thanks for answering.
So, here’s how I see it:
1) If you return the actual ticket to the client, it will be serialized, encrypted and signed. So it will be opaque to the client, the same as the access token
2) The client needs a mechanism for storing *something*. Either they are storing the string that is the GUID you are returning as a refresh token, or they are storing the (slightly longer) string that is the serialized auth ticket
3) When the token is returned to the server by the client during the request for a new access token, the server can unprotect it, which by definition will only succeed if the token is genuine and hasn’t been altered.
4) In this way, the refresh token works a lot like a bearer token. It can also contain claims, etc. As a bearer token, the refresh token can contain all the state that’s necessary to check its validity. So the only thing you need to store in the DB is some unique identifier that matches the a claim in the token, so you can revoke it when you need to
I’m trying to see why this wouldn’t work.. and it has the added benefit of minimizing the data that needs to be stored in the database..
Hi Taiseer,
Thanks for this article. I have a few basic (maybe lame) questions:-
1.) So once the application makes the first request with username and password and sets grant_type = password and successfully gets the access token and refresh token, is it then the application’s responsibility to make a post to the token uri just about when the access token is about to expire or does one make this request for a new token after every request to the service’s method?
2.) Instead of getting a new token when current access token is about to expire, is there a way to extend the expiry time stamp of the currently used access token?
3.) I have a use case where I want the logged in user to be able to use the website (typical session time being 20 minutes) and its corresponding back end service for as long as they are active. This means if the user doesn’t refresh the page or do any activity that results in a call to the service layer the count down to log out (T-20) keeps on running. In case of page refresh or any activity that results in a service call the timer is reset (back to T-20) How can we achieve this use case?
Thank you for your time Taiseer.
Hi Neville,
No problem, all questions are important so please find my answers below:
1) Correct, the application will be responsible to obtain new access token using the refresh token identifier, usually this can be done when you receive 401 response and you try to obtain new access token using the grant type “refresh_token” if it worked then you use the new access token to access the protected resource.
2) No way to extend the access token expiry date, because the expiry date is encoded within the token it self, so any changes on it will change the token signature.
3) you need to store the last time used value in DB along with some token identifier, and this token identifier can be added as a claim to the access token, then you will create custom authorization header which reads this token identifier from the claim and fetch the last time to use value from DB and compare if it was used in the last 20 minutes, if it wasn’t used then you consider this access token invalid and you return 401. The draw back here that you are doing DB checks with each call to the API.
Hope this answers your question.
regarding point 3, I had a similar solution done in the WCF days and yes it did include a database hit for every service call. (However, an incremental optimization would have been to store this information in a cache provider like Redis instead of a data base. I suppose owin internally does something similar?). I was wondering if there was a way to do this using owin/oauth. Maybe for every service call make a call to the /token endpoint and receive a new auth token and refresh token. But I kind of don’t like that approach too as it makes the whole process tedious, by having to keep track of a new token after every call and remember that for the next call and seems pretty wasteful of tokens too 🙂 having a way to extend expiry date of a token should have been built into the design of generating tokens in my humble opinion.
regarding point 1, wont the login in that case be valid for as long as the refresh token is valid? that sounds like the same as having a longer duration access token, unless I am missing something?
With respect, I think you have a fundamental misunderstanding of how bearer tokens work:
OWIN doesn’t do anything internally about keeping track of access tokens. The tokens are cryptographically encrypted and signed by OWIN using the machine key (by default), which his unique for each machine or each server farm, so if it can decrypt and verify the signature of the token, then it knows that it issued it. This is the whole point of bearer tokens– they leverage cryptography to keep you from having to check a database or state in memory on every call.
There’s really no point of extending an expiry date for an existing token. Cryptographically, this would just look to the client as though you gave it a new token. In fact, that would be the easiest way of doing it– you would just issue a new token on every call. You don’t need to worry about ‘wasting’ tokens.
Thanks for clearing that out Taiseer. So, if I go the route of giving a new token on every call, then that would make the refresh tokens to be of no use, correct? Because I’d be issuing a new access token only when the existing access token is still valid, and once that gets expired, I just show the login screen or disable access until a username /password combination is provided again. No need to store refresh tokens and involve a database.
Anyways, the more I think about it, the more I feel that the security aspect of bearer tokens could be compromised with the loss of SSL. To get around this would you recommend appending the ip address of the client making the request for a token to the machine id string when generating a token and doing the same thing when decrypting it on the resource server using the same machine id. That way the encryption would fail if anyone else got the token because their IP address would be different. This may not work if someone spoofed their IP address though.
Thanks again.
In part 1 of the tutorial you mentioned:
“… in our case we have only one client so we’ll always return that its validated successfully…”
and this is why you just put context.Validated();
in ValidateClientAuthentication()
But here you handle the scenario were you have multiple clients :
1)You added the clients table.
2)You added logic in ValidateClientAuthentication()
I am building a web API for a mobile app, and this mobile app is
the only one consuming the API (i.e. I only have one client), and
following the refresh token tutorial to add support for refresh tokens.
So it seems I can ignore the clients table, the logic in
ValidateClientAuthentication()(keep it like in part 1)
and also ignore any code regarding client Id later in the tutorial.
Is this OK regarding the whole authentication flow, can I omit this parts?
You are going to use the resource owner credential flow along with refresh token in your case, right? This means that you are building a trusted client (mobile application that built by trusted party and you trust that providing username/password to the mobile application will not end in store credentials locally or post them to other harmful service).
Usually you need to authenticate OAuth clients when you use refresh token grant, so thats why there are client Id and client secret and a DB table which contains information about this client (i.e. life time of the refresh token) so if you removed the client authentication you need to hardcode everything in the API and you will be able to use it for another client, so it is your call.
Mmm not sure I am getting this (-:…. (not really sure what I have to hard code here)
Maybe I will add some background to what I am doing.
We are building a mobile app in which you register only with a phone number, whatsapp style (you don’t need to enter or remember any credentials).
You enter your phone.
An SMS is sent with a random number.
Once you enter the number and press “send”, it arravies to the “ask for token” endpoint, and to GrantResourceOwnerCredentials().
There if the random number is correct;
We create a new user for you.
We send you back an access token and refresh token.
Now, if the access token is expired (you get 401 in the mobile app), you send a request with the refresh token and
get a new access token.
Isn’t it enough to present the back end API with a refresh token to get a new access token?
Are you saying we also need to present a secret key which will be baked into the client code (let say stored in a secure key chain in the mobile os).?
Hi Yaron,
Sure you need to present only a valid refresh token identifier to obtain new access token. The data I meant to hard-code is only the expiry date-time of the refresh token. So you can do this without using the table clients, but you will not be able to add new client in the future (i.e. you decide to build another desktop application or JS application that uses the same API used by the mobile app).
Ok , I see what you mean. I think I will ditch the clients table for now. Again, many thanks ! your tutorials are an oasis of knowledge in the confusing and harsh desert that is oAuth/OWIN (-:
Can you provide info on how you initially populated the Clients table? I am unsure how to hash a string with the proper security context so that I could store it in the Secrets column.
Thanks,
Hi, you can check the “seed” function where I populate the table and generate the secret, if it’s not clear let me know please
Figured out how to seed the table. Added mechanism using the Helper class to generate secret key and store the same in the database.
very complete and useful article !
Glad you find it useful 🙂
Hi Taiseer,
Just some noob question.
I don’t completely get how the Clients table is validating where the request is coming from at this point. You mentioned that it is particularly helpful to set the domain where the request is coming from, and I did that on the Clients table. But the login request is still getting the access token, even if I hosted the angularJS application from another domain.
I think there was a skip on the part where you modified the AngularJS application, which would have been good to document here, so that people will have a better understanding of how the clients and refreshtoken play out alongside the front end application.
thanks.
I had to double check this myself, but I think you’ve mis-read the article (like I did). The clients table contains a list of valid clients, there is a seed method that you need to implement that contains the values for the table. Check the source in GIT for the information on how to generate this.
Once the table is populated, your angularJs app will send through the Client_ID which is a constant defined in this example in the app.js. This along with the URI will be cross checked against the clients table.
Hi Taiseer,
I’ve been reading many Identity 2.0 and Owin posts, including MSDN articles.
This is without any doubt the finest, most comprehensive one and easy to follow due to the clear and detailed instructions.
your other Posts regarding Role-Based and Claims is also very thorough.
few questions :
1. Since the Repository pattern alway re-creates the AuthContext and the user manager, can we replace the call
with the use of the OwinContext.Get and OwinContext.GetUserManager, after all we did register those
with the owin context build in lifetime manager (which is per-owin context).
2. suppose I want to use another DI container such as unity in the solution, is it recommended to register the AuthContext
with it as well, or should I only keep “owin stuff” in the app internal lifetime manager ,and my custom classes and services
in my ID container . maybe everything should be using the owin lifetime managers implementations ?
3. for some reason I am getting “error”: “invalid_grant” without any description after sending a refresh_token request.
the ValidateClientAuthentication do gets called without any errors but after that nothing happens and I get the invalid_grant.
what could be the reason for that ?
Thanks again for your great contribution to the community.
Hi Tshaiman,
Happy to hear that posts are useful 🙂
Please find my answers below:
1. Yes you can and this is what I’m already doing in the latest series of posts
2. You can use different IoC containers, but i found that app.CreatePerOwinContext is simple to use.
3. I’m afraid that encryption method and decryption method for IIS are set to Automatic, that why the refresh token fails, please check this post
thanks ! regarding item #3 (the Machine Key generation): will that be a problem for Azure ? what are the settings needed for publishing the Web API as a web site ?
Thanks
+another one, sorry for the inconvenience :
is there a way to revoke the previous ACCESS TOKEN ( not refresh token) ?
since if you call refresh_token, it will invalidate the previous refresh_token but of course the old Access_token if it was not expired, is still valid.
in order to prevent users from multiple login under the same user name, I thought maybe something can be done to revoke the access_token , but could not find any solution to that.
Self contained access tokens are not revocable, once they are issued they will keep valid until they expire, thats why it is recommended to issue short lived access tokens and use longer refresh tokens.
Nop, it will work on Azure and even on Auto-Scale configuration. You can think of this as the same settings needed for load-balancing when you have web farm.
Excellent article and blog overall…
I am working to combine this article with your current JWT auth exercise you are currently working on.
Alas, I too am running into the error:invalid_grant issue when submitting the refresh token (testing of Step 6)
1) could you be more specific about why the encryption/decryption IIS automatic settings would cause the issue? Links to relevant source would be appreciated.
2) is it possible to set the machinekey when using the visual studio local iiS?
3) finally, where is a good resource for the order of when the various Oauth Calls occur? For instance, when does the GrantRefreshToken method call occur?
Thanks again. I look forward to future articles from you. Keep up the great work!
Hi,
regarding question 1, once you generate a refresh token identifier (GUID) and you store the protected ticked in the database, you were using encryption key of value (abc123), then after 2-3 hours you tried to refresh the access token using the refresh token grant but the encryption key value has changed to (xyz456) so once you read the stored protected ticked from DB and the middleware tried to decrypt it, it will fail because the encryption key value has been changed.
question 2, yes you can although I never did this on my tests.
question 3, I cant recall good resource, but the GrantRefreshToken event get invoked after you initiate the request for obtaining an access token using grant type (refresh token) where you have the chance to update existing claims/remove them.
Hope this answers your question.
Hi Taiseer,
First of all, great article. Very complete and very informative. I build upon your article and implemented an entire security infrastructure. But now I am stuck in a problem and was hoping you could help in right direction. Let me set the scenario for you.
I want to implement Logout and Change Password functionality
To implement a secure logout I want to be able to achieve the following:
– Remove access_token on client (Very easy)
– Invalidate/Remove access_token and refresh_token on the server too, so when client logs out, server server should always reject that access_token from then on.
Since there is no built in way to Revoke/Remove access_token, only way is to compare an incoming access_token against some kind of logged out users list and reject (send 401) if access_token is found in that list.
– So I maintain a list of logged out access_tokens in Cache object with expiration time similar as access_token lifetime.
– Then I delete refresh_token from db, (I am only removing refresh_token for which access_token was logged out. There could potentially be other sessions from other devices and browsers).
So far so good, although removing refresh_token wasn’t easy, because you would typically have a action method on some kind of controller called say “logout” or “signout”, so you could simply GET or POST. But within controller action you do not have access to refresh_token or access_token.
So I have to do following hackery around it.
– In OAuthBearerAuthenticationProvider’s RefreshToken method I used following line of code
context.OwinContext.Set(“access_token”, context.Token);
This way I was able to read to access_token later like this
HttpContext.Current.GetOwinContext().Get(“access_token”);
Then within Logout controller action I just add access_token to Cache object. To remove refresh_token it was even worse because refresh_token wasn’t even available via HttpContext.Current.GetOwinContext(), so I had add another claim “as:refresh_token” into token just so I could read claims list from within HttpContext.Current.GetOwinContext() and delete it from db in case of Logout.
Now I want to implement Change password functionality, which means when a user changes their password I want to invalidate all issued access_tokens and refresh_tokens issued to that user.
– Removing refresh_tokens is easy because I already have username and I have all refresh_tokens in a table.
– Removing access_tokens I have no idea how to do that because, access_token isn’t persisted anywhere. This is where I need your help.
My hope was that when I issue access_token I would persist access_token in db as well, and when user changes password I would read all access_tokens and add them into Cache object so they all will become invalid
And so far I haven’t been able to figure out where in the owin pipeline would I be able to read access_token that is about to be issued to client??
Any help will be greatly appriciated.
Thanks
-Abhi
Hi Abhi, you can read the access token before sending it to the client by overriding the event “TokenEndpointResponse”, check my answer here
Awesome!!! Thanks a lot man.
Is there a way to provide deterministic revocation to access tokens? I know you mentioned a refresh token to be deleted from the database so that a new access token cannot be granted, but this doesn’t fit the following scenario of the log out use case:
1. User logs in and gets an access token with 15 minute expiry.
2. User performs tasks and logs out in 5 minutes thereby deleting the refresh token so no new access tokens can be requested. (This will be a task in the Log Out use case)
Now, there is a 10 minute window period where this access token can be used by any unauthorized user. Correct? This seems a bit risky to me in the case of applications involving confidential data, like insurance or banking systems or anything similar where a malicious user could gain access to that token and use it. How do you work around this?
Hi Nbl,
This threat will exist with self contained tokens always, there is no way to revoke them. You need to use reference tokens where they can be revoked. Check Thinktecture identity server for this.
Hi Taiseer. Wonderful tutorials, thanks so much! I have struggled for days with an issue I’m hoping you can shed some light on it. Similar to an earlier reply by Tshaiman, when my refresh token is requested the response I get is “error”: “invalid_grant” and no description. Your response was to generate a MachineKey in the web.config, which I have done using the link and similar to your example project. But in my case I’m not sure that even applies as the Resource and Auth server are the same?
Anyway, the bottom line is in CreateAsync the context.Ticket deserialization does not work…after the call to “context.DeserializeTicket(refreshToken.ProtectedTicket);” the context.Ticket is still null. Interestingly, if I manually deserialize the ProtectedTicket using the Acccess Token’s AccessTokenFormat object, it will deserialize properly. But it does not work using the RefreshTokenFormat object.
var thisWorks = Startup.OAuthOptions.AccessTokenFormat.Unprotect(refreshToken.ProtectedTicket);
var thisDoesnt = Startup.OAuthOptions.RefreshTokenFormat.Unprotect(refreshToken.ProtectedTicket);
Seems like a config problem…but I’ve racked my brains and compared a lot of samples. Any ideas appreciated!
Oops: Where I referenced “CreateAsync” I meant to say “ReceiveAsync”…
Doh. Never mind…found a dumb mistake of course.
Hi,
I’m getting the same error, what did you do to solve the problem?
I have same problem. What mistake you found?
After: context.DeserializeTicket(refreshToken.ProtectedTicket);
I’m add: context.Ticket.Properties.ExpiresUtc = DateTime.MaxValue;
It’s help me.
More info here: http://www.codedisqus.com/CJVjkeUqeg/owin-oauth-20-authorization-server-refresh-token.html
Hi Taiseer Joudeh
Thanks for excellent post. It is very complete and very useful for me.
And I’m waiting the last post : AngularJS Authentication and Authorization with ASP.NET Web API and Identity 2.1 – Part 6
Thanks again.
You are welcome, and I really apologies for not posting it till now, but I’ve been super busy lately 🙁
Hi, great article. My question is, how exactly do you archive sliding expiration with this?
Lets say I want my user to get logged out after one hour of inactivity. Do I have to set the “refresh token” to a life span of 1 hour?
As I said in a previous comment, great post!
The post below is more skeptical about the security implications of OAuth and Javascript (Angular) applications. Basically it suggests to use encrypted cookies for some bits, like the refresh_token and client secret.
http://alexbilbie.com/2014/11/oauth-and-javascript/
Thanks for your comment, I agree with you http only cookie is more secure to store refresh token identifiers.
Hi, i have one question. If the admin remove the tokens then how to let the client know then remove the authentication data and move the user to the login page? currently, your solution work fine but when i delete the client token and on my client browser i click refresh the orders page still, it’s not moving the user to the login page and also the authentication data is still (it should be remove)
Cheers
If you deleted the refresh token this doesn’t mean revoking access token, access tokens are not revocable and you leave them to expire, so once the system tries to refresh the access token using the refresh token grant it will fail to do this and 401 returned. Currently the auto redirection is not specified you can check the comments section as there is another fork from this repo do what you are looking for.
A very helpful article! Nice work
Glad you liked it, thanks for your message!
Hi Taiseer,
Thanks for all your articles in this topic. Chapeau!
I have actually a question : I’m developping a lab project using WebApi-Owin-Jwt, and i came across a situation where the user’s claims changes while the JWT still contains the old claims and I need to update the UI with the new claims . So I’m wondering if it’s possible to combine the refresh token and the JWT in some way?
Hi Bes,
The GrantRefreshToken is the even that should be used to update the claims for the user once you generate new access token from a refresh token grant.
When I add claims to the AspNetUserClaims table they are issued successfully when I login. Any add/update/remove to the rows in that table are not reflected when a new refresh token is issued. If I logout and back in, the claims are correct. Still trying to figure out how these providers all work together, what am I missing on the refresh to force it to get the updated information from the database?
Hi,
You need to update the claims when using refresh token grant, this should be implemented in as this subroutine
Is there any way for this to happen automatically and pull the claims from the AspNetUserClaims table? It happens on login but not sure what I would do for refresh, short of manually querying the table and populating the claims myself.
If you are using the ASP.NET Identity system, then there is helper method which can get the claims for your, check this post.
Hi Taiseer,
Thanks for some great tutorials! Thanks to you I’m finally, slowly, getting to grips with the whole OAuth process!
I have one question though, I’ve implemented JWT tokens, and then I wanted to add refresh tokens as well. Everything works well for the first call and I get an access_token and a refresh_token. But the following call to the token endpoint with my refresh_token fails. I goes all the way into RemoveRefreshtoken, and it gets deleted in the database. But then it just dumps with a 400 error, “the request cannot be fulfilled due to bad syntax”. So it never enters GrantRefreshToken afterwards and issue a new one.
Do you have any clue on where I should start looking for mistakes. I’ve double checked the code and can’t find any obvious errors.
Best regards,
Anders
Hi Andres,
What I’m suspecting that you are sending different client id between the 2 requests, hard to tell where is the issue without seeing the code.
Hi Anders,
I have the same problem, did you solve it?
Best Regards,
Bruno Meletti
Hi Taiseer,
I am new to WebAPI stuff and while I was searching for refresh token on google, I found this wonderful article.
I did get to understand the flow of refresh token. I want to implement this concept of refresh token in my application that will run on load balanced scenario. Will this work in the load balanced environment (say for more than 2 servers) ?
Thanks Rahul
Hi Rahul, sure it will work if you unified the machine key values between the 2 servers. If you do not want to do this, then you can use JWT tokens. Check my other post about JWT tokens
Seems, I cannot refresh token twice. Any idea why that happen?
The steps I’m doing are:
1. Get Token
2. Wait until token expires
3. Refresh token
4. Wait until refresh token expires
5. Trying to refresh a token again returns “Invalid_Grant”
Probably I’m testing it wrong, could you help me to elaborate how to test and so I can be able to refresh token unlimited times?
Thanks in advance
Hi Victor,
If the refresh token expired, then for sure you will not be able to use it to obtain new access token without providing username/password. You need to use a valid (not expired) refresh token to obtain new access token.
I have read your article. First, good work. I have implemented your example in my applciation inclusive the both new tables in model first instead of code first. I can login and signup as always but if I signup, then login and have a look up in my database, both tables Client and RefreshToken are empty. So I miss some code to finish my work here.
Hard to tell what is the issue, sorry about this. And glad to hear that posts are useful.
In the RefreshTokensController Delete method you say the string tokenId parameter coming into the method will be the hashed tokenId which you can then simply call _repository.RemoveRefreshToken(tokenId) to revoke the token. However, the tokenId coming in is NOT hashed. Thus, I have to hash it before calling remove (just like in the SimpleRefreshTokenProvider’s ReceiveAsync method (hash before deleting).
…or am I missing something here?
Yes it will come as plain text, you need to hash it to match what is stored in DB, then your query the DB.
HiTaiseer,
Thanks for a great and useful post.
I’m a newbie to this method and have a few questions about it:
1. Other then enabling to revoke short lived access tokens, are there additional benefits to refresh tokens? For example: what would be the difference security wise between having a one week access token versus a one week refresh token + one hour access token.
2. If the refresh token is bound to specific user, does it mean that if the token is stolen, the userbidentity will not be verified, meaning, is there any confirmation performed once the refresh token + access token are sent to the server that the sender is indeed who he claims to be (ip address for instance) ?
3. Wouldnt it be risky to have a very long refresh token for one year if no validation is done for the sending user? Also, I guess if an attacker gets both tokens from user, the attacker can be verified to recieve the new access tokens as long as the request they send is before the expiration of the refresh token?
Thanks again for all the great information here.
Eran
Hi Eran,
Your questions have been answered already in the comments section. Please check them where you will find clear answers.
I can’t thank you enough for writing this. It’s the only tutorial I could find that made any sense.
Great to hear this Levi.. happy to help!
Hey, just wanted to say thanks so much for putting this all together. It took some tweaking to separate this out to a service-data-site layer implementation using MVC, but I learned a ton both from your examples and from where the examples deviated from what I wanted to accomplish. Great job in presenting a pretty complex topic in an easy-to-understand format. I really appreciate your time in doing this. Thanks!
Thanks Dave for your nice comment, really happy to know that posts were that useful 🙂
Taiseer, I’ve found this is a really,really informative and well-written tutorial – and on a subject where it’s hard to find good information.
I have a question about the refresh token lifetime: I can see nothing in here that enforces this, and in my solution (based on yours) the expiration has no effect. (This is easily tested by setting the RefreshTokenLifeTime for the client to something like 1 minute.) Have I missed something?
Great job on the article and thank you.
Hi Carl, thanks for your comment.
I didn’t get your question, you mean that the refresh token expiration time is not working?
Hi Taiseer, yes that’s exactly what I mean.
I’ve coded an additional check into the ReceiveAsync method of the refresh token provider and it’s not a big deal; but I was just wondering whether the expiration was supposed to happen automatically?
Thanks for taking the time to reply.
Same for me, I cannot see from the code how RefreshToken.ExpiresUtc is validated. Perhaps it should be done in RefreshTokenProvider.ReceiveAsync() or AuthRepository.FindRefreshToken()
Excellent tutorial… I noticed you need this: context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new[] { “*” });
… in your ValidateClientAuthentication method when returning exceptions. Otherwise for instance… when the clientid is null… they get a response that says cross domain requests are not enabled when that is not the actual error. Since the header doesn’t get added until the Grant method which never gets called in such a case the error is misleading and causes confusion. Took me awhile to figure out what was going on, but realized it happened because my clientid wasn’t being passed it and it was never reaching the grant method to add the cors header.
Thanks @Levi , I spent 3 hours figuring out what I was doing wrong !
Happy that you find the answer in the comments 🙂
Thx! You help me very much with your article!
I could finish my implementation of OAuth2 with refresh token using your content as reference.
🙂
Hi, and thanks for this great post!
I have one question, how did you resolve concurrency problems?
For example, if you allow one user (the same user with the same credentials and refresh token) to log in from multiple tabs and browsers there is a possibility for that user to try to use a token that is previously deleted from the database, and therefore in ReceiveAsync method refresh token won’t be found. That results in not found ticket to be serialized and the whole process fails and the user won’t receive a fresh refresh token.
I’ve managed to reproduce this scenario by creating two intervals. One interval to extract data that requires authorization (which I now think that it has no affect to the whole scenario I am trying to explain) and another timeout to refresh the token (which is important).
If you set refresh token interval to be less than the time required for the authentication process to complete, the process will fail because we’ve deleted the refresh token and didn’t have the time to create a new one before the whole process i executed again.
After failing for the first time the process fails every next time when you try to refresh the token because when the process fails for the first time in ReceiveAsync method (refresh token not found), the ticket is not serialized and therefore CreateAsync method is not reached. Because of that the new refresh token is not generated and the user is stuck with the old refresh token which doesn’t exists in the database anymore.
What are your thoughts about this scenario?
Hi Taiseer,
First of all Thanks for this great article. I have not tried to run it though, So my question might be stupid one. You have used Access-Control-Allow_Origin for ngAuthApp. Now nobody should be able to request Refresh token for this client Id from a different domain than what you have mentioned. If that is the case, how could you request for this through Postmen (which I believe is not running in context of mentioned domain)
Hi Balesh,
For any client not using JS and trying to request an access token, then this header is not useful and this client will be able to get the access token, unfortunately you can not authenticate JS clients as you will not be able to store client secret in JS.
Regards,
Taiseer
Hi Taiseer.
So there is absolutely no way to prevent the usage of clientid intercepted from javascript application in non javascript clients ?
Regards
Piotr
That’s right, you might consider using the implicit flow but this without refresh tokens.
If the non-javascript (malicious) client calls the Web API using the access token from a non-javascript application, will the request be denied or granted?
it will work, tokens are like cash, if you have them then you can use them!
Hi Mate,
Just quick question mate. Is it safe or good practice to save context.SerializeTicket(), which you storing in RefreshToken table, on client side?
For example in case of browser I am thinking about storing it as HttpCookie and In mobile app I am going to store it in a way where it cannot be accessed by other application?
Wait for your reply
Hi Taiseer,
First, let me thank you for the indepth information on web-api2 authentication. I was able to get most of my application requirements from your inputs. I have one thing though.
I want to request for refresh token on every service call, can you suggest me the best way to achieve this ?
In my application I have to show a popup message 5 mins before the token expires. Right now I set the token expiry time to 30 mins. The popup message is appearing at 25 mins after the login even when the user is actively using the app I want to extend this 30 mins as long as the user is active on the application,so that the popup appears only on user’s inactivity.
Can you guide me in the right direction as to how I should approach my requirement ?
Thanks in advance.
Hi Surender,
You are looking to implement some sort of sliding expiration access token, what you can do is on the min 25, you have to use your refresh token to get new access token for the user, if the response for this request is 200 and you obtained new access token, then there is no need to show the Pop UI for the user and you use the new access token to access the API, if the response was 400 then this means you were not able to refresh the token and the user needs to provide credentials again, the popup will show here and the user has to re-authenticate. Hope this answers your question.
Thanks for the response Taiseer.
I dont understand the statement “on the min 25 you have to use your refresh to get new access token for the user”. Is it 25 minutes from the login time should I get new access token with refresh token ?
Your response was close to what I wanted. I just want to rephrase my question so that it is more clear.
I wanted to show a popup UI on user’s inactivity of 25 mins i.e 5 mins before the actual expiry of token (assuming 30 mins).The popup will have only ok button. On clicking ok button with in 5 mins of launching (that is before the token expires), the session will be extended by 30 more minutes. And again on 25 mins inactivity the process repeats. If ok button is not pressed for 5 mins the user will be logged out.
What I am doing now is I created a directive which is placed on app.cshtml. This has a timer which checks for the difference of current time and token expiry time and if the expiry time is with in 5 mins it launches a popup. The button on popup will request for new access token with refresh token. This is working fine but my concern is user is prompted with popup ui even when the user is pretty much active on the app. This may not be a good as far as user experience is concerned… One option I am considering is to call refresh token on every service call, that way the token expiry time is refreshed as long as the user is accessing the system. Even this may not solve my problem as user may be just filling a user entry form on the app and not initiate any service call.
I am confused here and I am not sure if the choice of using tokens for authentication may not be right for my requirement or I should not consider this requirement for the token based authentication.
I am sorry if I am asking too much on a forum, but I am literally lost on understanding the whole thing here. May be I should spend some more time understand this whole thing to arrive at a proper solution to my problem.
But any suggestion or feedback is greatly appreciated and will surely save my lot of time.
Thanks once again for patiently answering everyone’s questions.
when u use grantype password u get expiry time with token so before making an authorized ajax call u can check the expiry time and if it is expired make the refresh token call first and then carry on with ur actual ajax call and if u get error in refresh token calls then it means user need to enter credential again.
Sometimes i am getting an
Exception type: InvalidOperationException
Exception message: Sequence contains more than one element
On the line
var existingToken = _ctx.RefreshTokens.SingleOrDefault(r => r.Subject == token.Subject && r.ClientId == token.ClientId);
Could this be related to the possibility that two refresh token requests arrive at the server in a short interval
The method expects to find only a single refresh token record (SingleOrDefault), there should be no way to have 2 refresh tokens per user and client, I’m not sure how you ended up having 2 refresh tokens.
I had this issue also. I then wrapped the code which removes the old refresh token in a transaction and the problem was resolved.
If a user accesses the site from 2 different browsers they could end up with multiple refresh tokens. ie. chrome on the desktop and safari on their phone.
Correct, you can add unique deviceid or browserid to the refresh tokens table to avoid this issue.
Hi Taiseer,
thanks for your awesome article, it helped me a lot.
I’m interested if there is a way to force token expiration on Web API side. I want to change user permissions and after that I want to force specific user to log out so that after next login user can fetch updated permissions (for hidding HTML content, to secure ng routes etc.)
Thanks in advance.
Hi Matija,
There is no direct way to enforce self contained access token expiration or logout, you need to wait them until they expire, that’s why it is recommended to issue short lived access tokens and keep refreshing them. Once you refresh them you have the chance to update the claims.
U can only revoke refresh token not access token and it is done in createasync and receiveasync method
Hi there,
Nice article series.
I’m probably missing something obvious, but instead of implementing refresh tokens can’t we just issue a new access token as an alternative way to implement sliding expiration IF time is not a critical factor for the update/revoke of access & user data ?
So from this article, the reasons to use a refresh token:
1. Updating access token content
2. Revoking access from authenticated users
3. No need to store or ask for username and password
Regarding #1 and #2, in theory, if our application is NOT time critical in regards to these two (I know most are, but just for the sake of argument), can we just re-issue another access token with a short expiration time ? For #3 revoking a short-lived access token will achieve the same.
———————————————
I’ve seen the argument about compromising the access token with the information inside, but I don’t know how an access token will be “more compromisable” than a refresh token if SSL is in place. I know you didn’t mention that but I’m just mentioning it for the sake of topic completion.
———————————————
Also another question about Asp.Net Identity 3.0 (I’m not sure if you can answer it but still):
Obviously the refresh access token is not supported by default in the current version of Asp.Net Identity (2.1). Do you think that will change with 3.0, i.e. will the custom logic from this post will still be required Q1 2016 with Asp.Net 5.0 ?
I’m trying to implement the security of an Asp.Net long term project at the moment and honestly I feel bombarded with new concepts and information. I’ve read your other posts on the matter and I’m really curious do you think most of the interfaces/general security concepts will stay the same in the 3.0 ? I wonder if I can just go with the 2.1 way of doing it and then “migrate” and if it’ll be a huge leap.
Thank you for your time.
Kind Regards,
Kosta
Hi Kosta,
The problem with self contained access tokens that they you can not revoke them once they are issues, as well you need the username/password to issue them, so how are you going to keep issuing new short lived access tokens without storing the username/password somewhere in the client application? When you use the refresh tokens, you will store this in the client application and you can issue new access tokens silently without the need to store your username/password any where.
Regarding ASP.NET Identity 3.0 I do not have comments on it yet, But I have used 2.1 in many solutions and it matches my needs, it is easy to use if you are going to use SQL store with EF.
First Thanks for the series! They are great!
I’m building on your examples and when I’m logging in with the refresh token option, I’m getting the error:
cannot load http://localhost:56065/token. The ‘Access-Control-Allow-Origin’ header has a value ‘http://localhost:32150/’ that is not equal to the supplied origin. Origin ‘http://localhost:32150’ is therefore not allowed access.
Am I missing something? I’ve downloaded your solution for github and the same happens.
Hi Joao,
For this OAuth client you need to set the right value for column “AllowedOrigin” in the “Clients” table in the database so it matches your local port http://localhost:56065/token.
Hi Taiseer.
I’ve inserted in the Client table the same clients you described. 1st Console App 2nd the angular app. The column AllowedOrigin of the ngAuthApp is set to http://localhost:32150/ which is the same of the app. Do I need to also insert a row for the api?
I found what was wrong. Turns out I was putting a “/” at the end of the AllowedOrigin and it wasn’t matching (of course) the origin. So that was that. Thanks!
Thanks for the tutorial
I have a web api that provides a new token with every call.
Is there a way to handle calls to the API
to ensure the next token is not lost if the call is interrupted by the user?
Hi JJ,
Why you are issuing new access token with each call? this is the not the correct implementation and you are not benefiting from the have the access token self containing everything you want and avoid hitting the DB which each call.
I am able to register and hit azure DB with local run of Authentication API using POST Request in chrome extenstion. When I publish Authentication API to web app azure and try to hit new URL and same register, I can only get 500 error. Am I missing something in the Azure web app setup?
I’m not sure, turn off customerrors in Azure web app and check the error generated.
Hello and thanks for the great post. I have implementd everything is this post but used the JWT implementation from your other post. I am unclear how I should configure teh JwtBearerAuthenticationOptions now that the Issuer and Audience are not static?
Because this runs at start up I would assume the values need to come as collections from the database but cannot figure out the API.
Any help would be great!
Hi Paul,
This is something I always wondered how it can be achieved, so I really have no answer about this, Maybe you can drop SO question or you think about using ThinkTecture Identity Server.
I believe I have it working. I updated my CustomJwtFormatProvider’s Protect method to look like so:
public string Protect(AuthenticationTicket data) {
if (data == null) {
throw new ArgumentNullException(nameof(data));
}
var clientid = data.Properties.Dictionary[“as:client_id”];
if (clientid == null) {
throw new InvalidOperationException(“Client Id cannot be null”);
}
var client = _oauthRepository.FindClient(clientid);
//— Hard coding this for now. Will change if we decide to add refresh tokens.
//var issuer = ConfigurationManager.AppSettings[“as:Issuer”]; //Allowed Origin
//var audienceId = ConfigurationManager.AppSettings[“as:AudienceId”]; //Client Id
//var symmetricKeyAsBase64 = ConfigurationManager.AppSettings[“as:AudienceSecret”]; //Secret
//—>
var keyByteArray = TextEncodings.Base64Url.Decode(client.Secret);
var signingKey = new HmacSigningCredentials(keyByteArray);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(client.AllowedOrigin, clientid, data.Identity.Claims, issued.Value.UtcDateTime,
expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
And my ConfigureOAuthTokenConsumption method to look like so:
public void Execute(IAppBuilder app, HttpConfiguration configuration) {
var clients = _oauthRepository.GetAllOauthClients();
//var issuer = ConfigurationManager.AppSettings[“as:Issuer”]; —-> Clients
//string audienceId = ConfigurationManager.AppSettings[“as:AudienceId”];
//byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings[“as:AudienceSecret”]);
var tokenProviders =
clients.Select(x => new SymmetricKeyIssuerSecurityTokenProvider(x.AllowedOrigin, x.Secret)).ToArray();
var options = new JwtBearerAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = clients.Select(x => x.Id).ToArray(),
IssuerSecurityTokenProviders = tokenProviders
};
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(options);
}
My OauthClient class looks like:
public class OauthClient : EntityBase {
//For EF
private OauthClient() {
}
public OauthClient(string name, ApplicationTypes applicationType, int refreshTokenLifeTime, string allowedOrigin) {
Contract.Requires(name.NotNullOrEmpty());
Contract.Requires(refreshTokenLifeTime > 0);
Contract.Requires(allowedOrigin.NotNullOrEmpty());
Id = GuidComb.Generate().ToString(“N”);
Secret = OauthUtils.CreateBase64ClientSecret();
Name = name;
ApplicationType = applicationType;
RefreshTokenLifeTime = refreshTokenLifeTime;
AllowedOrigin = allowedOrigin;
}
public string Secret { get; set; }
public string Name { get; set; }
public ApplicationTypes ApplicationType { get; set; }
public int RefreshTokenLifeTime { get; set; }
public string AllowedOrigin { get; set; }
public static OauthClient Create(
string name,
ApplicationTypes applicationType,
int refreshTokenLifeTime,
string allowedOrigin) {
return new OauthClient(name, applicationType, refreshTokenLifeTime, allowedOrigin);
}
}
The OauthUtils CreateBase64ClientSecret looks like:
public static string CreateBase64ClientSecret(string secret = null) {
secret = secret ?? Membership.GeneratePassword(32, 10);
var bytes = Encoding.UTF8.GetBytes(secret);
return TextEncodings.Base64Url.Encode(bytes);
}
The rest of the code follows the refresh token article you have written. Is there anything you notice that could cause a problem?
Looks good so far, I could not find anything wrong with this implementation.
Hi Taiseer, thanks for a great post on this topic.
I just wanted to confirm the workflow as I am seeing this behavior:
– User is not authenticated and gets redirected to login page
– User logs in successfully, a refresh token is created (along with access token)
– Access token expires after some time (e.g. 30 minutes)
– User tries to access a secure resource (e.g. an [Authorize] method in a Controller)
– User receives a 401
– At this point, I call authService.refreshToken() again. What happens is that the existing refresh token in the database is deleted by SimpleRefreshTokenProvider, and a new one is created. Why is that happening – why can’t we create a new access token and keep the refresh token (since it still isn’t expired)?
By the way, I’m using Nikolaj’s fork to intercept 401 responses and request for token. My implementation might be wrong, so I wanted to confirm this behavior.
Thanks!
Actually, I see that Neil also asked the same question, and your reply explains it – there’s sliding expiration on the refresh token, which can be disabled. This makes sense. Thanks.
You are welcome, and you flow is correct.
Hi Taiseer – I’m having the “invalid_grant” error mentioned a couple of times in the posts above but none of the solutions are working for me. Any other ideas as to why this might be happening?
Hi Ken,
Are you receiving this when you try to refresh a token or when you try to obtain new access token using username/password?
It happens when refreshing the token …
Hi Taiseer,
Not only have you written an incredibly helpful article here, but you are also being incredibly helpful with questions. You’re like the unofficial community Web API OAuth oracle!
Great job, I am in awe 🙂
You are welcome Carl, thanks for your sweet message and always happy to help 🙂
Hi,
Thanks for the awesome post.
I do have one question however. When an expired (or replaced) refresh token is used, is there a way to customise the error?
Currently, i just get “invalid_grant” with no explanation.
Thanks
Great post and great comments. Not only explains the working but helps me think beyond to solve problems based on business needs.
Glad to hear this, thanks for your comment 🙂
Hi … forgive me if this is kind of a bonehead question…
but… how would a ‘client secret’ be hidden from a client-side jscript app?
‘Client secret’s are optional i see… why are they necessary at all?
For instance, assuming https, couldn’t you just assume that the refresh
request was authentic? Is the only reason you’d use the token
is to take advantage of the built in expiration properties?
I saw something on the web indicating that the secret could be stored server
side in a session variable; but unless hashed, the client secret would be
vulnerable to modification once it got passed back to the client, wouldn’t it?
Nice article.
Is the database schema SQL source shown anywhere? Just wondering about the specification of the columns.
Thanks!
This is following code first approach, so Database is created based on the data annotations or how you configure your database model using fluent Apis. You can use Database first approach for sure.
Hi,
In your JWT post you state that the client_id should be a link to a resource server your are requesting authorization for. If you want to implement JWT with a refresh token, how do you send over what client is making the request along with the resource server you want access to, to get this and your JWT approach working together?
Would you extend your implementation of this: https://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/ and have the client_id relate to the client that is requesting the resource server but have that client relate to the resource server in a 1-1 relationship, in some kind of persistent storage?
Thanks
Hi Taiseer, first of all thank you very much for this unbelievable working demo and codebase for all of us!
Question about deleting or revoking refresh tokens. If you delete the refresh token from the database, the client still has access because it looks like it is using local storage as a “remember me” option. So the refresh token is still technically valid and passes authorization, correct? That is what I’m seeing in the code, locally and I have the forked version (although I really don’t know what the 401 fix was or where – maybe you can comment on that two? later).
So, I’m a little confused on the best practice. I want to have a client that has the “option” to “remember me” however I also want to make revocations, immediate or at least have the option.
Can you confirm that what I’m seeing is correct, if you delete the refresh token in DB, it is not immediately revoked until the client removes local storage or expires. This also goes against allowing you to switch user claims on the fly, almost real-time. Can you explain?
thanks
Marty
Hi Marty, glad you found the post is useful.
Well when you delete the refresh token from the database, the user will not be able to obtain new access token based on the refresh token he is storing locally, so he will be asked to provide his username/password again to obtain new access token and refresh token. So the refresh token stored locally is not used to access resource, it is used only to get new access token without asking the user for his credentials.
The access token type used in the post called, self contained access token, there is no way to revoke it after issuing it, you only keep it until it expires, thats why it is recommended to issue short lived access tokens, and keep refreshing them.
Hope this answers your question.
Hi Taiseer
Thanks for your great article and also great comments.
I have written an authentication system in my ASP.NET Web API project, applying all your 5 articles’ serie starting with “Configure ASP.NET Identity with ASP.NET Web API (Accounts Management) – Part 1.”. All work fine. The backends uses PostgreSQL database.
These last couple of weeks, I read and apply this present article to have the refresh/access token feature. Unless I have no Exception when running my application, I cannot get refreshed access token. It appears that after the method “SimpleRefreshTokenProvider.ReceiveAsync(AuthenticationTokenReceiveContext context)” is executed, the last method “CustomOAuthProvider.GrantRefreshToken(OAuthGrantRefreshTokenContext context)” never gets called. I think this leads to the return code 400 BadRequest and the error “Invalid_grant”.
Note that before I get the error “Invalid_grant”, all seems to work OK but I am surprised to see that in refresh_token context, int the ReceiveAsync() method, the refreshToken is identified and deleted from database; As a result, I do not understand how I can get access token after that.
For now after many searches, I have found no idea on this problem. I can authenticate, and get a refreshToken, but cannot get any accessToken.
Please, may I ask if you could help me to find and fix the problem ? All codes are exactly implemented following your posts.
Hi Kelly,
To understand your issue correctly, you are receiving a refresh token and an access token, but when you try to send the refresh token using the “refresh_token” grant, you are receiving 400 and you never receive an access token, right?
If this is the case then you need to make sure that you setting the correct client_id in the context dictionary and as well the refresh token identifier you are sending exesits in the database, all those methods can be debugged, just set a break points and watch what is happening.
Hope this helps.
I believe the problem (“invalid_grant” & ‘GrantRefreshToken’ never gets called”) 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 all as well.
If custom JWT format has been used to protect ticket as described in this post (https://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/) Unprotect method has to be implemented to deserialise ticket and set it in ReceiveAsync method of SimpleRefreshTokenProvider.
I tried different way and did research on the internet to find the way to deserialise ticket but could not succeeded neither find anything.
Finally I tried following way to deserialise and it works correctly.
public AuthenticationTicket Unprotect(string protectedText)
{
var issuer = “the issuer”;
var audience = “the audience”;
var key = Convert.FromBase64String(“the secret key”); // which has been used to protect
var provider = new SymmetricKeyIssuerSecurityTokenProvider(issuer, key);
return new JwtFormat(audience, provider).Unprotect(protectedText);
}
I used JWtFormat class to unprotect ticket, this class is available in Microsoft.Owin.Security.Jwt nuget package library.
Many Thanks Taiseer
You are most welcome, glad to hear it is working now.
Hi, Great post and very helpfull. But as I’m new to this Identity, I have few questions….
1. What if user changes his username and password? There’s no way to restrict him until the refresh token expires. If we write logic to revoke refresh token on change of username and password can’t we do the same to revoke access token?
2. The main question is what if refresh token expires? Do I have to ask for username and password again? If that’s the case how does the mobile apps works? Like google and facebook and twitter never asks for username and password unless you change the password.
Hi,
1. You can revoke refresh token, but you can’t revoke self contained access token. Check this post
2. Yes, you need to ask for username and password, usually the refresh token lifetime is long, 2 weeks, and they use sliding expiration, so if you kept using the app it will keep sliding the expiration of your refresh token. If you didn’t use the app for 2 weeks for example, then it will ask you for username/password.
Hi Taiser, thank you for your articles. I have a simple question. I tried to read the comments also but I didnt understand yet. Why there is a “Use refresh token” check box in Login page. Should the end user know how the token system works or this is only for us to understand the system. Also why an end user clicks the “Refresh Token” button. If these are for us to understand, In a normal web site how these should work. (may be like this: “Use refresh token” check => “Remember me” in Login Page. And when a request comes to server with access token and refres token, if access token is expired, server automatically creates a new access token by using refresh token)
Hi,
This is only for making the readers understand what is happening, the generate refresh token button should be replaced with automatic refresh token without user interfere, I need to look the fork for this code which do this automatic refresh token thing.
Thank you Taiseer
is the refresh token exposed to the Client at the time they Login ? or where should the refresh token be stored ?
Hey Taiseer, first of all, I’d to thank you! Your tutorials are amazing! I’ve learnt a lot from here!
I’m also sharing my implementation of Automatic RefreshToken in AngularJS, hope to help someone who is stuck on it.
There we go:
[code]
(function (angular) {
“use strict”;
angular.module(‘group.intranet.web’)
// I’ve created a Controller to manage the refresh_token
.controller(‘RefreshTokenController’, [‘AppConfig’, ‘ApiService’, ‘$rootScope’, ‘$timeout’, ‘$state’, ‘CacheService’, function(config, api, $rootScope, $timeout, $state, cache){
// Here is the function that will be called from the $timeout
var refresh = function(attempt){
// Here is a call to the api (it’s just an alias to $http.get)
api.refreshToken($rootScope.user.refresh_token)
.then(function(authData){
// Here we update the cache (it’s just an helper to manage the item auth.data in localStorage)
cache.set(‘auth.data’, authData);
$rootScope.user = authData;
}, function(rejection){
// In case of the refresh fails, it could be an connection problem, so, retry it up to 5 times;
// If the HTTP result is 401, so, the acess_token is already expired, so ignore
if (rejection.status !== 401) {
$timeout(function(){
if(attempt && attempt === 5) return;
refresh(attempt ? attempt++ : 1)
}, 1000 * 60); // retry 5 times
}
}
);
};
// Here the magic happens
// Create a var called “timer”
var timer = undefined;
// Watch $rootScope.user in order to schedule a refresh when this vars changes content (probably the user is logged and the var is created or we got the auth.data from localStorage and changed its value)
$rootScope.$watch(‘user’, function(user){
if(!user) return;
// Clear previous schedules
if(timer){
$timeout.cancel(timer);
}
// Get the .expires from auth.data
var expires = new Date(user[‘.expires’]);
var now = new Date();
// Get the schedule time (the diff between expires date and now)
var scheduleTo = expires – now;
var delay = 1000 * 60 * 10; // 10 minutes
// If the schedule time is bigger than a predefined delay, schedule to expires – delay
if(scheduleTo > delay)
scheduleTo = scheduleTo – delay;
// Set timer var and schedule the refresh
timer = $timeout(refresh, scheduleTo);
});
}]);
})(angular);
[/code]
Just it! To use, you just have to import this file to the project and call it in markup:
[code][/code]
Cheers!
Luiz, thanks for sharing this, I will take a look on this soon and let you know if there is a feedback.
In my implementation of auto-refresh, I intercept the auth error that results when the access token is expired and then attempt a refresh. That way I don’t have to deal with timers. I personally dislike timers for this kind of thing, since I feel like I there are more things that may go wrong with it. E.g. If you change the expiration time of the refresh token, you also need to change your code, and in case the timing doesn’t match the expiration of the token, you might end up with a token expiring before your timer gets to request a new refresh token.
My implementation is in this fork:
https://github.com/Excolo/AngularJSAuthentication
It’s quite old (last commit Aug 2014) so It probably isn’t up-to-date with Taiseer’s tutorial series.
Also keep in mind you need to switch the storage of the refresh token in production code. You don’t ever want to store the refresh token in localStorage – or any storage that can be accessed by Javascript. It’s a XSS attack waiting to happen. The only way one should ever store a refresh token, is in a httpOnly+secure cookie – at least in a real project. For a learning project, localStorage is fine, as long as it doesn’t go live.
Isn’t xss a non-issue by using the Access-Control-Allow-Origin header?
I would never base my security on an assumption that XSS is a “non-issue” – I might even go as far as saying that XSS is never a non-issue.
Let’s take the hypothetical case that your web app hasn’t been properly sanitized and I’m able to inject javascript through url parameters. I send an e-mail to your users with a cleverly constructed link, that when clicked will call a webservice of mine with the users refresh token as a payload. The CORS headers wouldn’t prevent me from doing that.
Hi Nikolaj
I’m interested in swtich from localStorage to a httpOnly+secure cookie. Is there any example that you could provide me?, I’ll appreciate a lot your help.
I havn’t got some code ready to show right now, but you basically add code to send a httpOnly+secure cookie (containing the refresh token) in the place where you’re returning the refresh token. Then in the angular code, you strip out the code to store the refresh token in localstorage and have angular send the cookie along with your request. Then you need to add a logout action to kill the cookie containing the refresh token, since you can´no longer just delete it from storage.
Hi Nikolaj
When you switched the RefreshToken to save in the secure/httponly cookie (which makes very good sense to me) does that then mean that the RefreshToken is sent on every request.
Thanks
Darren
It depends. In my case it didn’t, since my regular resource requests just used http, while any request to the /token endpoint requires https and the refresh token cookie has the ‘secure’ flag set, so the browser is only allowed to include it in https request.
Hi Taiseer,
Thanks for some great tutorials; you really helped me out with this and I can’t really thank you enough.
I’m curious as to why you don’t check the Origin header in ValidateClientAuthentication. I’ve added the following else clause to my implementation of your solution:
if (client.ApplicationType == ApplicationTypes.NativeConfidential) {
[Taiseer’s code: unchanged…]
} else if (client.ApplicationType == ApplicationTypes.JavaScript) {
var origin = context.OwinContext.Request.Headers.FirstOrDefault(o => o.Key == “Origin”).Value.FirstOrDefault();
if (client.AllowedOrigin != origin) {
context.SetError(“invalid_clientId”, “Invalid client.”);
return Task.FromResult(null);
}
}
I realise Request Headers aren’t definitive but, in my humble opinion, this strengthens the solution, but, since you haven’t included it, I’m wondering if I’m missing something…
In my case the Auth request will always be a CORS request so I can rely on this header being present if coming from a reputable browser and if it is coming from a non-js solution I want to “enforce” sending client_secret. It won’t stop a committed attempt to circumvent this, but it will tighten more casual problems, I think.
Kind regards,
Colin.
I have now worked through this solution and wrote an OwinMiddleware component that handles CORS (which enables my Auth service to check the AllowedOrigins in the db with ease and keeps that data in one place). OwinMiddleware also means that any WebApi endpoints (only Account for me at the moment) are CORS checked by the same code as the token endpoints.
This middleware allowed me to strip a lot of the header settings in your AuthProviders.
I also overrode the MatchEndpoint() method in your SimpleAuthorizationServerProvider class to handle OPTIONS requests correctly (ie: if the request is a token endpoint or OPTIONS then mark the request as Completed and return).
The code in my previous post above remains but is improved to take care of browsers that don’t meet expectations and only send Referer:
switch (client.ApplicationType) {
case ApplicationTypes.NativeConfidential:
[code from your original if statement]
break;
case ApplicationTypes.JavaScript:
var origin = context.OwinContext.Request.Headers.Get(“Origin”);
if (origin == null) {
var referer = context.OwinContext.Request.Headers.Get(“Referer”);
if (referer != null) origin = new Uri(referer).GetLeftPart(UriPartial.Authority);
}
if (client.AllowedOrigin != origin) {
context.SetError(“invalid_clientId”, “Invalid client.”);
return CompletedTask;
}
break;
default: break;
}
I’m not posting all of my code here because it would be too hard to follow in this format, and the main reason for my reply to my original post here is in case this information gives anyone a steer in the right direction of implementing CORS effectively using the data (AllowedOrigins mostly) that is being collected by your excellent Auth processes. I am intending on making the work I am doing Open Source and once it is complete I will come back here and post a link to the GitHub project.
I’m still interested in any feedback you, or anyone else may have…
Thanks again for all the time you have saved me.
Overriding the MatchEndpoint method was given to me by this SO answer: http://stackoverflow.com/questions/26614771/owin-token-authentication-400-bad-request-on-options-from-browser#answer-28311907
I’m interested in your solution. Please do post a link to your github project here. Thanks!
Will do Nick. I’m a little way off, but once it’s complete I’ll ensure I notify you too.
Colin.
Taiseer,
Thank you for the extensive information and demonstration. I downloaded your demo code and began experimenting to be sure I understood how it worked. In doing so, I found something that did not work as I expected. I must be missing a key part regarding how OAuth, Katana and CORS all interrelate. I thought you were ensuring that refresh tokens could not be re-used from other origins, but the following steps show that I was able to lift a refresh token and re-use it from Fiddler to generate another, valid access token. Please give it a try and let me know if I’m misunderstanding something, or if there is still a part of your demo server that was intentionally left unsecured, or, quite possibly, that I just don’t understand CORS at all. Thanks again.
1. Browse to your demo website, http://ngauthenticationweb.azurewebsites.net/#/home
2. Click Login button
3. Press F12 to view browser’s network debugger
4. Enter user name and password, click ‘Use Refresh Tokens’, and click Login
5. If you get 400, just click Login again
6. Click ‘Refresh Token’ in your UI header bar
7. click Refresh Token button
8. Using Fiddler or PostMan, compose a new POST request as follows
a. Target URL is http://ngauthenticationapi.azurewebsites.net/token
b. Copy headers in the browser’s network debugger from step 7
c. Body is grant_type=refresh_token&refresh_token=&client_id=ngAuthApp, where is the refresh token from the step 7 response
9. Execute or send the new request
10. You should receive new access and refresh tokens
11. Compose a new request to GET http://ngauthenticationapi.azurewebsites.net/api/orders in Fiddler or PostMan using the new access token
12. This should succeed, thereby proving that the refresh token can be used from any origin
Hey Taiseer,
Your article was really helpful. Everything works perfectly.
I’m wondering if it’s possible to set refresh_token in the way that it never expires? Of course I can set expiration to 10 years or so but I don’t like this solution 😉 what about leaving expiration field empty?
Another idea is to recreate refresh_token in database everytime when it expires. What is your idea?
Thanks one more for the articles.
W.
Hi,
Well I’m amazed, 2 year old article which is still current and correct.
Some questions though, as all comments here 🙂 Why do you delete the refresh token when we received an authentication request with a refresh token? A refresh token can be used multiple times to authenticate yourself, right? Why do you remove it in ReceiveAsync.
Yet on the other side, you are not checking in CreateAsync if there already is an refresh token for a user. Logging in twice will generate two refresh tokens. Isn’t it better to check if it exists first and still not expired then creating another one?
Also, I have read this post: http://stackoverflow.com/questions/25739710/how-to-create-refresh-token-with-external-login-provider And kind of did what the answer did. Also for receive. Could you verify me if it is safe to implement it that way? It works pretty well.
Last one: Where is as:clientRefreshTokenLifeTime coming from? Why not pull it from the database?
Thanks!
Hi, and thanks for your message.
For sure you can use the same refresh token without deleting it and obtaining a new one, but If you implemented this you will loose the sliding expiration feature I want to implement here, so with each new refresh token created an new issued at and expires at are set so we can keep sliding it as long as the user is using the application, this way we can the user logged in for ever as long as he uses the application and the refresh token get refreshed within the configured expiration time for it.
Regarding checking if there a valid refresh token, we can do this but as I told you I want a fresh refresh token with each new login, I do not want to look at old refresh tokens.
I need to check the SO question and I drop a comment there soon.
Hi Taiseer,
I just wanted to check with you if you considered using a cache to store the generated token where token is valid for a long period like a week, but the cached server just stores it for a shorter time like an hour or so. Then on every request use a delegating handler implementation on the Web Api to update the expiry period for tokens that are still in the cache and have not yet expired. That way you don’t need to maintain any database tables or generate new tokens or do cleanup on it after the token expires. The same token can be used again and again for a whole week for as long as it was extended every hour for that whole week.
Let me know if you see any challenges in this approach.
Thanks
Hello Taiseer Joudeh,
First I would like to thank for such a great blog’s articles. This is very helpful to new buddies.
I am new in WebAPI, I implemented auth token with refresh token.
Here is code snippet for refresh token provider.
public override void Receive(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
if (context.Ticket.Properties.ExpiresUtc.HasValue && context.Ticket.Properties.ExpiresUtc.Value < DateTime.UtcNow)
{
//Todo: Refresh token expired custom response to user.
}
}
So, If refresh token is expired then I want to give response like
{
"error": "Refresh token is expired"
}
instead of "error": "invalid_grant".
So pls give me some suggestion how to implement it.
You are welcome and I’m happy to hear that posts were useful. I do not have an answer right now, but I will try to look it up for you.
Did you find a solution already for this?
Thank you for reply.
No, still I am looking for this solution.
And one more thing I am also looking solution for custom response if “Auth barer token” is expired.
Please update me if you find any update.
Can you check out this comment?
First a thank-you to Taiseer Joudeh. Your blog is valuable and I learned lot from it. Regarding refresh token, I think it’s better to “outsource” to Azure Active Directory if that’s an option. That’s what I did and allow me to share it here: https://medium.com/@kevinle/securing-nodejs-rest-with-azure-active-directory-95379288a717#.l4rm5z51i
Unlike most articles on Azure Documentation Center, the approach in that article does not require a redirect of login and signup pages to Azure AD login/signup pages. The client app (web or mobile) is responsible for providing its own login/signup pages which can keep user experience high.
The Resource Provider in that article is a NodeJS REST app, but I think it would not be too hard to port it to webapi.
Hi Kevin, thanks for this message, I really didn’t know that we can achieve this with Azure AD. I will take a look on this post and see how we can apply it in ASP.NET Web API. Really appreciate sharing it.
I really enjoyed reading these blogs. Helped me a lot. Thanks!
You are most welcome. Thanks for your message!
Hi Taiseer.
First of all, Thanks for this amazing tutorial.
It’s a huge help for those like me, that are learning about this.
Now, I’m in trouble.
For a reason that I don’t know, my return when I request a token with password is returning the refresh_token instead of access_token data.
The field came with access_token title, but it’s refresh_token guid what returns, and refresh_token field is not avaliable.
What I’m doing wrong???
Thanks for your help!!!
Hi,
I really can’t tell what you have done incorrectly in your code, if you would like create a gist which contains your code and share it with me to troubleshoot it.
Hi!!!!
Here is where my code is hosted!
https://github.com/jedielson/IdentityApiTokenServer
Thanks!!
Hi Taiseer
Firstly many thanks for a great article – very useful in many areas.
I have a query though.
I notice you are storing the ‘RefreshToken’ in LocalStorage.
Do you see this as an exposure as anyone can find this and issue there own refresh request to obtain a valid JWT?
Is this simply in Local Storage for ease of explaining in the article?
Where do you store the RefreshToken in a Production system?
I note from the comments that it is suggested to store the refreshtoken in a secure/httponly cookie.
If it is stored in the cookie as suggested how do you get it into your payload when you wish to do a refresh api call.
Thanks again
Darren
Hi I’ve implemented based on your article.
I had great confusion from your article. Before your article, I’ve implemented like Calling the the Login Action in Common controller using username and password, and I’ll validate the user name and password against the database.
If the user name and password is matched, then I’ll generate the refresh token and will send the token to ui. From ui, I’ll send the token for the subsequent request.
When I implemented this one,
Do I need to call like http://domain/token? I have figured it out this api call from your demo application.
But when i call this, first I am always navigating to ValidateClientAuthentication. Here you are checking like client id and secret should exists already.
and you are checking for the client id.
Where I’ll send only the user name and password, and check the user and generate access token and refresh token.
(Note: I thought after the successfull login we need to insert the data to client table and after that when we need refresh token, we need to update the client table and reissue the refershed token)
Can you elaborate bit about login process about how the process is doing?
Hi Taiseer,
First of all thanks to this awesome article.
I have a question, do we need Client table in case of just single app consuming the Web Api?
In my case I have just one angular app consuming this Web Api so, can I store my Client Id & Secret in the Web Api config itself?
Please advise.
Thanks in advance!
Hi Shan,
No it is not needed, you can store them in a secure way in Web.Config maybe.
Also, one more question, in the future if I plan to extend my app to work in multiple devices (mobile & other devices) then do I need to add all these as Clients (i.e. One entry for Angular & One entry for Mobile)?
Btw, I have read some articles which says client side cookie is better than localstorage to store the tokens. Is that true?
Also, is there any other way to store the client tokens securely?
I have understood from your example.
But I had come across with one doubt.
How to handle this logic in multiple devices?
I’ve logged in one system a and again I’ve logged in another system b with the same user.
Now I want to retain the user logged in. So I’ve tried to get the refresh token from system a. The system gets logged out since system b generated the new refresh token.
How to retain the user log in even from multiple devices.
Hello Jeeva,
You need to include (device id) inorder to allow users to sign in from different devices or browsers.
Thanks for your reply.
Can you please suggest me logic regarding include device id?
I am in the midway of implementation.
I am waiting for your valuable suggestion.
Where is the logic for checking if the refresh token is expired? I see how you set the refresh token expiry in the ticket
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
But, when a request is made for a new access token using the refresh token I dont see how or where the authorization server checks to see if he refresh token is expired. Of course it works, I checked and it does, but I dont see how.
When you send the refresh token to get a new access token you are just sending the guid generated in the CreateAsync method. So, it doesnt look like the refresh token sent contains any information about expiry. So then how does the auth server know when a refresh token is expired.
Further, I’ve copied the code into my own project and when I send an expired refresh token NONE of the method in either of he auth server classes(IAuthenticationTokenProvider implmeentation or OAuthAuthorizationServerProvider derived class, are hit.
Its driving me crazy, where\how is the auth server checking if a refresh token is expired?
Any help is appreciated. I also asked on Stack Overflow, but no one knows.
Hello and thanks alot for the code. You seem to be the go to person regarding implementing an OWIN auth server as the documentation I am able to find is slim. Thank you.
I have implemented everything and its working, but one thing I cannot figure out is…
Where is the code that checks if the refresh token is expired? I dont see how that is done. In fact when I send an expired refresh token NONE of the Auth server code is hit. This implies that the OAuthAuthorizationServerProvider must handle it somewhere, but how? It seems to me when I send a refresh token, in a request for an access token, I am sending nothing more than the GUID created in the CreateAsync method, so then the refresh token actually contains no information about the expiry. The refresh token just points to the row in the refresh token table. However, when I send an expired refresh token NONE of the auth server code is hit. So then how/where is the refresh token expiry determined? Where is the code for that?
It should happen in the method “ReceiveAsync” when you hit this LOC.
Are you sure no code is hit on the server?
hi taiseer
first of all, thanks for this great post (and all other great posts). learned so much from you. But i have only one question. i tried your refresh token solution from anoter host, and get the error:
still getting error
XMLHttpRequest cannot load https://somefancyurl/token. Credentials flag is ‘true’, but the ‘Access-Control-Allow-Credentials’ header is ”. It must be ‘true’ to allow credentials. Origin ‘http://anotherFancyAngularJsUrl’ is therefore not allowed access.
Do you know what this means? sorry for this stupid question 🙂
Hi Andi,
Can you elaborate more on your issue? How do you try to refresh tokens from another host? The endpoint for the refresh token and obtaining an access token should be the same, most probably you didn’t configure CORS on the API correctly.
We foundd the solutio for the problem (i don’t know if this is the correct way but it works at the moment). AngularJS sends everytime a OPTIONS call before the post. It workes everywhere except the refreshtoken endpoint (eg. /token). I had to add this new method:
[code]
public override Task MatchEndpoint(OAuthMatchEndpointContext context)
{
if (context.IsTokenEndpoint && context.Request.Method == “OPTIONS”)
{
var allowedOrigin = context.Request.Headers.Get(“Origin”);
if (allowedOrigin == null) allowedOrigin = “*”;
context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new[] { allowedOrigin });
context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Headers”, new[] { “authorization” });
context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Credentials”, new[] { “true” });
context.RequestCompleted();
return Task.FromResult(0);
}
return base.MatchEndpoint(context);
}
[/code]
Next problem was, that AngularJS doens’t allow in our case *. We had to get the origin and then it works. maybe you know what the problem is with that 😀
It was really helpful. Thank you for you all effort. But I would like to ask a question. I receive exactly the same error when someone post with wrong token or expired one. In my code I want to assign a new token via RefreshTokens whenever I faced with the expired token. But I can not understand the 401 status code is caused by wrong token or expired. Is there any chance that I can make things a bit more clear? I havent thought about it deeply but it might result with that someone can exploit that?
Hi Boran,
If you look at well-established Identity Server, such as ThinkTecture Identity Servers, there is no need to distinguish between expired or invalid refresh token, in both cases it is invalid and the user needs to reauthenticate and should receive 400 status, as well check the standards for OAuth 2.0 here
Thank you very much for your answer. I thought you ve already forgot this post 🙂 Anyway I would like to ask one more thing, actually its a problem more than a question. Any ideas would help me a lot. Here is the scenario;
-User gets the token and refresh token and stores them.
-When token is expired, in my case its 401 unauthorized received, try to get refresh_token.
– It gets the new values and store them.
Everything worked well on debug mode. I tested with postman on my local IIS. But after I published to some hosting company, I get 400 Bad Request when I asked for refresh_token. What happened was when I try to get refresh token as you know it finds the refresh token on “ReceiveAsync” method and delete it. Then It should go on with CreateAsync method to create a new one. Somehow after client asks for refresh_token it deletes but never creates. When I check Refresh_token table I can not see any assigned refresh_token for that client after the request for refresh_token(if you ask for token, it appears on table). Do you know what would be the reason? Maybe Create method is invoked?
Thank you very much again,
Sincereley,
Boran
This is happening if you ask for refresh token after expiration like I said on main scenario.
Things are getting more exciting. Published code sometimes works depends on how much time I wait to send refresh token request after expiration of access_token. If I wait too much it just deletes and never creates again. Thats another clue I found. Starting to put a lot of logs everywhere. IF you come up with an idea please inform me.
Thank you,
Boran
I have the exact same issue as you
Did you get this fixed? I have the same error x.x working in dev but in production its failing.
This is a great example, I can certainly see the value of refresh tokens.
However one gap does seem to be that if I expire the refresh token and the token had a 10 minute life someone can continue to have access.
Could a similar principle be applied to the access token so that each access token can be expired?
e.g. someone logs in from another device, I get an email to warn me and I can immediately suspend this?
Or are there alternative approaches?
Hi Dan,
The expiry time is encoded within the access token, so trying to change the expiry time will invalidate the access token, this is the beauty fo self-contained tokens, you do not need to query the database to obtain any information about the token, everything is contained inside it. So once the token issued you have to wait until it expires.
That means you could be aware of a breach and allowing access for a further X minutes.
I agree it’s useful to be able to use them passively, but adding an appropriate ID and having a “register” of this so that you can reject specific tokens when they try to authorize would be useful in the event you need to lock out access and don;t want to wait 10 minutes to do this.
Thanks again,
I have one question which hunts me since the time I put my mind on token authentication. Pls help me.
The table schema for allowed Origin prevents for entertaining the requests for creating a token in case the client_id is compromised. Then how can we get the token successfully being generated from Postman Client.
Also please help me understanding if some one has my authenticated token(also refresh_token) and client id can he be able to stay connected in an browser tab (to meet the allowed origin constraint.) Exp. User with normal access can fool the system as an higher access level user.
Thanks for this superb article series. I am good till Part 2 and want that much functionality only. But there is a issue, that while consuming the API for login. First time things go well but if the user try to login with the same credential on first login click , it provide an error : ‘Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:57459′ is therefore not allowed access. The response had HTTP status code 400.’ and if we click again then it let the user in.
I think when we are overriding ValidateClientAuthentication, we are missing something. Please help me in this issue. I tried many other blogs but didn’t get any real help.
amazing , thank you very much
You are welcome, thanks for your comment.
Hi, first of all I want to thanks for your articles. They helped a lot.
But there is one thing I don’t understand right now: The database access in combination with failure
For relevant systems you don’t want to have a single point of failure. So you need multiple authentication servers.
But what is the way to go with the database then? What are the best practices?
1. don’t store refresh tokens -> no revocation possible, but depending on the business case it’s feasible with relative short expiration time
2. each server has its own database and the client has to remember on which server the refresh token is valid (when that server goes down, the client has to reauthenticate to an other server)
3. all servers have one common database and that has to be e.g. a cluster on its own
or are all ways valid and I can decide what fits best?
are there any other downsides to solution 1 except for the revocation problem?
Thanks for this great article. is there any can i make access token shorter.
You are welcome, thanks for you commetn
Can someone explain me why after I publish my work, when refreshing the token my context.ticket is null if you wait like 5 min? Would it be hosting problem? On my local everything is perfect. A friend of mine told me something about load balancer that there are different concurrent servers and dont know about each others session like info. Would it be the reason?
Hi Boran,
Where did you host your app? A Shared hosting environment such as GoDaddy? If your app is hosted in web farm then hosting servers should share the same machineKey encryption/decrypt values which are something you do not have control on. What you can try to do is to override those values in your app web.config or to use JWTs instead. Check this post here
Thank you for your great tutorial!! so, I need to implement this in a different approach, instead of use AngularJS I want to use Asp .Net MVC. So, please, how can I proced? The web application must be a different project in the same solution? is this case, how can I use authentication in the web layer? I have to create a normal Asp .Net MVC project with single user authentication? Thanks!
Hi Pablo,
Apologies for the late reply, but this link should be very useful for your case. It uses MVC as FE which talks to WebAPI on the back and do authentication.
Taiseer,
First of all thank you very much for this very detailed tutorial.
I was able to generate the acess token but I’m having difficulties to generate the refresh token.
How can I generate the refresh token from a controller/method?
Thanks again!!
Hi Bartho,
what the use case that you need to generate refresh token from a controller? I’m not sure how this is done so I need to check it.
Taiseer,
I’m trying to generate both, the access and refresh tokens from a controller because my users are authenticating agains my OAuth Web API instead of my OAuthClient website (a bit different from your tutorial, in which you have users authenticate on your AngularJS, the OAuthClient website).
Instead of having the user authenticate on my OAuthClient website, I’ll have him authenticate on my OAuth Web API. Unauthenticated users that access my OAuthClient website are redirected to an logon page on my OAuth Web API. I send the client_id and redirect uri as query strings to my logon page, the user authenticates and then he is redirected back to my OAuthClient Website. Once he authenticates I should generate both the the access_token and refresh_token and send it back to the OAuthClient website.
I’m able to generate the access_token successfully, I’m even able to use it later to retrieve some information from my Web API application, but I’m not able to generate the refresh_token successfully.
I receive the following error:
“Make sure that the controller has a parameterless public constructor”
when trying to create a user.. please if anyone have any idea?
Are you using any sort of DI? You controller constructor has a parameter passed to it.
Hi Taiseer,
Thanks for the post. Would you have a client controller that you can post, to create clients?
Thanks.
Raj
Yes, I think this post covers creating a clients controller.
Hi, Does it mean I need to add the authorization header for both the access token and refresh token for every request?
No, you just send the access token in the AuthZ header and you store the refresh token locally until the access token is expired, once it is expired you use the refresh token to obtain new access token and so on..
All this is very nice thanks a lot. However, I ran into trouble with concurrency / threading issues when using the database to store the refresh tokens, using the async methods. Sometimes when two requests are started from the same client – which is a scenario that I think should be taken into account – this creates a problem for example here in the code:
var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId);
if (refreshToken != null) {
_ctx.RefreshTokens.Remove(refreshToken);
return await _ctx.SaveChangesAsync() > 0;
}
The savechangesasync complains that the found token does not exist anymore (as another thread has meanwhile removed it).
Any suggestions on how to take into account this problem? I thought about using a database transaction using context.Database.begintransaction in a using statement surrounding this code. Other places might also be vulnerable.
Hello Taiseer, Great series on this topic! I am wondering, how can a client app (angular for example) detect an access token is expired and then issue a refresh token to generate a new access token? Thanks
I guess I understood now the cycle. In the $http Interceptor you handle 401 responses and you call upon RefreshToken. Thanks again!
Hi,
I think, we can use last updated token datetime, current time and expires_in on client
Hey Taiseer Great Tutorial but one issue i faced while implementing refresh token is when i re request server for a new refresh token it through an error
“error”:”invalid grant_type”
i think its due to while context.deserialize(refreshToken.protectedTicket) doesn’t update context.ticket property it remains null …. can you guide me ???? please reply A.S.A.P
Me,
Can someone explain me why after I publish my work, when refreshing the token my context.ticket is null if you wait like 5 min? Would it be hosting problem? On my local everything is perfect. A friend of mine told me something about load balancer that there are different concurrent servers and dont know about each others session like info. Would it be the reason?
Reply
Taiseer Joudeh says
May 12, 2016 at 12:35 am
Hi Boran,
Where did you host your app? A Shared hosting environment such as GoDaddy? If your app is hosted in web farm then hosting servers should share the same machineKey encryption/decrypt values which are something you do not have control on. What you can try to do is to override those values in your app web.config or to use JWTs instead. Check this post https://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
Might be the answer you are looking. I tried that machine key thing but I changed my implementation that put the lifetime of refresh token longer. Might be useful for you
This is nice article.
I’ve implemented based on your article.
I’ve called through localhost:/token and generated the token and refresh token.
But I have the following scenario.
The user will have forgot password and will be invited through the application.
So after user activated, need to go to the application as logged in user.
But I can get only the user name through activation token, I cannot take the password.
Previously I have done like,
after activation, I have done like,
ClaimsIdentity identity = UserManager.CreateIdentity(user, OAuthDefaults.AuthenticationType);
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);.
Now I don’t know how to do that with your method.
How to Call the generate refresh token from controller?
I don’t have a file named “AuthContext” how do I make one?
AuthContext was build in this article
https://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
Thank you very much Taiseer. Your tutorial is the best and most thorough. I don’t know anything about security and can still follow through
However, there is 1 issue. How do you create the Client in the database and set up the schema as you did for UserModel? Could someone help? Thanks a lot,
Hi Hung,
I’m using Code First initialization to fill the table and build the DB scheme, this exists in the source code repo. For a real world application you need to create your own endpoints to create clients.
Greetings Taiseer!
Must agree! You are a great teacher, I wonder if you are this amazing offline then how you will be teaching live!
I have two queries:
1) I have added my query on stackoverflow i was wondering if you can help me with that, please read from the below link; at least till now there has been no response. My fingers are crossed
http://stackoverflow.com/questions/38328177/web-appmvc-5-web-api-2-native-android-and-ios-to-use-one-mode-of-authenticati
2) If i wish to have the token refreshed automatically after AccessTokenExpireTimeSpan elapses, is it possible to do from c# or javascript, i can see its possible via AngularJS, but i do not intent to implement AngularJS. If yes can you please guide or assist me!
Thank you !!
Hi,
Thank you for this informative series, it has been a great help.
I’ve run into an issue regarding regarding concurrency which I was hoping you may shed some light on please.
Using your demo code above, if many concurrent calls are made to the login (/token) end point with the same credentials, at some point in one of these concurrent requests, it ends up generating multiple records in the refresh token table. When this happens, the next request fails with a “Sequence contains more than one element” error when it tries to fetch a user’s refresh token (because there is more than one refresh token in the database).
Is there any check to put around this?
Thanks!
Hello Talha,
Maybe you can have a protection on the DB level by creating a unique constraint on the columns used to ensure refresh token uniqueness. Or you need to handle this on the EF code using transactions. But that is strange to have concurrency issue for the same refresh token if you are trying to regenerate this without using an automated tool (Human).
Taiseer,
Thanks for posting this article. I just started to build an app to play with and I noticed I get logged out after the timeSpan set in the following code (in Startup class) when it ends.
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString(“/token”),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
Provider = new SimpleAuthorizationServerProvider()
};
What I noticed is that I get logged out of the app every 2 hours even though there’s “activity” in the app. Should the “refresh token” enhancement that you described here solve this issue? Would this refresh the access token so I won’t be logged out? Basically, I want the token to expire after 2 hours, but only if there’s no API calls in those 2 hours.
If the solution is not presented here, can you suggest the best and easiest way to do it?
Thanks,
Andrei
Hello Anderi,
Refresh tokens will not keep the same access token refreshed, it will generate a new access token silently on behalf of the user so the user will not be logged out, you need to handle refreshing tokens and sending the new obtained access token in your app logic, this is can not be done from the backend.
Taiseer
Awesome article! Thanks so much.
One question: When we refresh an existing RefreshToken, shouldn’t the new RefresToken be logged in the database, on the RefreshToken table?
Tks
Hello Pascal,
Thanks for your comment. Indeed the new refresh token should remove the existing one and the new one is inserted.
Taiseer,
Thank you very much for this article. I am currently implementing a slight variation to this. However, when I make my call to get a new refresh token, I am not hitting the GrantRefreshToken method. My ReceiveAsync method is identical to yours. My request body has the refresh token and grant_type of refresh_token. I noticed the summary for the default GrantRefreshToken suggests it should be called each time a grant_type of refresh_token is received. That is not the case for me. I have verified that the ValidateClientAuthentication method is reached, but I am not sure what is happening after this method that prevents GrantRefreshToken from being called. Any insight is appreciated.
I am also not setting the protected ticket or client Id values because our implementation needs to be a bit different. Are these two items needed?
Hello Gavin,
I’m not sure why this is happening but make sure you are hitting context.Validated(); and you are not calling context.SetError(); try to put extensive trace messages in order to troubleshoot it. Sorry for not being more helpful.
Hi Taiseer.
I have the same error Gavin case. And I noticed after the command completes context.DeserializeTicket (refreshToken.ProtectedTicket) then context.Ticket always null. I think this is the cause of this error born. Hope you can help me. Thank you.
Thank you so much for this article! It is very well-explained and informative.
Everything before step 6 ran smoothly. At step 6, I encountered exactly the same issue as Gavin did. Instead of getting an access token back, I am getting the error message “{“error”: “invalid_grant”}”. I have set one break point at the start of the method ReceiveAsync and one break point at the start of the method GrantRefreshToken.
This is my observation from debugging: the ReceiveAsync did get executed. However, everything stopped after the last line “var result = await userManager.RemoveRefreshToken(hashedTokenId);” ran. The method GrantRefreshToken never got executed.
My code strictly followed yours, so I am confused about what the problem might be. I believe the first comment by Kelly sACAULT described a similar problem I am having right now.
Could you please give me some insight? Or has anyone else who ran into this problem before had a solution now?
Thank you so much!
To add to my previous comment:
I went through the ReceiveAsync method step by step by debugging. Each step generated the correct non-null values for the variables, but everything just magically stopped after this method.
Taiseer, Gavin, Kelly, and whoever’s seeing the error “invalid_grant”, or had the problem where the app stopped before calling the GrantRefreshToken method:
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 all as well.
Thanks Lydia for sharing this, I need to check the code as it has been a while. I’m happy that all is working fine at you end now 🙂
Hi Taiseer,
Thanks for the awesome tutorial! If I am understanding properly refresh tokens you explain here are OAuth tokens. Would the approach be any different for JWT Refresh Tokens? Thank you!
– David