You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Framework/Connected.Security/Authentication/Middleware/DefaultAuthenticationMiddle...

118 lines
4.5 KiB

using Connected.Annotations;
using Connected.Configuration;
using Connected.Middleware;
using Connected.Security.Cryptography;
using Connected.Security.Identity;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Connected.Security.Authentication.Middleware;
/// <summary>
/// The default implementation of the <see cref="IAuthenticationMiddleware"/>.
/// </summary>
[Priority(0)]
internal sealed class DefaultAuthenticationMiddleware : MiddlewareComponent, IAuthenticationMiddleware
{
public DefaultAuthenticationMiddleware(IUserService userService, IRoleService roleService, IConfigurationService configurationService, ICryptographyService cryptographyService)
{
UserService = userService;
RoleService = roleService;
ConfigurationService = configurationService;
CryptographyService = cryptographyService;
}
private IUserService UserService { get; }
private IRoleService RoleService { get; }
public IConfigurationService ConfigurationService { get; }
public ICryptographyService CryptographyService { get; }
public async Task<IAuthenticationResult> Authenticate(AuthenticationArgs args)
{
if (args is BasicAuthenticationArgs basic)
return await AuthenticateBasic(basic);
else if (args is PinAuthenticationArgs pin)
return await AuthenticatePin(pin);
else if (args is BearerAuthenticationArgs bearer)
return await AuthenticateBearer(bearer);
else if (args is SsoAuthenticationArgs sso)
return await AuthenticateSso(sso);
else
throw new NotSupportedException();
}
private async Task<IAuthenticationResult> AuthenticateSso(SsoAuthenticationArgs sso)
{
if (await UserService.Resolve(new UserResolveArgs { Criteria = sso.Token }) is not IUserPassport user)
return AuthenticationResult.Fail(AuthenticationResultReason.InvalidCredentials);
return AuthenticationResult.OK(user, new JwtSecurityTokenHandler().WriteToken(CreateToken(user)));
}
private async Task<IAuthenticationResult> AuthenticateBearer(BearerAuthenticationArgs bearer)
{
throw new NotImplementedException();
}
private async Task<IAuthenticationResult> AuthenticatePin(PinAuthenticationArgs pin)
{
if (await UserService.Resolve(new UserResolveArgs { Criteria = pin.User }) is not IUserPassport user)
return AuthenticationResult.Fail(AuthenticationResultReason.NotFound);
if (Validate(user, false) is IAuthenticationResult result && !result.Success)
return result;
if (!CryptographyService.Verify(pin.Pin, user.Pin))
return AuthenticationResult.Fail(AuthenticationResultReason.InvalidCredentials);
return AuthenticationResult.OK(user, new JwtSecurityTokenHandler().WriteToken(CreateToken(user)));
}
private async Task<IAuthenticationResult> AuthenticateBasic(BasicAuthenticationArgs basic)
{
if (await UserService.Resolve(new UserResolveArgs { Criteria = basic.User }) is not IUserPassport user)
return AuthenticationResult.Fail(AuthenticationResultReason.NotFound);
if (Validate(user, true) is IAuthenticationResult result && !result.Success)
return result;
if (!CryptographyService.Verify(basic.Password, user.Password))
return AuthenticationResult.Fail(AuthenticationResultReason.InvalidPassword);
return AuthenticationResult.OK(user, new JwtSecurityTokenHandler().WriteToken(CreateToken(user)));
}
private static IAuthenticationResult? Validate(IUserPassport user, bool validatePassword)
{
if (user.Status == UserStatus.Inactive)
return AuthenticationResult.Fail(AuthenticationResultReason.Inactive);
if (user.Status == UserStatus.Locked)
return AuthenticationResult.Fail(AuthenticationResultReason.Locked);
if (validatePassword)
{
if (user.Password is null || !user.Password.Any())
return AuthenticationResult.Fail(AuthenticationResultReason.NoPassword);
if (user.PasswordExpiration != DateTime.MinValue && user.PasswordExpiration < DateTime.UtcNow)
return AuthenticationResult.Fail(AuthenticationResultReason.PasswordExpired);
}
return null;
}
private JwtSecurityToken CreateToken(IUserPassport user)
{
var config = ConfigurationService.Authentication.JwToken;
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.Key));
var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.AuthenticationToken.ToString()) };
return new JwtSecurityToken(issuer: config.Issuer, audience: config.Audience, claims: claims,
expires: DateTime.Now.AddDays(Math.Max(1, config.Duration)), signingCredentials: cred);
}
}