Hello all,
I'm trying to figure out if there is any way to provide a custom error description and custom https status code when we request a bearer token with expired Refresh Token.
Authentication: OAuth 2.0
Grant Type as Resource Owner Password
We authenticate every user using resource owner password credentials.
All the API calls were protected with Bearer token authentication.
After the expiration of the bearer token. We use refresh_token to generate a new bearer token.Refresh token has also an expiration time. Let's say expiration of the refresh token is 30min.After 30min the refresh token is invalid which will force the user to re-enter the credentials to log-in.
By default when providing an invalid refresh token.API throws the system error description as "invalid grant" as BAD REQUEST
But we want to throw a custom error to the user as INTERNAL SERVER ERROR.
Current behavior when we request an expired refresh token it throw as below
Bad request 400
with the response as
{
"error": "invalid_grant"
}
My Startup.Auth.cs
public partial class Startup { private int _AccessTokenExpirationMintues =Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["_AccessTokenExpirationMintues"]); public void ConfigureAuth(IAppBuilder app) { // Enable Application Sign In Cookie app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = "Application", AuthenticationMode = AuthenticationMode.Passive, LoginPath = new PathString(Paths.LoginPath), LogoutPath = new PathString(Paths.LogoutPath), }); // Enable External Sign In Cookie app.SetDefaultSignInAsAuthenticationType("External"); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = "External", AuthenticationMode = AuthenticationMode.Passive, CookieName = CookieAuthenticationDefaults.CookiePrefix + "External", ExpireTimeSpan = TimeSpan.FromMinutes(_AccessTokenExpirationMintues), }); // Setup Authorization Server app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions { AuthorizeEndpointPath = new PathString(Paths.AuthorizePath), TokenEndpointPath = new PathString(Paths.TokenPath), ApplicationCanDisplayErrors = true, #if DEBUG AllowInsecureHttp = true, #endif AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(_AccessTokenExpirationMintues), //TimeSpan.FromMinutes(_AccessTokenExpirationMintues), // Authorization server provider which controls the lifecycle of Authorization Server Provider = new OAuthAuthorizationServerProvider { //OnValidateClientRedirectUri = ValidateClientRedirectUri, OnValidateClientAuthentication = ValidateClientAuthentication, OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials, OnGrantClientCredentials = GrantClientCredetails }, // Authorization code provider which creates and receives authorization code AuthorizationCodeProvider = new AuthenticationTokenProvider { OnCreate = CreateAuthenticationCode, OnReceive = ReceiveAuthenticationCode, }, // Refresh token provider which creates and receives referesh token RefreshTokenProvider=new SimpleRefreshTokenProvider() }); app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); } private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); return Task.FromResult(0); } private Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { var identity = new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x))); context.Validated(identity); return Task.FromResult(0); } private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context) { var identity = new ClaimsIdentity(new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x))); context.Validated(identity); return Task.FromResult(0); } private readonly ConcurrentDictionary<string, string> _authenticationCodes = new ConcurrentDictionary<string, string>(StringComparer.Ordinal); private void CreateAuthenticationCode(AuthenticationTokenCreateContext context) { context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n")); context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(_AccessTokenExpirationMintues); _authenticationCodes[context.Token] = context.SerializeTicket(); } private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context) { string value; if (_authenticationCodes.TryRemove(context.Token, out value)) { context.DeserializeTicket(value); } } } }
My SimpleRefreshTokenProvider.cs
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider { private int _RefreshTokenExpirationMintues = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["_RefreshTokenExpirationMintues"]); private static ConcurrentDictionary<string, AuthenticationTicket> RefreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>(); public async Task CreateAsync(AuthenticationTokenCreateContext context) { var guid = Guid.NewGuid().ToString(); var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary) { IssuedUtc = context.Ticket.Properties.IssuedUtc, ExpiresUtc = DateTime.UtcNow.AddMinutes(_RefreshTokenExpirationMintues) //SET DATETIME to 5 Minutes //ExpiresUtc = DateTime.UtcNow.AddMonths(3) }; //context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddSeconds(40); /*CREATE A NEW TICKET WITH EXPIRATION TIME OF 5 MINUTES *INCLUDING THE VALUES OF THE CONTEXT TICKET: SO ALL WE *DO HERE IS TO ADD THE PROPERTIES IssuedUtc and *ExpiredUtc to the TICKET*/ var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties); //saving the new refreshTokenTicket to a local var of Type ConcurrentDictionary<string,AuthenticationTicket> // consider storing only the hash of the handle RefreshTokens.TryAdd(guid, refreshTokenTicket); context.SetToken(guid); } // this method will be used to generate Access Token using the Refresh Token public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { AuthenticationTicket ticket; if (RefreshTokens.TryRemove(context.Token, out ticket)) { context.SetTicket(ticket); } } public void Create(AuthenticationTokenCreateContext context) { throw new NotImplementedException(); } public void Receive(AuthenticationTokenReceiveContext context) { throw new NotImplementedException(); } }