Where does OAuthAuthorizationServerProvider check Refresh Token Expiry?
<p>I have implemented an authorization server following the article</p>

<p><a href="http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/" rel="nofollow noreferrer">http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/</a></p>

<p>I've implemented it almost exactly like the article, but I dont see how the auth server knows the refresh token is expired. Indeed I've tested and the server does not grant access tokens when presented expired refresh tokens, but I dont see the logic for this in my auth server. Further, when I request an access token using an expired refresh token the my OAuthAuthorizationServerProvider sub class is never called at all, actually none of my methods in my OAuthAuthorizationServerProvider derived class or my IAuthenticationTokenProvider implementation are called when I request a new access token using an expired refresh token. Any help is appreciated. Here is what I have</p>

<pre><code>public class SmartCardOAuthAuthenticationTokenProvider : IAuthenticationTokenProvider
{
    private IDataAccessFactoryFactory _producesFactoryThatProducesIAuthenticateDataAccess;
    public SmartCardOAuthAuthenticationTokenProvider(IDataAccessFactoryFactory producesFactoryThatProducesIAuthenticateDataAccess)
    {
        _producesFactoryThatProducesIAuthenticateDataAccess = producesFactoryThatProducesIAuthenticateDataAccess;

    }
    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(IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory())//using (IAuthorizationDataAccess _repo = new AuthRepository())
        {
            IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess&lt;IAuthorizationDataAccess&gt;();

            var refreshTokenLifeTime = context.OwinContext.Get&lt;string&gt;("as:clientRefreshTokenLifeTime");

            var token = new RefreshToken()
            {
                RefreshTokenId = 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.AddRefreshTokenAsync(token);

            if (result)
            {
                context.SetToken(refreshTokenId);
            }

        }
    }

    public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {

        var allowedOrigin = context.OwinContext.Get&lt;string&gt;("as:clientAllowedOrigin");
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });

        string hashedTokenId = Helper.GetHash(context.Token);

        //using (IAuthorizationDataAccess _repo = new AuthRepository())
        //{
        using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository())
        {
            IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess&lt;IAuthorizationDataAccess&gt;();

            var refreshToken = await _repo.FindRefreshTokenAsync(hashedTokenId);

            if (refreshToken != null)
            {
                //Get protectedTicket from refreshToken class
                context.DeserializeTicket(refreshToken.ProtectedTicket);
                var result = await _repo.RemoveRefreshTokenAsync(hashedTokenId);
            }
        }
    }

    public void Create(AuthenticationTokenCreateContext context)
    {
        throw new NotImplementedException();
    }

    public void Receive(AuthenticationTokenReceiveContext context)
    {
        throw new NotImplementedException();
    }
}

public class SmartCardOAuthAuthorizationProvider : OAuthAuthorizationServerProvider
{
    private IDataAccessFactoryFactory _producesFactoryThatProducesIAuthenticateDataAccess;
    public SmartCardOAuthAuthorizationProvider(IDataAccessFactoryFactory producesFactoryThatProducesIAuthenticateDataAccess)
    {
        _producesFactoryThatProducesIAuthenticateDataAccess = producesFactoryThatProducesIAuthenticateDataAccess;

    }
    public override System.Threading.Tasks.Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var allowedOrigin = context.OwinContext.Get&lt;string&gt;("as:clientAllowedOrigin");

        if (allowedOrigin == null) allowedOrigin = "*";

        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });

        if (context.UserName != "onlyOneHardCodedUserForSakeOfExploration" &amp;&amp; context.Password!="thePassword")
        {
            context.SetError("invalid_grant", "the user name or password is incorrect");
            return Task.FromResult&lt;object&gt;(null); ;
        }
        ClaimsIdentity 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(ClaimTypes.Role, "PostVSDebugBreakModeEnterEventArgs"));
        identity.AddClaim(new Claim(DatawareClaimTypes.SmartCardUserId.ToString(), 1.ToString()));

        var props = new AuthenticationProperties(new Dictionary&lt;string, string&gt;
            {
                { 
                    "as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
                },
                { 
                    "userName", context.UserName
                }
            });

        var ticket = new AuthenticationTicket(identity, props);
        //ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddDays(2));
        context.Validated(ticket);

        return Task.FromResult&lt;object&gt;(null);
    }

    public override System.Threading.Tasks.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&lt;object&gt;(null);
        }

        string[] clientIdClientUnique = context.ClientId.Split(':');

        if (clientIdClientUnique == null || clientIdClientUnique.Length &lt;= 1)
        {
            context.SetError("invalid_client_unique");
            return Task.FromResult&lt;object&gt;(null);
        }

        clientId = clientIdClientUnique[0];
        string clientUnique = clientIdClientUnique[1];

        using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository())
        {
            IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess&lt;IAuthorizationDataAccess&gt;();


            client = _repo.FindClient(clientId);//new Client { Active = true, AllowedOrigin = "*", ApplicationType = ApplicationTypes.DesktopClient, ClientId = context.ClientId, Name = "Visual Studio Event Source", RefreshTokenLifeTimeInMinutes = 14400, Secret = Helper.GetHash(clientSecret) };//_repo.FindClient(context.ClientId);
        }

        if (client == null)
        {
            //context.SetError("invalid_client_unique");
            context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
            return Task.FromResult&lt;object&gt;(null);
        }

        if (string.IsNullOrWhiteSpace(clientSecret))
        {
            context.SetError("invalid_clientId", "Client secret should be sent.");
            return Task.FromResult&lt;object&gt;(null);
        }
        else
        {
            if (client.Secret != Helper.GetHash(clientSecret))
            {
                context.SetError("invalid_clientId", "Client secret is invalid.");
                return Task.FromResult&lt;object&gt;(null);
            }
        }

        if (!client.Active)
        {
            context.SetError("invalid_clientId", "Client is inactive.");
            return Task.FromResult&lt;object&gt;(null);
        }

        context.OwinContext.Set&lt;string&gt;("as:clientAllowedOrigin", client.AllowedOrigin);
        context.OwinContext.Set&lt;string&gt;("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTimeInMinutes.ToString());

        context.Validated();
        return Task.FromResult&lt;object&gt;(null);
    }

    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&lt;object&gt;(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&lt;object&gt;(null);
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair&lt;string, string&gt; property in context.Properties.Dictionary)
        {
            context.AdditionalResponseParameters.Add(property.Key, property.Value);
        }

        return Task.FromResult&lt;object&gt;(null);
    }

}
</code></pre>

<p>Again, when I make a request</p>

<p><a href="https://i.stack.imgur.com/lbNTw.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/lbNTw.png" alt="request with expired refresh"></a>
and the refresh token is expired, NONE of the above classes\methods get hit. Further, the refresh token passed to the API is nothing more than the GUID I generated in the <code>SmartCardOAuthAuthenticationTokenProvider.CreateAsync</code> it contains no information about expiry. If none of the above methods are hit when requesting access by refresh and the request passes nothing(it looks like nothing) when requesting a new access token by refresh then how does the server know the refresh token is expired?</p>

<p>Looks like magic to me.</p>

<p>UPDATE 1 - Add Startup code</p>

