public async Task <IActionResult> Issue(LoginCredentials creds) { string prefix = nameof(Issue) + Constants.FNSUFFIX; string sInvalid = "Invalid Credentials - "; if (!ModelState.IsValid) { return(BadRequest(sInvalid + "(A)")); } if (creds == null) { return(BadRequest(sInvalid + "(B)")); } if (string.IsNullOrWhiteSpace(creds.UserName)) { return(BadRequest(sInvalid + "(C)")); } if (string.IsNullOrWhiteSpace(creds.Password)) { return(BadRequest(sInvalid + "(D)")); } var identity = await getClaimsIdentity(creds); // creds=un+pw if (identity == null) { _logger.LogInformation(prefix + $"Invalid username ({creds.UserName}) or password ({creds.Password})"); return(BadRequest("Invalid credentials (E)")); } string ip = AppUtility.GetRequestIP(HttpContext, true); return(await issueTokens(identity, ip)); }
public async Task <IActionResult> Refresh(string sRefreshToken) { var prefix = "Refresh() - "; string msg = ""; string sInvalid = "Invalid Refesh Token - "; if (!ModelState.IsValid) { return(BadRequest(sInvalid + "(A)")); } var handler = new JwtSecurityTokenHandler(); string secret = _configuration[Constants.SECRET_ENV_VAR] ?? "DEFAULT_SECRET_KEY"; // SECRET KEY MUST BE 16 CHARS OR MORE SymmetricSecurityKey signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secret.PadRight(16))); var tokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = _jwtOptions.Issuer, ValidateAudience = true, ValidAudience = _jwtOptions.Audience, ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, RequireExpirationTime = true, ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(_jwtOptions.RefreshClockSkew) }; SecurityToken validatedToken = null; ClaimsPrincipal claimsPrincipal = null; try { claimsPrincipal = handler.ValidateToken(sRefreshToken, tokenValidationParameters, out validatedToken); } catch (Exception ex) { msg = $"Refresh Token failed JwtSecurityTokenHandler.ValidateToken(); Token:[{sRefreshToken}]; Ex:[{ex.Message}]; Base:[{ex.GetBaseException().Message}]"; _logger.LogInformation(prefix + msg); return(BadRequest(sInvalid + "(B)")); } if (validatedToken == null) { return(BadRequest(sInvalid + "(C)")); } // OK, we have a valid refresh token. Does it have the correct custom payload? var validatedJwtSecurityToken = validatedToken as JwtSecurityToken; if (validatedJwtSecurityToken == null) { return(BadRequest(sInvalid + "(D)")); } object objPayloadGuid = null; string sPayloadGuid = ""; if (validatedJwtSecurityToken.Payload.TryGetValue(GUIDKEY, out objPayloadGuid)) { sPayloadGuid = (objPayloadGuid == null) ? "" : (string)objPayloadGuid; } if (string.IsNullOrWhiteSpace(sPayloadGuid)) { return(BadRequest(sInvalid + "(E)")); } object objPayloadName = null; string sPayloadName = ""; if (validatedJwtSecurityToken.Payload.TryGetValue(NAMEKEY, out objPayloadName)) { sPayloadName = (objPayloadName == null) ? "" : (string)objPayloadName; } if (string.IsNullOrWhiteSpace(sPayloadName)) { return(BadRequest(sInvalid + "(F)")); } object objPayloadIP = null; string sPayloadIP = ""; if (validatedJwtSecurityToken.Payload.TryGetValue(IPKEY, out objPayloadIP)) { sPayloadIP = (objPayloadIP == null) ? "" : (string)objPayloadIP; } // IP address may be empty // Payload values are now known to not be null // Check against database. var appJwtRefreshToken = await _tokenStore.ExtractByGuidAsync(sPayloadGuid, new CancellationToken()); if (!string.Equals(appJwtRefreshToken.Guid, sPayloadGuid, StringComparison.OrdinalIgnoreCase)) { return(BadRequest(sInvalid + "(G)")); } if (!string.Equals(appJwtRefreshToken.Name, sPayloadName, StringComparison.OrdinalIgnoreCase)) { return(BadRequest(sInvalid + "(H)")); } if (!string.Equals(appJwtRefreshToken.IP, sPayloadIP, StringComparison.OrdinalIgnoreCase)) { return(BadRequest(sInvalid + "(I)")); } // Payload values match those in the database. // The token has an IP address in it; The request has an IP address; The database has the issued IP address; // If the incoming request's IP address differs from the recorded IP address, the user may have legitimately // switched IP addresses, or a legitimate refresh token may have been stolen by a bad actor and they may be // trying to get access tokens using a stolen token. Force a re-authentication just in case. string ip = AppUtility.GetRequestIP(HttpContext, true); if (!string.Equals(ip, appJwtRefreshToken.IP)) { return(BadRequest(sInvalid + "(I)")); } // If valid, re-pull the claims in case the roles have changed, // or the user has been locked out for being evil, and re-issue. ApplicationUser user = _userManager.FindByNameAsync(sPayloadName).Result; if (user == null) { return(BadRequest($"User [{user.UserName}] not found")); } if (!user.Enabled) { return(BadRequest($"User [{user.UserName}] is not enabled")); } // We have an active user, who is not evil. // Re-pulling their claimsIdentity ensures that the new tokens issued have recent claims. ClaimsIdentity identity = getClaimsIdentityForAppUser(user).Result; return(await issueTokens(identity, ip)); }