Reputation: 105
I have implemented the OAuth token-based Authentication in our WebAPI Application and validating the username and password against the database. But we are not syncing the access tokens and refresh tokens to any type of database. Here is the code, however, I have one question where the token values are stored.
Below code for generating the Token
/// <summary>
/// Grant resource owner credentials overload method.
/// </summary>
/// <param name="context">Context parameter</param>
/// <returns>Returns when task is completed</returns>
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// Initialization.
var usernameVal = context.UserName;
var passwordVal = context.Password;
var user = _securityLogic.AuthenticateApiUser(usernameVal, passwordVal);
// Verification.
if (!user)
{
// Settings.
context.SetError("invalid_grant", "The user name or password is incorrect.");
// Return info.
return;
}
// Initialization.
var claims = new List<Claim>
{
//var userInfo = user.FirstOrDefault();
// Setting
new Claim(ClaimTypes.Name, usernameVal)
};
// Setting Claim Identities for OAUTH 2 protocol.
ClaimsIdentity oAuthClaimIdentity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesClaimIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationType);
// Setting user authentication.
AuthenticationProperties properties = CreateProperties(usernameVal);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthClaimIdentity, properties);
// Grant access to authorize user.
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesClaimIdentity);
}
#endregion
#region Token endpoint override method.
/// <summary>
/// Token endpoint override method
/// </summary>
/// <param name="context">Context parameter</param>
/// <returns>Returns when task is completed</returns>
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
// Adding.
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
// Return info.
return Task.FromResult<object>(null);
}
#endregion
This is code is for generating the refresh Token
#region GrantRefreshToken
private static readonly ConcurrentDictionary<string, AuthenticationTicket> RefreshTokens =
new ConcurrentDictionary<string, AuthenticationTicket>();
/// <summary>
/// Grants Refresh Token
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
// Change authentication 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);
}
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
// Copy claims from the previous token
var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
{
IssuedUtc = context.Ticket.Properties.IssuedUtc,
ExpiresUtc = DateTime.UtcNow.AddMinutes(30)
};
var refreshTokenTicket = await Task.Run(() =>
new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties));
RefreshTokens.TryAdd(guid, refreshTokenTicket);
// Consider storing only the hash of the handle
context.SetToken(guid);
}
#endregion
So, my question is .NET/Owin/IdentityServer3 writing them to some in-memory database? If so, can they be accessed for things like viewing and deleting? And what happens if the app server is restarted, are all the tokens wiped out? Or are they persistent?
And do you recommend storing in the Database and retrieving it from the database? Any help is appreciated, by the way, this code is working perfectly fine.
Upvotes: 0
Views: 339
Reputation:
From the documentation:
If not specifically configured we will always provide an in-memory version stores for authorization codes, consent, reference and refresh tokens.
Please note that they talk about reference tokens and refresh tokens. JWT access tokens and identity tokens are not stored.
In order to use a refresh token in IdentityServer3 (and also IdentityServer4) it has to match a stored token.
The major benefit of that is that you can control the token. You can revoke it (removes it from the store), and define how to use it: OneTime or ReUse.
I'm not familiar with IdentityServer3, but you can take a look at github and search the code where the RevocationEndpoint is implemented, the place where the refresh token is removed from the store. That may give a clue on how to access and use the store.
With in-memory stores, the tokens are lost on restart of the IdentityServer. So persisting them in a persistent store, like a database, is a good thing for production servers. For IdentityServer4 you can implement an operational store.
Please note that JWT tokens remain valid regardless a restart of the server, unless the private key is also not persisted. In that case IdentityServer can't validate the token and has no choice but to consider the JWT tokens as invalid.
So for production environments you should persist keys and data, and using a database is fine. As you can see in IdentityServer4 there is support for this.
Speaking of IdentityServer4, since (free) support of IdentityServer3 has ended I would recommend to switch to IdentityServer4, if you are in the position to do so. Since both versions implement oidc/auth2 you should be able to keep using the clients with the upgraded IdentityServer. On stackoverflow there are questions that can help you with that. And take a look at the IdentityServer4 documentation, it's very informative.
Upvotes: 1