Add project files.
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
namespace MyOffice.Web.Identity;
|
||||
|
||||
using MyOffice.Data.Models.Users;
|
||||
using Domain;
|
||||
using Data.Repositories.Users;
|
||||
using Repositories;
|
||||
using IdentityModel;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Security.Claims;
|
||||
using Core.Extensions;
|
||||
using Core.Identity;
|
||||
|
||||
public class ExtensionGrantValidator : IExtensionGrantValidator
|
||||
{
|
||||
private readonly IUserStore<ApplicationUser<Guid>> _userStore;
|
||||
private readonly ILogger<ExtensionGrantValidator> _logger;
|
||||
private readonly AppUserManager _appUserManager;
|
||||
private readonly IUserExternalRepository _userExternalRepository;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ExternalProvidersConfig _externalProvidersConfig;
|
||||
private readonly IEnumerable<IExternalProviderValidator> _externalProviderValidators;
|
||||
|
||||
public ExtensionGrantValidator(
|
||||
ILogger<ExtensionGrantValidator> logger,
|
||||
IUserStore<ApplicationUser<Guid>> userStore,
|
||||
AppUserManager appUserManager,
|
||||
IUserExternalRepository userExternalRepository,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IOptions<ExternalProvidersConfig> externalProvidersConfig,
|
||||
IEnumerable<IExternalProviderValidator> externalProviderValidators
|
||||
)
|
||||
{
|
||||
if (externalProvidersConfig == null)
|
||||
throw new ArgumentNullException(nameof(externalProvidersConfig));
|
||||
|
||||
_userStore = userStore ?? throw new ArgumentNullException(nameof(userStore));
|
||||
_appUserManager = appUserManager ?? throw new ArgumentNullException(nameof(appUserManager));
|
||||
_userExternalRepository =
|
||||
userExternalRepository ?? throw new ArgumentNullException(nameof(userExternalRepository));
|
||||
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
|
||||
_externalProvidersConfig = externalProvidersConfig.Value;
|
||||
_externalProviderValidators = externalProviderValidators;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public string GrantType => "external";
|
||||
|
||||
public async Task ValidateAsync(ExtensionGrantValidationContext context)
|
||||
{
|
||||
var provider = context.Request.Raw["provider"];
|
||||
if (provider == null)
|
||||
throw new ArgumentNullException("provider not found");
|
||||
|
||||
var validator = _externalProviderValidators.FirstOrDefault(x => x.Provider.EqualsIgnoreCase(provider));
|
||||
if (validator == null || !validator.IsConfigured)
|
||||
{
|
||||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest,
|
||||
$"Provider not supported: {provider}");
|
||||
return;
|
||||
}
|
||||
|
||||
var token = context.Request.Raw["token"];
|
||||
if (token == null)
|
||||
throw new ArgumentNullException("token not found");
|
||||
|
||||
var result = await validator.ValidateAsync(token);
|
||||
if (!result.IsSuccessed)
|
||||
{
|
||||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, $"Token not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
context.Result = await ProcessAsync(result.Email!, result.ExternalId!, provider, result.EmailVerified,
|
||||
result.FullName);
|
||||
}
|
||||
|
||||
private readonly object _lock = new();
|
||||
|
||||
private async Task<GrantValidationResult> ProcessAsync(
|
||||
string email,
|
||||
string externlaId,
|
||||
string provider,
|
||||
bool isEmailConfirmed,
|
||||
string? fullName
|
||||
)
|
||||
{
|
||||
// connect to exists user
|
||||
if (_httpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
return new GrantValidationResult(TokenRequestErrors.InvalidRequest, $"Authentication failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
var user = await _appUserManager.FindByEmailAsync(email);
|
||||
if (user == null)
|
||||
{
|
||||
if (!isEmailConfirmed)
|
||||
return new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Email not confirmed");
|
||||
|
||||
user = await CreateUser(email, isEmailConfirmed, fullName);
|
||||
if (user == null) return new GrantValidationResult(TokenRequestErrors.InvalidRequest);
|
||||
}
|
||||
|
||||
var claim = await _userExternalRepository.GetByUserIdAsync(user.Id, provider);
|
||||
if (claim == null) claim = AddClaim(user, email, externlaId, provider);
|
||||
|
||||
return ClaimToGrantValidationResult(claim.UserId, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ApplicationUser<Guid>?> CreateUser(string email, bool isEmailConfirmed, string? fullName)
|
||||
{
|
||||
var user = new ApplicationUser<Guid>
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserName = email,
|
||||
Email = email,
|
||||
IsEmailConfirmed = isEmailConfirmed,
|
||||
FullName = fullName
|
||||
};
|
||||
var password = "Qq1!_" + Guid.NewGuid();
|
||||
var result = await _appUserManager.CreateAsync(user, password);
|
||||
if (!result.Succeeded) return null;
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private UserExternal AddClaim(ApplicationUser<Guid> user, string email, string externalId, string provider)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var claim = _userExternalRepository.GetByUserId(user.Id, provider);
|
||||
|
||||
if (claim != null) return claim;
|
||||
|
||||
claim = new UserExternal
|
||||
{
|
||||
UserId = user.Id,
|
||||
CreatedOn = DateTime.UtcNow,
|
||||
Provider = provider.ToLower(),
|
||||
ExternalId = externalId,
|
||||
Email = email
|
||||
};
|
||||
_userExternalRepository.AddUserExternal(claim);
|
||||
|
||||
return claim;
|
||||
}
|
||||
}
|
||||
|
||||
private GrantValidationResult ClaimToGrantValidationResult(Guid userId, string provider, string email)
|
||||
{
|
||||
var userIdStr = userId.ToString();
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new(JwtClaimTypes.Id, userIdStr),
|
||||
new(JwtClaimTypes.Subject, userIdStr),
|
||||
new(JwtClaimTypes.Email, email)
|
||||
};
|
||||
|
||||
var grantValidationResult = new GrantValidationResult(userIdStr, provider, claims);
|
||||
|
||||
return grantValidationResult;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user