<pre><code>public static class OwinStartUpConfig
{
    public static void Configure(HttpConfiguration configFromOwinStartup)
    {
        configFromOwinStartup.MapHttpAttributeRoutes();
        configFromOwinStartup.Routes.MapHttpRoute("Default", "{controller}/{id}", new { id = RouteParameter.Optional });

        var jsonFormatter = configFromOwinStartup.Formatters.OfType&lt;JsonMediaTypeFormatter&gt;().First();
        jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

        RegiserDependencies(configFromOwinStartup);

    }

    public static void RegiserDependencies(HttpConfiguration configFromOwinStartup)
    {
        string connectionStringForSmartCardDbCntx = System.Configuration.ConfigurationManager.ConnectionStrings["SmartCardDataContext"].ConnectionString;
        string projectNameWhenNewProjectCreatedDueToNoMatch = System.Configuration.ConfigurationManager.AppSettings["ProjectNameWhenNewProjectCreatedDueToNoMatch"];

        Autofac.ContainerBuilder builderUsedToRegisterDependencies = new Autofac.ContainerBuilder();

        builderUsedToRegisterDependencies.RegisterType&lt;DataAccessFactoryFactoryEf&gt;()
            .As&lt;IDataAccessFactoryFactory&gt;()
            .WithParameter(new TypedParameter(typeof(string), connectionStringForSmartCardDbCntx));

        builderUsedToRegisterDependencies.Register(
            c =&gt;
                    new List&lt;IProjectActivityMatch&lt;VSDebugBreakModeEnterActivity&gt;&gt;
            {
                new MatchVSProjectWithMostRecentActivity&lt;VSDebugBreakModeEnterActivity&gt;(),
                new MatchVSSolutionWithMostRecentActivityActivityMatch&lt;VSDebugBreakModeEnterActivity&gt;(),
                new MatchMostRecentActivityMatch&lt;VSDebugBreakModeEnterActivity&gt;(),
                new MatchToNewProjectActivityMatch&lt;VSDebugBreakModeEnterActivity&gt;(projectNameWhenNewProjectCreatedDueToNoMatch)
            }
        ).As&lt;IEnumerable&lt;IProjectActivityMatch&lt;VSDebugBreakModeEnterActivity&gt;&gt;&gt;();

        builderUsedToRegisterDependencies
            .RegisterType&lt;MatchDontGiveUpActivityMatch&lt;VSDebugBreakModeEnterActivity&gt;&gt;()
            //.WithParameter(Autofac.Core.ResolvedParameter.ForNamed&lt;IEnumerable&lt;IProjectActivityMatch&lt;VSDebugBreakModeEnterActivity&gt;&gt;&gt;("VSDebugBreakModeEnterActivityMatchers"))
            .As&lt;IProjectActivityMatch&lt;VSDebugBreakModeEnterActivity&gt;&gt;();

        builderUsedToRegisterDependencies
            .RegisterType&lt;VSDebugBreakModeEnterEventArgsEventSaver&gt;()
            .Named&lt;ISaveVisualStudioEvents&gt;("VSDebugBreakModeEnterSaver");

        builderUsedToRegisterDependencies
            .RegisterType&lt;VSDebugBreakModeEnterEventArgsController&gt;()
            .WithParameter(Autofac.Core.ResolvedParameter.ForNamed&lt;ISaveVisualStudioEvents&gt;("VSDebugBreakModeEnterSaver"));

        var container = builderUsedToRegisterDependencies.Build();

        configFromOwinStartup.DependencyResolver = new AutofacWebApiDependencyResolver(container);

    }
}
    public static class OAuthStartupConfig
{
    internal static void Configure(IAppBuilder app)
    {
        OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions
        {
            AllowInsecureHttp = false,
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), //TimeSpan.FromDays(1),
            Provider = new SmartCardOAuthAuthorizationProvider(new AuthorizationDataAccessFactoryFactory()),
            RefreshTokenProvider = new SmartCardOAuthAuthenticationTokenProvider(new AuthorizationDataAccessFactoryFactory())
        };
        app.UseOAuthAuthorizationServer(oAuthServerOptions);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    }
}
</code></pre>

<p>UPDATE - In response to comments</p>

<p><a href="https://i.stack.imgur.com/m3clm.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/m3clm.png" alt="request for access token by pass returns access and refresh token"></a></p>

<p>The access token in the above response contains claims information and the expiry information for the access token. All that info is serialized by the server into the access token above. So, I can see how the access token's expiry is checked. However, what about the refresh token? When a request for an access token is make using the refresh token:</p>

<p><a href="https://i.stack.imgur.com/LnIjP.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/LnIjP.png" alt="enter image description here"></a></p>

<p>In the request above the refresh token is good and so the request for the access token by refresh token is granted, but if the refresh token were expired how would OAuthAuthorizationServerProvider check the refresh token for expiry?</p>

<p>Again, I have checked that the code provided above does indeed check for expired refresh tokens and does not grant an access token if the refresh token is expired, but HOW does it know? I didnt write anything for checking refresh token expiry into my OAuthAuthorizationServerProvider derived class. So then how????</p>

<p>UPDATE</p>

<p>The magic happens in the ReceiveAsync method of the IAuthenticationTokenProvider implementation. </p>

<pre><code>public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {

        var allowedOrigin = context.OwinContext.Get&lt;string&gt;("as:clientAllowedOrigin");
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });

        string hashedTokenId = Helper.GetHash(context.Token);

        //using (IAuthorizationDataAccess _repo = new AuthRepository())
        //{
        using (IDataAccessFactory producesIAuthenticateDataAccess = _producesFactoryThatProducesIAuthenticateDataAccess.GetDataAccessFactory()) //using (AuthRepository _repo = new AuthRepository())
        {
            IAuthorizationDataAccess _repo = producesIAuthenticateDataAccess.GetDataAccess&lt;IAuthorizationDataAccess&gt;();

            var refreshToken = await _repo.FindRefreshTokenAsync(hashedTokenId);

            if (refreshToken != null)
            {
                //Get protectedTicket from refreshToken class
                context.DeserializeTicket(refreshToken.ProtectedTicket);
                var result = await _repo.RemoveRefreshTokenAsync(hashedTokenId);
            }
        }
    }
</code></pre>

<p>Particularly the line <code>context.DeserializeTicket(refreshToken.ProtectedTicket);</code> is the magic. That will set the context.Ticket property. After the method ReceiveAsync has completed its done. No need to manually check anything OWIN, somewhere behind the scenes, knows the ticket is expired.</p>