-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
741a4e6
commit 91793a0
Showing
36 changed files
with
937 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
namespace Kirel.Identity.Core.Interfaces; | ||
|
||
/// <summary> | ||
/// Interface for sms sender | ||
/// </summary> | ||
public interface ISmsSender | ||
{ | ||
/// <summary> | ||
/// Sends sms message to the given phone number | ||
/// </summary> | ||
/// <param name="text"></param> | ||
/// <param name="phoneNumber"></param> | ||
/// <returns></returns> | ||
Task SendSms(string text, string phoneNumber); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
src/Kirel.Identity.Core/Services/KirelSmsAuthenticationService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using Kirel.Identity.Core.Interfaces; | ||
using Kirel.Identity.Core.Models; | ||
using Kirel.Identity.Exceptions; | ||
using Microsoft.AspNetCore.Identity; | ||
|
||
namespace Kirel.Identity.Core.Services; | ||
|
||
/// <summary> | ||
/// Service for sms authentication | ||
/// </summary> | ||
public class KirelSmsAuthenticationService<TKey, TUser, TRole, TUserRole, TUserClaim,TRoleClaim> | ||
where TKey : IComparable, IComparable<TKey>, IEquatable<TKey> | ||
where TUser : KirelIdentityUser<TKey, TUser, TRole, TUserRole, TUserClaim, TRoleClaim> | ||
where TRole : KirelIdentityRole<TKey, TRole, TUser, TUserRole, TRoleClaim, TUserClaim> | ||
where TUserRole : KirelIdentityUserRole<TKey, TUserRole, TUser, TRole, TUserClaim, TRoleClaim> | ||
where TUserClaim : IdentityUserClaim<TKey> | ||
where TRoleClaim : IdentityRoleClaim<TKey> | ||
{ | ||
/// <summary> | ||
/// ISmsSender implementation | ||
/// </summary> | ||
protected readonly ISmsSender SmsSender; | ||
/// <summary> | ||
/// Identity user manager | ||
/// </summary> | ||
protected readonly UserManager<TUser> UserManager; | ||
|
||
/// <summary> | ||
/// Constructor for KirelSmsAuthenticationService | ||
/// </summary> | ||
/// <param name="userManager">Identity user manager</param> | ||
/// <param name="smsSender">ISmsSender implementation</param> | ||
public KirelSmsAuthenticationService(UserManager<TUser> userManager, ISmsSender smsSender) | ||
{ | ||
UserManager = userManager; | ||
SmsSender = smsSender; | ||
} | ||
|
||
/// <summary> | ||
/// Login by phone number and single-use 4-digit code | ||
/// </summary> | ||
/// <param name="phoneNumber">User phone number</param> | ||
/// <param name="code">Single-use 4-digit code</param> | ||
/// <returns></returns> | ||
public async Task<TUser> LoginByCode(string phoneNumber, string code) | ||
{ | ||
var user = UserManager.Users.FirstOrDefault(u => u.PhoneNumber == phoneNumber); | ||
if (user == null) | ||
throw new KirelNotFoundException("User with given phone number was not found"); | ||
var numberConfirmed = await UserManager.IsPhoneNumberConfirmedAsync(user); | ||
if (!numberConfirmed) | ||
throw new KirelUnauthorizedException("Your phone number is not verified"); | ||
var result = await UserManager.VerifyUserTokenAsync(user, "Code provider", "PhoneAuthentication", code); | ||
if (!result) throw new KirelUnauthorizedException("Invalid token"); | ||
return user; | ||
} | ||
|
||
/// <summary> | ||
/// Sends single-use 4-digit code to the given phone number | ||
/// </summary> | ||
/// <param name="phoneNumber">User phone number</param> | ||
public async Task SendCodeBySms(string phoneNumber) | ||
{ | ||
var user = UserManager.Users.FirstOrDefault(u => u.PhoneNumber == phoneNumber); | ||
if (user == null) | ||
throw new KirelNotFoundException("User with given phone number was not found"); | ||
|
||
if (!user.PhoneNumberConfirmed) | ||
throw new Exception("Your phone number is not verified"); | ||
var code = await UserManager.GenerateUserTokenAsync(user, "Code provider", "PhoneAuthentication"); | ||
var text = $"Please enter the following code on the login page: {code}"; | ||
|
||
await SmsSender.SendSms(text, phoneNumber); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
src/Kirel.Identity.Core/Services/KirelSmsConfirmationService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using Kirel.Identity.Core.Interfaces; | ||
using Kirel.Identity.Core.Models; | ||
using Kirel.Identity.Exceptions; | ||
using Microsoft.AspNetCore.Identity; | ||
|
||
namespace Kirel.Identity.Core.Services; | ||
|
||
/// <summary> | ||
/// Service for user phone confirmation | ||
/// </summary> | ||
/// <typeparam name="TKey"> User key type </typeparam> | ||
/// <typeparam name="TUser"> User type </typeparam> | ||
/// <typeparam name="TRole"> The role entity type </typeparam> | ||
/// <typeparam name="TUserRole"> The user role entity type </typeparam> | ||
/// <typeparam name="TUserClaim"> User claim type</typeparam> | ||
/// <typeparam name="TRoleClaim"> Role claim type</typeparam> | ||
public class KirelSmsConfirmationService<TKey, TUser, TRole, TUserRole, TUserClaim, TRoleClaim> | ||
where TKey : IComparable, IComparable<TKey>, IEquatable<TKey> | ||
where TUser : KirelIdentityUser<TKey, TUser, TRole, TUserRole, TUserClaim, TRoleClaim> | ||
where TRole : KirelIdentityRole<TKey, TRole, TUser, TUserRole, TRoleClaim, TUserClaim> | ||
where TUserRole : KirelIdentityUserRole<TKey, TUserRole, TUser, TRole, TUserClaim, TRoleClaim> | ||
where TUserClaim : IdentityUserClaim<TKey> | ||
where TRoleClaim : IdentityRoleClaim<TKey> | ||
{ | ||
/// <summary> | ||
/// Identity user manager | ||
/// </summary> | ||
protected readonly UserManager<TUser> UserManager; | ||
/// <summary> | ||
/// Implementation of the ISmsSender interface | ||
/// </summary> | ||
protected readonly ISmsSender SmsSender; | ||
|
||
/// <summary> | ||
/// Constructor for KirelSmsConfirmationService | ||
/// </summary> | ||
/// <param name="userManager">Identity user manager</param> | ||
/// <param name="smsSender">Implementation of the ISmsSender interface</param> | ||
public KirelSmsConfirmationService(UserManager<TUser> userManager, ISmsSender smsSender) | ||
{ | ||
UserManager = userManager; | ||
SmsSender = smsSender; | ||
} | ||
|
||
/// <summary> | ||
/// Sends confirmation code to the user non confirmed phone number | ||
/// </summary> | ||
/// <param name="user">Identity user</param> | ||
/// <exception cref="KirelUnauthorizedException">If user phone number already confirmed</exception> | ||
public async Task SendConfirmationSms(TUser user) | ||
{ | ||
if (user.PhoneNumberConfirmed) | ||
throw new KirelUnauthorizedException("Your phone number has already been confirmed"); | ||
var token = await UserManager.GenerateUserTokenAsync(user, "Code provider", "PhoneConfirmation"); | ||
var text = $"Please enter the following code on the login page: {token}"; | ||
|
||
await SmsSender.SendSms(text, user.PhoneNumber); | ||
} | ||
|
||
/// <summary> | ||
/// Confirms user phone number by validating given code | ||
/// </summary> | ||
/// <param name="user">Identity user</param> | ||
/// <param name="code">Confirmation code</param> | ||
/// <exception cref="KirelUnauthorizedException">If given code is invalid</exception> | ||
/// <exception cref="KirelIdentityStoreException">If identity store failed to update user</exception> | ||
public async Task ConfirmPhoneNumber(TUser user, string code) | ||
{ | ||
var codeValid = await UserManager.VerifyUserTokenAsync(user, "Code provider", "PhoneConfirmation", code); | ||
if (!codeValid) | ||
throw new KirelUnauthorizedException("Invalid code"); | ||
|
||
user.PhoneNumberConfirmed = true; | ||
var result = await UserManager.UpdateAsync(user); | ||
if (!result.Succeeded) | ||
throw new KirelIdentityStoreException("Failed to update user field 'PhoneNumberConfirmed'"); | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
src/Kirel.Identity.Core/Validators/KirelUserAuthenticationDtoValidator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using FluentValidation; | ||
using Kirel.Identity.Core.Models; | ||
using Kirel.Identity.DTOs; | ||
using Microsoft.AspNetCore.Identity; | ||
|
||
namespace Kirel.Identity.Core.Validators; | ||
|
||
/// <summary> | ||
/// Validation for KirelUserAuthenticationDtoValidator | ||
/// </summary> | ||
public class KirelUserAuthenticationDtoValidator<TKey, TUser, TRole, TUserRole, TUserAuthenticationDto, TUserClaim, TRoleClaim> : AbstractValidator<TUserAuthenticationDto> | ||
where TKey : IComparable, IComparable<TKey>, IEquatable<TKey> | ||
where TUser : KirelIdentityUser<TKey, TUser, TRole, TUserRole, TUserClaim, TRoleClaim> | ||
where TRole : KirelIdentityRole<TKey, TRole, TUser, TUserRole, TRoleClaim, TUserClaim> | ||
where TUserRole : KirelIdentityUserRole<TKey, TUserRole, TUser, TRole, TUserClaim, TRoleClaim> | ||
where TUserAuthenticationDto : KirelUserAuthenticationDto | ||
where TUserClaim : IdentityUserClaim<TKey> | ||
where TRoleClaim : IdentityRoleClaim<TKey> | ||
{ | ||
private readonly UserManager<TUser> _userManager; | ||
|
||
/// <summary> | ||
/// Constructor for KirelUserAuthenticationDtoValidator | ||
/// </summary> | ||
/// <param name="userManager">Identity user manager</param> | ||
public KirelUserAuthenticationDtoValidator(UserManager<TUser> userManager) | ||
{ | ||
_userManager = userManager; | ||
|
||
var message = ""; | ||
var typeValues = new List<string>{ "username", "phone", "email" }; | ||
RuleFor(d => d.Type.ToLower()) | ||
.Must(type => typeValues.Contains(type)) | ||
.WithMessage($"Authentication type can only be one of the following: {string.Join(",", typeValues)}"); | ||
When(d => d.Type.ToLower() == "username", () => | ||
{ | ||
RuleFor(d => d.Login) | ||
.MinimumLength(4).WithMessage("The username must be at least 4 characters long.") | ||
.Matches(@"^(?=.*[a-zA-Z]{1,})(?=.*[\d]{0,})[a-zA-Z0-9.]{4,20}$") | ||
.WithMessage("Username can only contains letters, numbers and dots"); | ||
}); | ||
When(d => d.Type.ToLower() == "phone", () => | ||
{ | ||
RuleFor(d => d.Login) | ||
.Matches("^[1-9][0-9]{10,12}$") | ||
.WithMessage("The phone number must be in international format: 11 to 13 digits without a plus") | ||
.Must((dto, _) => PhoneConfirmed(dto.Login, out message)) | ||
.WithMessage(_ => message); | ||
}); | ||
When(d => d.Type.ToLower() == "email", () => | ||
{ | ||
RuleFor(d => d.Login) | ||
.EmailAddress().WithMessage("The email address is invalid") | ||
.Must((dto, _) => EmailConfirmed(dto.Login, out message)) | ||
.WithMessage(_ => message); | ||
}); | ||
} | ||
|
||
private bool PhoneConfirmed(string phone, out string message) | ||
{ | ||
message = ""; | ||
var user = _userManager.Users.FirstOrDefault(u => u.PhoneNumber == phone); | ||
if (user == null) return false; | ||
if (!user.PhoneNumberConfirmed) | ||
message = "To use phone authentication, you must confirm your phone number"; | ||
return user.PhoneNumberConfirmed; | ||
} | ||
|
||
private bool EmailConfirmed(string email, out string message) | ||
{ | ||
message = ""; | ||
var user = _userManager.Users.FirstOrDefault(u => u.Email == email); | ||
if (user == null) return false; | ||
if (!user.EmailConfirmed) | ||
message = "To use email authentication, you must confirm your email"; | ||
return user.EmailConfirmed; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
namespace Kirel.Identity.DTOs; | ||
|
||
/// <summary> | ||
/// Dto for user authentication | ||
/// </summary> | ||
public class KirelUserAuthenticationDto | ||
{ | ||
/// <summary> | ||
/// Login type can be one of the following: 'Username', 'Phone' or 'Email'. | ||
/// </summary> | ||
public string Type { get; set; } = ""; | ||
/// <summary> | ||
/// User login can be one of the following: Username, Phone number or Email. | ||
/// For authentication by number or email those must be confirmed | ||
/// </summary> | ||
public string Login { get; set; } = ""; | ||
/// <summary> | ||
/// User password or token from phone/email based on authentication type | ||
/// </summary> | ||
public string Password { get; set; } = ""; | ||
} |
Oops, something went wrong.