|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|