using System; using System.Configuration; using System.IdentityModel.Tokens; using System.Linq; using System.Net; using System.Net.Http; using System.Security.Claims; using System.ServiceModel.Security.Tokens; using System.Threading; using System.Threading.Tasks; using iiie.Authentication.DBO; using iiie.Logs.DataAccess; using iiie.Logs.DBO; namespace iiie.Authentication.Business.JWT { /// <summary> /// Handler for token authentication /// </summary> public abstract class TokenValidationHandler : DelegatingHandler { /// <summary> /// Gets the token from the HTTP AUthorization header /// </summary> /// <param name="request">The HTTP request</param> /// <param name="token">The variable to store the token</param> /// <returns>True if the token has been found, false otherwise</returns> private static bool TryRetrieveToken(HttpRequestMessage request, out string token) { token = null; var auth = request.Headers.Authorization; if (auth == null || auth.Scheme != "Bearer") return false; token = auth.Parameter; return true; } /// <summary> /// Contructs a user dbo from the specified username and salt /// </summary> /// <param name="username">The username of the verified token</param> /// <param name="salt">The salt in the token</param> /// <returns>The user dbo, or null if user is not valid</returns> protected abstract BasicUserDbo GetUserDbo(string username, string salt); /// <summary> /// Attempts to verify user token /// </summary> /// <param name="request">The HTTP request</param> /// <param name="cancellationToken">Token used for cancelation</param> /// <returns>The HTTP response</returns> protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpStatusCode statusCode; string token; if (!TryRetrieveToken(request, out token)) { return base.SendAsync(request, cancellationToken); } try { JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler { Configuration = new SecurityTokenHandlerConfiguration() { MaxClockSkew = new TimeSpan(0, 1, 0) } }; var stringValidator = ConfigurationManager.AppSettings["StringValidator"]; TokenValidationParameters validationParameters = new TokenValidationParameters() { RequireSignedTokens = true, RequireExpirationTime = true, ValidAudience = stringValidator, ValidateIssuerSigningKey = true, ValidIssuer = "urn:" + stringValidator, IssuerSigningToken = new BinarySecretSecurityToken(Convert.FromBase64String(ConfigurationManager.AppSettings["CredentialKey"])) }; SecurityToken validateToken; ClaimsPrincipal claim = tokenHandler.ValidateToken(token, validationParameters, out validateToken); Thread.CurrentPrincipal = claim; var name = ((ClaimsIdentity)claim.Identity).Claims.FirstOrDefault(x => x.Type == ClaimTypes.Name); var salt = ((ClaimsIdentity)claim.Identity).Claims.FirstOrDefault(x => x.Type == ClaimTypes.Authentication); if (name == null || salt == null) { OpResult<bool>.Error(ResultStatus.LoginError, string.Format("Incomplete token; username is present: {0}; salt is present {1}", name != null, salt != null)).Log(); statusCode = HttpStatusCode.Unauthorized; } else { var user = GetUserDbo(name.Value, salt.Value); if (user == null) { OpResult<bool>.Error(ResultStatus.LoginError, string.Format("Username {0} not found", name.Value)).Log(); statusCode = HttpStatusCode.Unauthorized; } else { UserStorage.BasicUserDbo = user; return base.SendAsync(request, cancellationToken); } } } catch (Exception e) { OpResult<bool>.Error(ResultStatus.LoginError, e).Log(); statusCode = HttpStatusCode.Unauthorized; } return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode), cancellationToken); } } }