Search code examples
asp.netoauthowin

Why CreateAsync of AuthenticationTokenProvider Is Called When Request to Exchange RefreshToken Arrives?


I have custom implementation of AuthenticationTokenProvider abstraction. It has two methods to be overriden that I'm using: CreateAsync, ReceiveAsync.

In OAuthAuthorizationServerOptions I have RefreshTokenProvider set to my custom AuthenticationTokenProvider implementation.

My access tokens expire in 20 minutes. My refresh tokens expire in 24 hours. When access token expires a request comes with grant_type=refresh_token containing refresh token. I observe ReceiveAsync is called. There is a logic of setting Ticket property of AuthenticationTokenReceiveContext. But afterwards CreateAsync method is called, where there is a logic of setting token in AuthenticationTokenCreateContext. The Ticket property of AuthenticationTokenCreateContext does not seem to be that one I have set previously in ReceiveAsync method.

As a result I receive response with new access token and refresh token. I don't want refresh token to be reissued each time I want to exchange my access token, I already have one valid for 24 hours.


Solution

  • Eventually I have found how to answer my question. I can leverage OwinContext.Environment to store a flag which tells that my refresh token is not expired yet so there is no need of creation a new one.

    public class RefreshTokenProvider : AuthenticationTokenProvider
    {
        private const string IsRefreshTokenExpiredName = "IsRefreshTokenExpired";
    
        #region ctor
        public RefreshTokenProvider()
        {
        }
        #endregion
    
        public async override Task CreateAsync(AuthenticationTokenCreateContext context)
        {
            if (!context.OwinContext.Environment.ContainsKey(IsRefreshTokenExpiredName) || (bool)context.OwinContext.Environment[IsRefreshTokenExpiredName])
            {
                var hours = int.Parse(ConfigurationManager.AppSettings["RefreshTokenExpirationHours"]);
                var now = DateTime.UtcNow;
                context.Ticket.Properties.IssuedUtc = now;
                context.Ticket.Properties.ExpiresUtc = now.AddHours(hours);
                context.SetToken(context.SerializeTicket());
            }
        }
    
        public async override Task ReceiveAsync(AuthenticationTokenReceiveContext context)
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { ConfigurationManager.AppSettings["CorsOrigins"] });
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Method", new[] { "POST" });
    
            context.DeserializeTicket(context.Token);
            if (context.Ticket.Properties.ExpiresUtc > DateTime.UtcNow)
                context.OwinContext.Environment[IsRefreshTokenExpiredName] = false;
        }
    }