362 lines
12 KiB
C#
362 lines
12 KiB
C#
using Microsoft.IdentityModel.Tokens;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Linq;
|
|
using System.Security.Claims;
|
|
using System.Text;
|
|
|
|
namespace HRD.LDAPService.JWT
|
|
{
|
|
public class JwtManager
|
|
|
|
{
|
|
private const string GlbExtendedAttributes = "ExtendedAttributes_";
|
|
|
|
private readonly LdapAuthenticationService _ldapAuthService;
|
|
|
|
public JwtManager(LdapAuthenticationService ldapAuthService)
|
|
{
|
|
_ldapAuthService = ldapAuthService;
|
|
}
|
|
|
|
public static LdapUser DecryptTokenAsLdapUser(string token)
|
|
{
|
|
if (string.IsNullOrEmpty(token)) { return default; }
|
|
//Check token with "Bearer" prefix
|
|
if (token.StartsWith("Bearer", StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
token = token.Split(" ").Last();
|
|
}
|
|
if (string.IsNullOrEmpty(token)) { return default; }
|
|
|
|
JwtSecurityToken jwtSecurityToken = DecryptToken(token);
|
|
if (jwtSecurityToken == null) { return default; }
|
|
|
|
LdapUser ldapUser = ClaimsIdentityToLdapUser(jwtSecurityToken.Claims.ToList());
|
|
if (ldapUser == null)
|
|
{
|
|
return default;
|
|
}
|
|
|
|
ldapUser.Token = token;
|
|
|
|
return ldapUser;
|
|
}
|
|
|
|
public static JwtSecurityToken DecryptToken(string token)
|
|
{
|
|
var tokenHandler = new JwtSecurityTokenHandler();
|
|
var key = Encoding.ASCII.GetBytes(JwtTokenConfig.Secret);
|
|
try
|
|
{
|
|
tokenHandler.ValidateToken(token, new TokenValidationParameters
|
|
{
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey = new SymmetricSecurityKey(key),
|
|
ValidateIssuer = false,
|
|
ValidateAudience = false,
|
|
ClockSkew = TimeSpan.FromSeconds(60) // set clockskew to zero so tokens expire exactly at token expiration time
|
|
}, out SecurityToken validatedToken);
|
|
|
|
var jwtToken = (JwtSecurityToken)validatedToken;
|
|
return jwtToken;
|
|
}
|
|
//IDX10223: Lifetime validation failed. The token is expired. ValidTo: 'System.DateTime', Current time: 'System.DateTime'.
|
|
catch (SecurityTokenExpiredException ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// return null if validation fails
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
public LdapUser RenewLdapUserWithJwtToken(string token)
|
|
{
|
|
LdapUser renewLdapUser = null;
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(token))
|
|
{
|
|
throw new ArgumentNullException($"Token is missing!");
|
|
}
|
|
|
|
renewLdapUser = _ldapAuthService.RenewIdentity(token);
|
|
if (renewLdapUser is null)
|
|
{
|
|
throw new Exception($"Can't renew from token!");
|
|
}
|
|
|
|
if (!renewLdapUser.IsValidatCredentials)
|
|
{
|
|
throw new Exception($"Invalid credentials!");
|
|
}
|
|
|
|
if (!renewLdapUser.Enabled)
|
|
{
|
|
throw new Exception($"Ldap-User is disabled!");
|
|
}
|
|
|
|
(string newtoken, DateTime newExpiredOn) = CreateToken(renewLdapUser);
|
|
renewLdapUser.Token = newtoken;
|
|
renewLdapUser.JwtExpiredOn = newExpiredOn;
|
|
|
|
if (!renewLdapUser.IsValid())
|
|
{
|
|
throw new Exception($"Ldapuser is not valid!");
|
|
}
|
|
|
|
return renewLdapUser;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
public LdapUser RenewLdapUserWithJwtToken(LdapUser ldapUser)
|
|
{
|
|
LdapUser renewLdapUser = null;
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(ldapUser?.Token))
|
|
{
|
|
throw new Exception($"Token is missing (Login:{ldapUser.LoginName})");
|
|
}
|
|
|
|
renewLdapUser = _ldapAuthService.RenewIdentity(ldapUser);
|
|
if (renewLdapUser is null)
|
|
{
|
|
return default;
|
|
}
|
|
|
|
//if (!ldapUser.IsValidatCredentials)
|
|
//{
|
|
// ldapUser.Token = string.Empty;
|
|
// return false;
|
|
//}
|
|
|
|
if (!renewLdapUser.Enabled)
|
|
{
|
|
renewLdapUser.Token = string.Empty;
|
|
return renewLdapUser;
|
|
}
|
|
|
|
var tokenHandler = new JwtSecurityTokenHandler();
|
|
var key = Encoding.ASCII.GetBytes(JwtTokenConfig.Secret);
|
|
var claims = CreateClaimsIdentity(renewLdapUser);
|
|
|
|
var tokenDescriptor = new SecurityTokenDescriptor
|
|
{
|
|
Subject = claims,
|
|
Expires = DateTime.UtcNow.AddMinutes(JwtTokenConfig.ExpirationInMin),
|
|
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
|
};
|
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
|
ldapUser.Token = tokenHandler.WriteToken(token);
|
|
ldapUser.JwtExpiredOn = token.ValidTo;
|
|
if (renewLdapUser.IsValid())
|
|
{
|
|
return renewLdapUser;
|
|
}
|
|
|
|
return default;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
public bool GenerateLdapUserWithJwtToken(LdapUser ldapUser)
|
|
{
|
|
try
|
|
{
|
|
if (!_ldapAuthService.CheckAndUpdateIdentityWithPassword(ldapUser))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ldapUser.IsValidatCredentials)
|
|
{
|
|
ldapUser.Token = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
if (!ldapUser.Enabled)
|
|
{
|
|
ldapUser.Token = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
(string token, DateTime jwtExpiredOn) = CreateToken(ldapUser);
|
|
ldapUser.Token = token;
|
|
ldapUser.JwtExpiredOn = jwtExpiredOn;
|
|
return ldapUser.IsValid();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
public static string GenerateHash(string password)
|
|
{
|
|
return JWTCrypt.SHA512(password);
|
|
}
|
|
|
|
private static (string, DateTime) CreateToken(LdapUser ldapUser)
|
|
{
|
|
var tokenHandler = new JwtSecurityTokenHandler();
|
|
var key = Encoding.ASCII.GetBytes(JwtTokenConfig.Secret);
|
|
var claims = CreateClaimsIdentity(ldapUser);
|
|
|
|
var tokenDescriptor = new SecurityTokenDescriptor
|
|
{
|
|
Subject = claims,
|
|
Expires = DateTime.UtcNow.AddMinutes(JwtTokenConfig.ExpirationInMin),
|
|
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
|
};
|
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
|
return (tokenHandler.WriteToken(token), token.ValidTo);
|
|
}
|
|
|
|
public static bool IsValidatJwtTokenSubject(string token)
|
|
{
|
|
if (string.IsNullOrEmpty(token))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
token = token.Trim();
|
|
|
|
if (token.IndexOf(" ", StringComparison.InvariantCultureIgnoreCase) > 0)
|
|
{
|
|
if (token.StartsWith("Bearer", StringComparison.InvariantCultureIgnoreCase)) //token with "Bearer" prefix
|
|
{
|
|
token = token.Split(" ").Last();
|
|
}
|
|
else
|
|
{
|
|
token = token.Split(" ").First();
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
var jwtToken = DecryptToken(token);
|
|
return !String.IsNullOrEmpty(jwtToken?.Subject); //Loginname
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
private static LdapUser ClaimsIdentityToLdapUser(List<Claim> claims)
|
|
{
|
|
LdapUser user = new LdapUser("");
|
|
foreach (var claim in claims)
|
|
{
|
|
switch (claim.Type)
|
|
{
|
|
case JwtRegisteredClaimNames.Sub:
|
|
user.LoginName = claim.Value;
|
|
break;
|
|
|
|
case JwtRegisteredClaimNames.Email:
|
|
user.Email = claim.Value;
|
|
break;
|
|
|
|
case JwtRegisteredClaimNames.NameId:
|
|
{
|
|
if (int.TryParse(claim.Value, out int id))
|
|
{
|
|
user.UserId = id;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JwtRegisteredClaimNames.Jti:
|
|
{
|
|
if (Guid.TryParse(claim.Value, out Guid g))
|
|
{
|
|
user.LdapGuid = g;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JwtGlobals.CLAIM_DEPARTNENTID:
|
|
user.DepartmentId = int.Parse(claim.Value);
|
|
break;
|
|
|
|
case JwtGlobals.CLAIM_EXTENDETDEPARTNENTIDLIST:
|
|
user.ExtendedDepartmentIdList = claim.Value;
|
|
break;
|
|
|
|
case JwtGlobals.CLAIM_ROLE:
|
|
user.AddRole(claim.Value);
|
|
break;
|
|
|
|
case JwtRegisteredClaimNames.Exp:
|
|
{
|
|
//#pragma warning disable CA1305 // Specify IFormatProvider
|
|
var expValue = Convert.ToInt32(claim.Value);
|
|
//#pragma warning restore CA1305 // Specify IFormatProvider
|
|
DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds(expValue);
|
|
user.JwtExpiredOn = dateTimeOffset.UtcDateTime;
|
|
}
|
|
break;
|
|
|
|
case var s when claim.Type.StartsWith(GlbExtendedAttributes):
|
|
var strKey = claim.Type.Substring(GlbExtendedAttributes.Length, claim.Type.Length - GlbExtendedAttributes.Length);
|
|
user.ExtendedAttributesList.Add(new KeyValuePair<string, string>(strKey, claim.Value));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
private static ClaimsIdentity CreateClaimsIdentity(LdapUser user)
|
|
{
|
|
ClaimsIdentity claimsIdentity = new ClaimsIdentity();
|
|
|
|
List<Claim> claims = new List<Claim>
|
|
{
|
|
CreateClaim(JwtRegisteredClaimNames.Sub, user.LoginName),
|
|
CreateClaim(JwtRegisteredClaimNames.NameId, user.UserId),
|
|
CreateClaim(JwtRegisteredClaimNames.Email, user.Email),
|
|
CreateClaim(JwtGlobals.CLAIM_DEPARTNENTID, user.DepartmentId),
|
|
CreateClaim(JwtGlobals.CLAIM_EXTENDETDEPARTNENTIDLIST, user.ExtendedDepartmentIdList)
|
|
};
|
|
|
|
user.RoleList.ForEach(role => claims.Add(
|
|
CreateClaim(ClaimTypes.Role, role.Role)
|
|
));
|
|
|
|
user.ExtendedAttributesList.ForEach(item => claims.Add(
|
|
CreateClaim($"{GlbExtendedAttributes}{item.Key}", item.Value)
|
|
));
|
|
|
|
claimsIdentity.AddClaims(claims);
|
|
return claimsIdentity;
|
|
}
|
|
|
|
private static Claim CreateClaim(string claimName, int claimValue)
|
|
{
|
|
return new Claim(claimName, string.IsNullOrEmpty($"{claimValue}") ? string.Empty : $"{claimValue}");
|
|
}
|
|
|
|
private static Claim CreateClaim(string claimName, string claimValue)
|
|
{
|
|
return new Claim(claimName, string.IsNullOrEmpty(claimValue) ? string.Empty : claimValue);
|
|
}
|
|
}
|
|
} |