Created
May 26, 2017 12:33
-
-
Save ArtemAvramenko/8f34ec8aac5c0fb6380871a18e69ffd8 to your computer and use it in GitHub Desktop.
ASP.NET Identity: Add ability to have different token lifespan for different purposes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Globalization; | |
using System.IO; | |
using System.Text; | |
using System.Threading.Tasks; | |
using Microsoft.AspNet.Identity; | |
using Microsoft.Owin.Security.DataProtection; | |
/// <summary> | |
/// Token provider that uses an IDataProtector to generate encrypted tokens based off of the security stamp | |
/// </summary> | |
public class DataProtectorTokenProviderEx<TUser, TKey> : IUserTokenProvider<TUser, TKey> | |
where TUser : class, IUser<TKey> | |
where TKey : IEquatable<TKey> | |
{ | |
/// <summary> | |
/// Constructor | |
/// </summary> | |
public DataProtectorTokenProviderEx(IDataProtector protector) | |
{ | |
if (protector == null) | |
{ | |
throw new ArgumentNullException("protector"); | |
} | |
Protector = protector; | |
TokenLifespan = TimeSpan.FromDays(1.0); | |
} | |
/// <summary> | |
/// IDataProtector for the token | |
/// </summary> | |
public IDataProtector Protector | |
{ | |
get; | |
private set; | |
} | |
/// <summary> | |
/// Lifespan after which the token is considered expired | |
/// </summary> | |
public TimeSpan TokenLifespan | |
{ | |
get; | |
set; | |
} | |
/// <summary> | |
/// Generate a protected string for a user | |
/// </summary> | |
public async Task<string> GenerateAsync(string purpose, UserManager<TUser, TKey> manager, TUser user) | |
{ | |
if (user == null) | |
{ | |
throw new ArgumentNullException("user"); | |
} | |
var memoryStream = new MemoryStream(); | |
using (var binaryWriter = new BinaryWriter(memoryStream, new UTF8Encoding(false, true), true)) | |
{ | |
binaryWriter.Write(DateTimeOffset.UtcNow.UtcTicks); | |
binaryWriter.Write(Convert.ToString(user.Id, CultureInfo.InvariantCulture)); | |
binaryWriter.Write(purpose ?? ""); | |
string stamp = null; | |
if (manager.SupportsUserSecurityStamp) | |
{ | |
stamp = await manager.GetSecurityStampAsync(user.Id); | |
} | |
binaryWriter.Write(stamp ?? ""); | |
} | |
byte[] token = this.Protector.Protect(memoryStream.ToArray()); | |
return Convert.ToBase64String(token); | |
} | |
/// <summary> | |
/// Return false if the token is not valid | |
/// </summary> | |
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser, TKey> manager, TUser user) | |
{ | |
try | |
{ | |
var buffer = Protector.Unprotect(Convert.FromBase64String(token)); | |
var stream = new MemoryStream(buffer); | |
using (var binaryReader = new BinaryReader(stream, new UTF8Encoding(false, true), true)) | |
{ | |
var dateTimeOffset = new DateTimeOffset(binaryReader.ReadInt64(), TimeSpan.Zero); | |
string tokenId = binaryReader.ReadString(); | |
if (!string.Equals(tokenId, Convert.ToString(user.Id, CultureInfo.InvariantCulture))) | |
{ | |
return false; | |
} | |
string tokenPurpose = binaryReader.ReadString(); | |
if (!string.Equals(tokenPurpose, purpose)) | |
{ | |
return false; | |
} | |
///////////////////////////////////////////////// | |
// INSERT CUSTOM TOKEN LIFE SPAN VALIDATION HERE | |
if (purpose != "Confirmation") | |
{ | |
var left = dateTimeOffset + TokenLifespan; | |
if (left < DateTimeOffset.UtcNow) | |
{ | |
return false; | |
} | |
} | |
///////////////////////////////////////////////// | |
string tokenStamp = binaryReader.ReadString(); | |
if (binaryReader.PeekChar() != -1) | |
{ | |
return false; | |
} | |
var stamp = ""; | |
if (manager.SupportsUserSecurityStamp) | |
{ | |
stamp = await manager.GetSecurityStampAsync(user.Id); | |
} | |
return tokenStamp == stamp; | |
} | |
} | |
catch | |
{ | |
return false; | |
} | |
} | |
/// <summary> | |
/// Returns true if the provider can be used to generate tokens for this user | |
/// </summary> | |
public Task<bool> IsValidProviderForUserAsync(UserManager<TUser, TKey> manager, TUser user) | |
{ | |
return Task.FromResult(true); | |
} | |
/// <summary> | |
/// This provider no-ops by default when asked to notify a user | |
/// </summary> | |
public Task NotifyAsync(string token, UserManager<TUser, TKey> manager, TUser user) | |
{ | |
return Task.FromResult(0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment