Skip to content

Instantly share code, notes, and snippets.

@RredCat
Created July 22, 2019 15:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RredCat/3c3a30fbe79765b30911a2785b8910b6 to your computer and use it in GitHub Desktop.
Save RredCat/3c3a30fbe79765b30911a2785b8910b6 to your computer and use it in GitHub Desktop.
TokenHandler for FA blog.
internal class TokenHandler : AuthenticationHandler
{
private const ulong _REQUEST_MAX_AGE_IN_SECONDS = 300; //5 mins
private const string _AUTHENTICATION_SCHEME = "amx";
private static readonly DateTime _1970 = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
private static readonly Dictionary _AllowedApps = new Dictionary();
private readonly IMemoryCache _cache;
public TokenHandler(IMemoryCache memoryCache, IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
_cache = memoryCache;
if (_AllowedApps.Count == 0)
{
_AllowedApps.Add("4d53bce03ec34c0a911182d4c228ee6c", "A93reRTUJHsCuQSHR+L3GxqOJyDmQpCgps102ciuabc=");
}
}
protected override async Task HandleAuthenticateAsync()
{
var headers = (FrameRequestHeaders)Context.Request.Headers;
var authorizations = headers.HeaderAuthorization;
if (authorizations.Any())
{
var authorization = authorizations.First();
if (authorization.StartsWith(_AUTHENTICATION_SCHEME))
{
var autherizationHeaderArray = GetAutherizationHeaderValues(authorization);
if (autherizationHeaderArray != null)
{
var appId = autherizationHeaderArray[0];
var incomingBase64Signature = autherizationHeaderArray[1];
var nonce = autherizationHeaderArray[2];
var requestTimeStamp = autherizationHeaderArray[3];
var request = Context.Request;
var isValid = IsValidRequest(request, appId, incomingBase64Signature, nonce, requestTimeStamp);
if (isValid)
{
var identity = new ClaimsIdentity("api");
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), null, "api");
return AuthenticateResult.Success(ticket);
}
return AuthenticateResult.Fail("Incorrect'Authorization' header.");
}
}
}
return AuthenticateResult.Fail("Missing or malformed 'Authorization' header.");
}
private bool IsValidRequest(HttpRequest request, string appId, string incomingBase64Signature, string nonce, string requestTimeStamp)
{
string requestContentBase64String = "";
string requestUri =
HttpUtility.UrlEncode(request.Scheme + "://" + request.Host +
request.Path); //http://localhost:43326/api/values
string requestHttpMethod = request.Method;
if (!_AllowedApps.ContainsKey(appId)) return false;
var sharedKey = _AllowedApps[appId];
if (IsReplayRequest(nonce, requestTimeStamp)) return false;
request.EnableRewind();
byte[] hash = ComputeHash(request.Body);
request.Body.Position = 0;
if (hash != null)
{
requestContentBase64String = Convert.ToBase64String(hash);
}
var data = $"{appId}{requestHttpMethod}{requestUri}{requestTimeStamp}{nonce}{requestContentBase64String}";
var secretKeyBytes = Convert.FromBase64String(sharedKey);
byte[] signature = Encoding.UTF8.GetBytes(data);
using (var hmac = new HMACSHA256(secretKeyBytes))
{
byte[] signatureBytes = hmac.ComputeHash(signature);
return incomingBase64Signature.Equals(Convert.ToBase64String(signatureBytes), StringComparison.Ordinal);
}
}
private bool IsReplayRequest(string nonce, string requestTimeStamp)
{
if (_cache.TryGetValue(nonce, out object _)) return true;
TimeSpan currentTs = DateTime.UtcNow - _1970;
var serverTotalSeconds = Convert.ToUInt64(currentTs.TotalSeconds);
var requestTotalSeconds = Convert.ToUInt64(requestTimeStamp);
if (serverTotalSeconds - requestTotalSeconds > _REQUEST_MAX_AGE_IN_SECONDS) return true;
_cache.Set(nonce, requestTimeStamp, DateTimeOffset.UtcNow.AddSeconds(_REQUEST_MAX_AGE_IN_SECONDS));
return false;
}
private static string[] GetAutherizationHeaderValues(string rawAuthzHeader)
{
var credArray = rawAuthzHeader.Split(' ')[1].Split(':');
return credArray.Length == 4 ? credArray : null;
}
private static byte[] ComputeHash(Stream body)
{
using (var md5 = MD5.Create())
{
var content = GetBytes(body);
byte[] hash = content.Length != 0
? md5.ComputeHash(content)
: null;
return hash;
}
}
private static byte[] GetBytes(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (var ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment