When I try to generate an Access Token using the Refresh Token before Access Token expires, the system generate a new one and everything is okey. But if the Access Token is expired, the request return invalid_grant.
Isn't the Validated() method from GrantRefreshToken generate the Access Token using the identity I stored in the dictionary from the previously Access Token?
And how can I block clients from asking for new Access Token using the same Refresh Token if the last one isn't expired yet?
Here is my code:
Startup.cs:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new OAuthProvider(),
RefreshTokenProvider = new RefreshTokenProvider()
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseWebApi(config);
}
}
OAuthProvider.cs:
public class OAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName == "admin" && context.Password == "123456")
{
var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
var ticket = new AuthenticationTicket(claimsIdentity, null);
context.Validated(ticket);
}
return Task.FromResult<object>(null);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
context.Validated(context.Ticket);
return Task.FromResult<object>(null);
}
}
RefreshTokenProvider.cs:
public class RefreshTokenProvider : AuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public override Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
_refreshTokens.TryAdd(guid, context.Ticket);
context.SetToken(guid);
return Task.FromResult<object>(null);
}
public override Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
if (_refreshTokens.TryRemove(context.Token, out AuthenticationTicket ticket))
{
context.SetTicket(ticket);
}
return Task.FromResult<object>(null);
}
}
Sorry for bad english, I hope you understand!
EDIT:
Well, I modified the code to implement Database support and set the Refresh Token expiration to 5 minutes. The expiration is small for testing purposes.
Here is the result:
OAuthProvider.cs:
public class OAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
try
{
var account = AccountRepository.Instance.GetByUsername(context.UserName);
if (account != null && Global.VerifyHash(context.Password, account.Password))
{
var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, account.Username));
claimsIdentity.AddClaim(new Claim("DriverId", account.DriverId.ToString()));
var newTicket = new AuthenticationTicket(claimsIdentity, null);
context.Validated(newTicket);
}
}
catch { }
return Task.FromResult<object>(null);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
}
RefreshTokenProvider.cs:
public class RefreshTokenProvider : AuthenticationTokenProvider
{
public override Task CreateAsync(AuthenticationTokenCreateContext context)
{
var refreshToken = new TokenModel()
{
Subject = context.Ticket.Identity.Name,
Token = GenerateToken(),
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(5)
};
context.Ticket.Properties.IssuedUtc = refreshToken.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = refreshToken.ExpiresUtc;
refreshToken.Ticket = context.SerializeTicket();
try
{
TokenRepository.Instance.Insert(refreshToken);
context.SetToken(refreshToken.Token);
}
catch { }
return Task.FromResult<object>(null);
}
public override Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
try
{
var refreshToken = TokenRepository.Instance.Get(context.Token);
if (refreshToken != null)
{
if (TokenRepository.Instance.Delete(refreshToken))
{
context.DeserializeTicket(refreshToken.Ticket);
}
}
}
catch { }
return Task.FromResult<object>(null);
}
private string GenerateToken()
{
HashAlgorithm hashAlgorithm = new SHA256CryptoServiceProvider();
byte[] byteValue = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString("N"));
byte[] byteHash = hashAlgorithm.ComputeHash(byteValue);
return Convert.ToBase64String(byteHash);
}
}
Thanks for the support!