Safet Purkovic 3 лет назад
Родитель
Сommit
093e51fd8d

+ 30
- 0
Diligent.WebAPI.Business/Helper/HTMLHelper.cs Просмотреть файл

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Diligent.WebAPI.Business.Helper
{
public static class HTMLHelper
{
public static string RenderForgotPasswordPage(string url)
{
return "<div style=\"font-family: sans-serif\">" +
"<div style=\"font-family: sans-serif;text-align: center;\">" +
"<h2 style=\"color: #017397;\">HR Center Password Reset</h2>" +
"<p style=\"font-size: 20px\">" +
"To reset your HR Center password, please click on the button below." +
"</p>" +
"<a style = \"color: white;text-decoration:none;background-color: #017397;cursor: pointer;font-size: 20px;width: 220px;text-align: center;border-radius: 5px;padding: 5px 15px;height: 25px;\" " +
$"href=\"{url}\">" +
" RESET PASSWORD" +
"</a>" +
"<p style = \"font-size: 12px; margin-top: 25px;\" >" +
"Please do not reply to this email.This message was sent from a notification-only address that is not monitored." +
"</p>" +
"</div>" +
"</div>";
}
}
}

+ 1
- 2
Diligent.WebAPI.Business/Services/AdService.cs Просмотреть файл

@@ -1,5 +1,4 @@

namespace Diligent.WebAPI.Business.Services
namespace Diligent.WebAPI.Business.Services
{
public class AdService : IAdService
{

+ 400
- 0
Diligent.WebAPI.Business/Services/AuthenticationService.cs Просмотреть файл

@@ -0,0 +1,400 @@
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using System.Net;

namespace Diligent.WebAPI.Business.Services
{
public class AuthenticationService : IAuthenticationService
{
private readonly AuthorizationSettings _authSettings;
private readonly FrontEndSettings _frontEndSettings;
private readonly UserManager<User> _userManager;
private readonly DatabaseContext _databaseContext;
private readonly IEmailer _emailer;
private readonly ILogger<AuthenticationService> _logger;
private readonly IHttpClientService _httpClient;
public AuthenticationService(IOptions<AuthorizationSettings> authSettings,
IOptions<FrontEndSettings> frontEndSettings,
UserManager<User> userManager,
DatabaseContext databaseContext,
IEmailer emailer,
ILogger<AuthenticationService> logger,
IHttpClientService httpClient)
{
_authSettings = authSettings.Value;
_frontEndSettings = frontEndSettings.Value;
_userManager = userManager;
_databaseContext = databaseContext;
_httpClient = httpClient;
_emailer = emailer;
_logger = logger;
}
public async Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(AuthenticateRequestDto model)
{
var user = await _userManager.FindByNameAsync(model.Username);

// return null if user not found
if (user == null)
{
return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Username is not valid"
};
}

var result = await _userManager.CheckPasswordAsync(user, model.Password);

// password is not correct
if (!result)
{
await _userManager.AccessFailedAsync(user);

return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Password is not correct"
};
}

return await GenerateToken(user);
}

public async Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(GoogleApiModel model)
{

if (!(await _httpClient.IsTokenValid(model.Token)))
{
return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Invalid Google Api Token"
};
}
var user = await _userManager.FindByEmailAsync(model.User.email);

// return null if user not found
if (user == null)
{
return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = $"User with email {model.User.email} does not exist in database"
};
}
return await GenerateToken(user);
}

private async Task<ServiceResponseDTO<AuthenticateResponseDto>> GenerateToken(User user)
{
var isLocked = await _userManager.IsLockedOutAsync(user);

if (isLocked)
return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "The account is locked out"
};



// authentication successful so generate jwt token
var token = await GenerateJwtToken(user, true);

var data = new AuthenticateResponseDto
{
Id = user.Id,
Username = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
Token = token,
RefreshToken = token
};

return new ServiceResponseDTO<AuthenticateResponseDto>
{
Data = data
};
}

private async Task<string> GenerateJwtToken(User user, bool authenticate = false)
{
// generate token that is valid for 7 days
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_authSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] {
new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()),
new Claim("id", user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(_authSettings.JwtExpiredTime),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);

var writedToken = tokenHandler.WriteToken(token);

var refreshToken = new RefreshToken
{
Token = writedToken,
JwtId = user.Id.ToString(),
UserId = user.Id,
User = user,
CreationDate = DateTime.UtcNow,
ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime)
};

var existRefreshToken = await _databaseContext.RefreshTokens.Where(x => x.UserId == user.Id).FirstOrDefaultAsync();

if (existRefreshToken != null)
{
existRefreshToken.Token = writedToken;
existRefreshToken.JwtId = token.Id;
existRefreshToken.CreationDate = DateTime.UtcNow;
existRefreshToken.ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime);

if (authenticate)
{
existRefreshToken.Used = false;
existRefreshToken.Invalidated = false;
}

_databaseContext.RefreshTokens.Update(existRefreshToken);
await UpdateRefreshToken(existRefreshToken);
}
else
{
await _databaseContext.RefreshTokens.AddAsync(refreshToken);
}

await _databaseContext.SaveChangesAsync();

return writedToken;
}

public async Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model)
{
var validatedToken = GetPrincipalFromToken(model.Token, false);

if (validatedToken == null)
{
return new RefreshTokenResultDto { Error = "Invalid token" };
}

var jti = validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Jti).Value;

var storedRefreshToken = await _databaseContext.RefreshTokens.SingleOrDefaultAsync(x => x.JwtId == jti);

if (storedRefreshToken == null)
{
return new RefreshTokenResultDto { Error = "This refresh token does not exist" };
}

var userk = await _databaseContext.Users.Where(u => u.Id == storedRefreshToken.UserId).FirstOrDefaultAsync();

if (userk == null)
{
return new RefreshTokenResultDto { Error = "There is no user which is associated with refresh token" };
}

var expiryDateUnix = long.Parse(validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Exp).Value);

var expiryDateTimeUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
.AddMinutes(expiryDateUnix);

if (expiryDateTimeUtc < DateTime.UtcNow)
{
return new RefreshTokenResultDto
{
Data = new AuthenticateResponseDto
{
Id = userk.Id,
FirstName = userk.FirstName,
LastName = userk.LastName,
Username = userk.UserName,
Token = model.Token,
RefreshToken = model.RefreshToken
}
};
}

if (DateTime.UtcNow > storedRefreshToken.ExpiryDate)
{
return new RefreshTokenResultDto { Error = "This refresh token has expired" };
}

if (storedRefreshToken.Invalidated)
{
return new RefreshTokenResultDto { Error = "This refresh token has been invalidated" };
}

if (storedRefreshToken.JwtId != jti)
{
return new RefreshTokenResultDto { Error = "This refresh token does not match this JWT" };
}

storedRefreshToken.ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime);

await _databaseContext.SaveChangesAsync();

var user = await _userManager.FindByIdAsync(validatedToken.Claims.Single(x => x.Type == "id").Value);

var token = await GenerateJwtToken(user);

return new RefreshTokenResultDto
{
Data = new AuthenticateResponseDto
{
Id = userk.Id,
FirstName = userk.FirstName,
LastName = userk.LastName,
Username = userk.UserName,
Token = model.Token,
RefreshToken = model.RefreshToken
}
};
}

public async Task<ServiceResponseDTO<string>> DeleteRefreshToken(int userId)
{
var refreshToken = await _databaseContext.RefreshTokens.Where(r => r.UserId == userId).FirstOrDefaultAsync();

if (refreshToken is null)
return new ServiceResponseDTO<string>
{
IsError = true,
ErrorMessage = "There is no refresh token for user"
};

_databaseContext.RefreshTokens.Remove(refreshToken);

var result = await _databaseContext.SaveChangesAsync() > 0;

if (!result)
return new ServiceResponseDTO<string>
{
IsError = true,
ErrorMessage = "Problem with saving changes into database"
};

return new ServiceResponseDTO<string>
{
Data = null
};
}

private ClaimsPrincipal? GetPrincipalFromToken(string token, bool validateLifetime)
{
var tokenHandler = new JwtSecurityTokenHandler();

var key = Encoding.ASCII.GetBytes(_authSettings.Secret);
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = false,
ValidateLifetime = validateLifetime,
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
//ClockSkew = TimeSpan.Zero
};

try
{
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var validatedToken);
if (!IsJwtWithValidSecurityAlgorithm(validatedToken))
{
return null;
}

return principal;
}
catch (Exception)
{
return null;
}
}

private static bool IsJwtWithValidSecurityAlgorithm(SecurityToken validatedToken)
{
return (validatedToken is JwtSecurityToken jwtSecurityToken) &&
jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
StringComparison.InvariantCultureIgnoreCase);
}

public async Task<RefreshToken?> GetRefreshTokenByUserId(int userId)
{
return await _databaseContext.RefreshTokens.Where(x => x.UserId == userId).FirstOrDefaultAsync();
}

public async Task UpdateRefreshToken(RefreshToken refreshToken)
{
_databaseContext.RefreshTokens.Update(refreshToken);

await _databaseContext.SaveChangesAsync();
}

public async Task<ServiceResponseDTO<object>> GetEmailConfirmationUrlAsync(string email)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Email did not find."
};
}

var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
token = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token));
await _emailer.SendEmailAndWriteToDbAsync(email, "Reset password", HTMLHelper.RenderForgotPasswordPage($"{_frontEndSettings.BaseUrl}/reset-password?token={token}&email={email}"), isHtml: true);

user.PasswordResetToken = token;
await _databaseContext.SaveChangesAsync();
return new ServiceResponseDTO<object>
{
Data = new { code = token, email = email }
};
}

public async Task<ServiceResponseDTO<object>> PasswordResetAsync(string email, string code, string password)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Email did not find."
};
}
// FOR SOME REASON USERMANAGER.RESETPASSWORDASYNC returns InvalidToken. In future change this
//var passwordResetToken = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));

//IdentityResult resetResult = await _userManager.ResetPasswordAsync(user, passwordResetToken, password);
//if (resetResult.Succeeded)
await _userManager.RemovePasswordAsync(user);
await _userManager.AddPasswordAsync(user, password);
if (user.PasswordResetToken == code)
{
if (await _userManager.IsLockedOutAsync(user))
{
await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow);
}
return new ServiceResponseDTO<object> { Data = true };
}

//var errors = resetResult.Errors.Select(x => x.Description);
return new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Invalid reset password token"
};
}
}
}

+ 46
- 0
Diligent.WebAPI.Business/Services/HttpClientService.cs Просмотреть файл

@@ -0,0 +1,46 @@
using System.Net;

namespace Diligent.WebAPI.Business.Services
{
public class HttpClientService : IHttpClientService
{
private const string GoogleApiTokenInfoUrl = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}";
private string[] SupportedClientsIds = { "" };
private readonly AuthorizationSettings _authSettings;

public HttpClientService(IOptions<AuthorizationSettings> authSettings)
{

}
public async Task<bool> IsTokenValid(string providerToken)
{
var httpClient = new HttpClient();
var requestUri = new Uri(string.Format(GoogleApiTokenInfoUrl, providerToken));

HttpResponseMessage httpResponseMessage;
try
{
httpResponseMessage = httpClient.GetAsync(requestUri).Result;
}
catch
{
return false;
}

if (httpResponseMessage.StatusCode != HttpStatusCode.OK)
{
return false;
}

var response = httpResponseMessage.Content.ReadAsStringAsync().Result;
var googleApiTokenInfo = JsonConvert.DeserializeObject<GoogleApiTokenInfo>(response);

//if (!SupportedClientsIds.Contains(googleApiTokenInfo.aud))
if (googleApiTokenInfo.aud != _authSettings.GoogleClientId)
{
return false;
}
return true;
}
}
}

+ 21
- 0
Diligent.WebAPI.Business/Services/Interfaces/IAuthenticationService.cs Просмотреть файл

@@ -0,0 +1,21 @@
namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IAuthenticationService
{
Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(AuthenticateRequestDto model);
Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(GoogleApiModel model);

Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model);


Task<RefreshToken?> GetRefreshTokenByUserId(int userId);

Task UpdateRefreshToken(RefreshToken refreshToken);

Task<ServiceResponseDTO<string>> DeleteRefreshToken(int userId);

Task<ServiceResponseDTO<object>> GetEmailConfirmationUrlAsync(string email);

Task<ServiceResponseDTO<object>> PasswordResetAsync(string email, string code, string password);
}
}

+ 13
- 0
Diligent.WebAPI.Business/Services/Interfaces/IHttpClientService.cs Просмотреть файл

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IHttpClientService
{
Task<bool> IsTokenValid(string providedToken);
}
}

+ 1
- 15
Diligent.WebAPI.Business/Services/Interfaces/IUserService.cs Просмотреть файл

@@ -2,25 +2,11 @@
{
public interface IUserService
{
Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(AuthenticateRequestDto model);
Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(GoogleApiModel model);

Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model);

Task<IEnumerable<User?>> GetAll();

Task<User?> GetById(int id);

Task CreateUser(CreateUserRequestDto model);

Task<RefreshToken?> GetRefreshTokenByUserId(int userId);

Task UpdateRefreshToken(RefreshToken refreshToken);

Task<ServiceResponseDTO<string>> DeleteRefreshToken(int userId);

Task<ServiceResponseDTO<object>> GetEmailConfirmationUrlAsync(string email);

Task<ServiceResponseDTO<object>> PasswordResetAsync(string email, string code, string password);
}
}

+ 4
- 422
Diligent.WebAPI.Business/Services/UserService.cs Просмотреть файл

@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.WebUtilities;
using Diligent.WebAPI.Business.Services.Interfaces;
using Diligent.WebAPI.Business.Settings;
using Diligent.WebAPI.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using System.Net;

namespace Diligent.WebAPI.Business.Services
{
@@ -40,425 +42,5 @@ namespace Diligent.WebAPI.Business.Services

await _userManager.CreateAsync(user, model.Password);
}

private bool IsTokenValid(string providerToken)
{
var httpClient = new HttpClient();
var requestUri = new Uri(string.Format(GoogleApiTokenInfoUrl, providerToken));

HttpResponseMessage httpResponseMessage;
try
{
httpResponseMessage = httpClient.GetAsync(requestUri).Result;
}
catch (Exception ex)
{
return false;
}

if (httpResponseMessage.StatusCode != HttpStatusCode.OK)
{
return false;
}

var response = httpResponseMessage.Content.ReadAsStringAsync().Result;
var googleApiTokenInfo = JsonConvert.DeserializeObject<GoogleApiTokenInfo>(response);

//if (!SupportedClientsIds.Contains(googleApiTokenInfo.aud))
if(googleApiTokenInfo.aud != _authSettings.GoogleClientId)
{
return false;
}

return true;
}

public async Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(AuthenticateRequestDto model)
{
var user = await _userManager.FindByNameAsync(model.Username);

// return null if user not found
if (user == null)
{
return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Username is not valid"
};
}

var result = await _userManager.CheckPasswordAsync(user, model.Password);

// password is not correct
if (!result)
{
await _userManager.AccessFailedAsync(user);

return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Password is not correct"
};
}

return await GenerateToken(user);
}

public async Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(GoogleApiModel model)
{

if (!IsTokenValid(model.Token))
{
return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "Invalid Google Api Token"
};
}
var user = await _userManager.FindByEmailAsync(model.User.email);

// return null if user not found
if (user == null)
{
return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = $"User with email {model.User.email} does not exist in database"
};
}

return await GenerateToken(user);
}

private async Task<ServiceResponseDTO<AuthenticateResponseDto>> GenerateToken(User user)
{
var isLocked = await _userManager.IsLockedOutAsync(user);

if (isLocked)
return new ServiceResponseDTO<AuthenticateResponseDto>
{
IsError = true,
ErrorMessage = "The account is locked out"
};



// authentication successful so generate jwt token
var token = await GenerateJwtToken(user, true);

var data = new AuthenticateResponseDto
{
Id = user.Id,
Username = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
Token = token,
RefreshToken = token
};

return new ServiceResponseDTO<AuthenticateResponseDto>
{
Data = data
};
}

private async Task<string> GenerateJwtToken(User user, bool authenticate = false)
{
// generate token that is valid for 7 days
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_authSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] {
new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()),
new Claim("id", user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(_authSettings.JwtExpiredTime),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);

var writedToken = tokenHandler.WriteToken(token);

var refreshToken = new RefreshToken
{
Token = writedToken,
JwtId = user.Id.ToString(),
UserId = user.Id,
User = user,
CreationDate = DateTime.UtcNow,
ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime)
};

var existRefreshToken = await _databaseContext.RefreshTokens.Where(x => x.UserId == user.Id).FirstOrDefaultAsync();

if (existRefreshToken != null)
{
existRefreshToken.Token = writedToken;
existRefreshToken.JwtId = token.Id;
existRefreshToken.CreationDate = DateTime.UtcNow;
existRefreshToken.ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime);

if (authenticate)
{
existRefreshToken.Used = false;
existRefreshToken.Invalidated = false;
}

_databaseContext.RefreshTokens.Update(existRefreshToken);
await UpdateRefreshToken(existRefreshToken);
}
else
{
await _databaseContext.RefreshTokens.AddAsync(refreshToken);
}

await _databaseContext.SaveChangesAsync();

return writedToken;
}

public async Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model)
{
var validatedToken = GetPrincipalFromToken(model.Token, false);

if (validatedToken == null)
{
return new RefreshTokenResultDto { Error = "Invalid token" };
}

var jti = validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Jti).Value;

var storedRefreshToken = await _databaseContext.RefreshTokens.SingleOrDefaultAsync(x => x.JwtId == jti);

if (storedRefreshToken == null)
{
return new RefreshTokenResultDto { Error = "This refresh token does not exist" };
}

var userk = await _databaseContext.Users.Where(u => u.Id == storedRefreshToken.UserId).FirstOrDefaultAsync();

if (userk == null)
{
return new RefreshTokenResultDto { Error = "There is no user which is associated with refresh token" };
}

var expiryDateUnix = long.Parse(validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Exp).Value);

var expiryDateTimeUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
.AddMinutes(expiryDateUnix);

if (expiryDateTimeUtc < DateTime.UtcNow)
{
return new RefreshTokenResultDto
{
Data = new AuthenticateResponseDto
{
Id = userk.Id,
FirstName = userk.FirstName,
LastName = userk.LastName,
Username = userk.UserName,
Token = model.Token,
RefreshToken = model.RefreshToken
}
};
}

if (DateTime.UtcNow > storedRefreshToken.ExpiryDate)
{
return new RefreshTokenResultDto { Error = "This refresh token has expired" };
}

if (storedRefreshToken.Invalidated)
{
return new RefreshTokenResultDto { Error = "This refresh token has been invalidated" };
}

if (storedRefreshToken.JwtId != jti)
{
return new RefreshTokenResultDto { Error = "This refresh token does not match this JWT" };
}

storedRefreshToken.ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime);

await _databaseContext.SaveChangesAsync();

var user = await _userManager.FindByIdAsync(validatedToken.Claims.Single(x => x.Type == "id").Value);

var token = await GenerateJwtToken(user);

return new RefreshTokenResultDto
{
Data = new AuthenticateResponseDto
{
Id = userk.Id,
FirstName = userk.FirstName,
LastName = userk.LastName,
Username = userk.UserName,
Token = model.Token,
RefreshToken = model.RefreshToken
}
};
}

public async Task<ServiceResponseDTO<string>> DeleteRefreshToken(int userId)
{
var refreshToken = await _databaseContext.RefreshTokens.Where(r => r.UserId == userId).FirstOrDefaultAsync();

if (refreshToken is null)
return new ServiceResponseDTO<string>
{
IsError = true,
ErrorMessage = "There is no refresh token for user"
};

_databaseContext.RefreshTokens.Remove(refreshToken);

var result = await _databaseContext.SaveChangesAsync() > 0;

if (!result)
return new ServiceResponseDTO<string>
{
IsError = true,
ErrorMessage = "Problem with saving changes into database"
};

return new ServiceResponseDTO<string>
{
Data = null
};
}

private ClaimsPrincipal? GetPrincipalFromToken(string token, bool validateLifetime)
{
var tokenHandler = new JwtSecurityTokenHandler();

var key = Encoding.ASCII.GetBytes(_authSettings.Secret);
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = false,
ValidateLifetime = validateLifetime,
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
//ClockSkew = TimeSpan.Zero
};

try
{
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var validatedToken);
if (!IsJwtWithValidSecurityAlgorithm(validatedToken))
{
return null;
}

return principal;
}
catch (Exception)
{
return null;
}
}

private static bool IsJwtWithValidSecurityAlgorithm(SecurityToken validatedToken)
{
return (validatedToken is JwtSecurityToken jwtSecurityToken) &&
jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
StringComparison.InvariantCultureIgnoreCase);
}

public async Task<RefreshToken?> GetRefreshTokenByUserId(int userId)
{
return await _databaseContext.RefreshTokens.Where(x => x.UserId == userId).FirstOrDefaultAsync();
}

public async Task UpdateRefreshToken(RefreshToken refreshToken)
{
_databaseContext.RefreshTokens.Update(refreshToken);

await _databaseContext.SaveChangesAsync();
}

public async Task<ServiceResponseDTO<object>> GetEmailConfirmationUrlAsync(string email)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Email did not find."
};
}
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);

token = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token));

await _emailer.SendEmailAndWriteToDbAsync(email, "Reset password", renderHTMLtoString($"{_frontEndSettings.BaseUrl}/reset-password?token={token}&email={email}"), isHtml: true);

user.PasswordResetToken = token;
await _databaseContext.SaveChangesAsync();
return new ServiceResponseDTO<object>
{
Data = new { code = token, email = email }
};
}

private string renderHTMLtoString(string url)
{
return "<div style=\"font-family: sans-serif\">" +
"<div style=\"font-family: sans-serif;text-align: center;\">" +
"<h2 style=\"color: #017397;\">HR Center Password Reset</h2>" +
"<p style=\"font-size: 20px\">" +
"To reset your HR Center password, please click on the button below." +
"</p>" +
"<a style = \"color: white;text-decoration:none;background-color: #017397;cursor: pointer;font-size: 20px;width: 220px;text-align: center;border-radius: 5px;padding: 5px 15px;height: 25px;\" " +
$"href=\"{url}\">" +
" RESET PASSWORD" +
"</a>" +
"<p style = \"font-size: 12px; margin-top: 25px;\" >" +
"Please do not reply to this email.This message was sent from a notification-only address that is not monitored." +
"</p>" +
"</div>" +
"</div>";
}

public async Task<ServiceResponseDTO<object>> PasswordResetAsync(string email, string code, string password)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Email did not find."
};
}
// FOR SOME REASON USERMANAGER.RESETPASSWORDASYNC returns InvalidToken. In future change this
//var passwordResetToken = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));

//IdentityResult resetResult = await _userManager.ResetPasswordAsync(user, passwordResetToken, password);
//if (resetResult.Succeeded)
await _userManager.RemovePasswordAsync(user);
await _userManager.AddPasswordAsync(user, password);
if(user.PasswordResetToken == code)
{
if (await _userManager.IsLockedOutAsync(user))
{
await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow);
}
return new ServiceResponseDTO<object> { Data = true };
}

//var errors = resetResult.Errors.Select(x => x.Description);
return new ServiceResponseDTO<object>
{
IsError = true,
ErrorMessage = "Invalid reset password token"
};
}
}
}

+ 1
- 0
Diligent.WebAPI.Business/Usings.cs Просмотреть файл

@@ -1,5 +1,6 @@
global using Diligent.WebAPI.Business.Services.Interfaces;
global using Diligent.WebAPI.Business.Settings;
global using Diligent.WebAPI.Business.Helper;

global using Diligent.WebAPI.Data;
global using Diligent.WebAPI.Data.Entities;

+ 85
- 0
Diligent.WebAPI.Host/Controllers/V1/AuthenticationsController.cs Просмотреть файл

@@ -0,0 +1,85 @@
namespace Diligent.WebAPI.Host.Controllers.V1
{
[ApiVersion("1.0")]
[Route("v{version:apiVersion}/authentications")]
[ApiController]
public class AuthenticationsController : ControllerBase
{
private readonly IAuthenticationService _service;

public AuthenticationsController(IAuthenticationService service)
{
_service = service;
}

[HttpGet("ForgotPassword")]
public async Task<IActionResult> ForgotPassword(string email)
{
var response = await _service.GetEmailConfirmationUrlAsync(email);

if (response.IsError is true)
return BadRequest(new { message = response.ErrorMessage });

return Ok(response.Data);
}

[HttpPost("RessetPassword")]
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordModel model)
{
var response = await _service.PasswordResetAsync(email: model.Email, code: model.Code, password: model.Password);

if (response.IsError is true)
return BadRequest(new { message = response.ErrorMessage });

return Ok(response.Data);
}

[HttpPost("authenticate")]
public async Task<IActionResult> Authenticate([FromBody] AuthenticateRequestDto model)
{
var response = await _service.Authenticate(model);

if (response.IsError is true)
return BadRequest(new { message = response.ErrorMessage });

return Ok(response.Data);
}

[HttpPost("refresh")]
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequestDto model)
{
var response = await _service.RefreshTokenAsync(model);

if (response.Error != null)
{
return BadRequest(new AuthFailedResponse { Error = response.Error });
}

return Ok(response);
}

[HttpPost("logout")]
public async Task<IActionResult> Logout(int userId)
{
var response = await _service.DeleteRefreshToken(userId);

if (response.IsError)
{
return BadRequest(new { message = response.ErrorMessage });
}

return Ok();
}
[HttpPost("authenticateGoogle")]
public async Task<IActionResult> GoogleLogin(GoogleApiModel model)
{
var response = await _service.Authenticate(model);

if (response.IsError is true)
return BadRequest(new { message = response.ErrorMessage });

return Ok(response.Data);
}
}

}

+ 3
- 64
Diligent.WebAPI.Host/Controllers/V1/UsersController.cs Просмотреть файл

@@ -10,13 +10,11 @@ namespace Diligent.WebAPI.Host.Controllers.V1
{
private readonly IUserService _userService;
private readonly IMapper _mapper;
private readonly IEmailer _emailer;

public UsersController(IUserService userService, IEmailer emailer, IMapper mapper)
public UsersController(IUserService userService, IMapper mapper)
{
_userService = userService;
_mapper = mapper;
_emailer = emailer;
}

[Authorize]
@@ -26,19 +24,7 @@ namespace Diligent.WebAPI.Host.Controllers.V1
return Ok(_mapper.Map<IEnumerable<User?>, IEnumerable<UserResponseDTO>>(await _userService.GetAll()));
}

[HttpGet("ForgotPassword")]
public async Task<IActionResult> ForgotPassword(string email)
{
var result = await _userService.GetEmailConfirmationUrlAsync(email);
return Ok(result);
}
[HttpPost("RessetPassword")]
public async Task<IActionResult> ResetPassword([FromBody]ResetPasswordModel model)
{
var result = await _userService.PasswordResetAsync(email:model.Email,code: model.Code,password: model.Password);
return Ok(result);
}

[Authorize]
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] CreateUserRequestDto model)
{
@@ -46,53 +32,6 @@ namespace Diligent.WebAPI.Host.Controllers.V1

return Ok();
}

[HttpPost("authenticate")]
public async Task<IActionResult> Authenticate([FromBody] AuthenticateRequestDto model)
{
var response = await _userService.Authenticate(model);

if (response.IsError is true)
return BadRequest(new { message = response.ErrorMessage });

return Ok(response.Data);
}

[HttpPost("refresh")]
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequestDto model)
{
var response = await _userService.RefreshTokenAsync(model);

if (response.Error != null)
{
return BadRequest(new AuthFailedResponse { Error = response.Error });
}

return Ok(response);
}

[HttpPost("logout")]
public async Task<IActionResult> Logout(int userId)
{
var response = await _userService.DeleteRefreshToken(userId);

if (response.IsError)
{
return BadRequest(new { message = response.ErrorMessage });
}

return Ok();
}
[HttpPost("authenticateGoogle")]
public async Task<IActionResult> GoogleLogin(GoogleApiModel model)
{
var response = await _userService.Authenticate(model);

if (response.IsError is true)
return BadRequest(new { message = response.ErrorMessage });

return Ok(response.Data);
}
}

}

+ 0
- 11
Diligent.WebAPI.Host/Controllers/V2/UsersController.cs Просмотреть файл

@@ -18,16 +18,5 @@
{
return Ok("Hello from protected route");
}

[HttpPost("authenticate")]
public IActionResult Authenticate([FromBody] AuthenticateRequestDto model)
{
var response = _userService.Authenticate(model);

if (response == null)
return BadRequest(new { message = "Username or password is incorrect" });

return Ok(response);
}
}
}

+ 2
- 0
Diligent.WebAPI.Host/Extensions/BusinessConfigurationExtension.cs Просмотреть файл

@@ -15,6 +15,7 @@

services.AddScoped<IInsurersService, InsurersService>();
services.AddScoped<IEmailer, Emailer>();
services.AddScoped<IHttpClientService, HttpClientService>();
services.AddScoped<IInsuranceCompaniesService, InsuranceCompaniesService>();
services.AddScoped<IInsurancePoliciesService, InsurancePoliciesService>();
services.AddScoped<IWebhookSubscriptionService, WebhookSubscriptionService>();
@@ -22,6 +23,7 @@
services.AddScoped<IWebhookPublisherService, WebhookPublisherService>();
services.AddScoped<IWebhookPublisherService, WebhookPublisherService>();
services.AddScoped<IApplicantService, ApplicantService>();
services.AddScoped<IAuthenticationService, AuthenticationService>();
services.AddScoped<IAdService, AdService>();
services.AddScoped<ITechnologyService, TechnologyService>();
}

+ 7
- 7
Diligent.WebAPI.Host/Middlewares/JwtMiddleware.cs Просмотреть файл

@@ -11,17 +11,17 @@
_authSettings = authSettings.Value;
}

public async Task Invoke(HttpContext context, IUserService userService)
public async Task Invoke(HttpContext context, IAuthenticationService authService, IUserService userService)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();

if (token != null)
await AttachUserToContext(context, userService, token);
await AttachUserToContext(context, authService, userService, token);

await _next(context);
}

private async Task AttachUserToContext(HttpContext context, IUserService userService, string token)
private async Task AttachUserToContext(HttpContext context, IAuthenticationService authService,IUserService userService, string token)
{
try
{
@@ -45,7 +45,7 @@
// attach user to context on successful jwt validation
context.Items["User"] = await userService.GetById(userId);

await UpdateRefreshToken(context, userService, userId);
await UpdateRefreshToken(context, authService, userId);
}
catch
{
@@ -54,17 +54,17 @@
}
}

private async Task UpdateRefreshToken(HttpContext context, IUserService userService, int userId)
private async Task UpdateRefreshToken(HttpContext context, IAuthenticationService service, int userId)
{
var refreshToken = await userService.GetRefreshTokenByUserId(userId);
var refreshToken = await service.GetRefreshTokenByUserId(userId);

if (refreshToken == null)
return;

refreshToken.ExpiryDate = DateTime.UtcNow.AddMinutes(30);

await userService.UpdateRefreshToken(refreshToken);
await service.UpdateRefreshToken(refreshToken);
}
}
}

Загрузка…
Отмена
Сохранить