Browse Source

Merge branch 'Password-Flow-TwoFactor' of https://github.com/abpframework/abp into Password-Flow-TwoFactor

pull/9176/head
mehmet-erim 5 years ago
parent
commit
cc36c877de
  1. 1
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json
  2. 1
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json
  3. 1
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json
  4. 224
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs

1
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json

@ -9,6 +9,7 @@
"InvalidUserNameOrPassword": "Invalid username or password!", "InvalidUserNameOrPassword": "Invalid username or password!",
"LoginIsNotAllowed": "You are not allowed to login! You need to confirm your email/phone number.", "LoginIsNotAllowed": "You are not allowed to login! You need to confirm your email/phone number.",
"InvalidUsername": "Invalid username or password!", "InvalidUsername": "Invalid username or password!",
"InvalidAuthenticatorCode": "Invalid authenticator code!",
"TheTargetUserIsNotLinkedToYou": "The target user is not linked to you!" "TheTargetUserIsNotLinkedToYou": "The target user is not linked to you!"
} }
} }

1
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json

@ -8,6 +8,7 @@
"InvalidUserNameOrPassword": "Kullanıcı adı ya da şifre geçersiz!", "InvalidUserNameOrPassword": "Kullanıcı adı ya da şifre geçersiz!",
"LoginIsNotAllowed": "Giriş yapamazsınız! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.", "LoginIsNotAllowed": "Giriş yapamazsınız! E-posta adresinizi ya da telefon numaranızı doğrulamanız gerekiyor.",
"InvalidUsername": "Kullanıcı adı ya da şifre geçersiz!", "InvalidUsername": "Kullanıcı adı ya da şifre geçersiz!",
"InvalidAuthenticatorCode": "Geçersiz kimlik doğrulama kodu!",
"TheTargetUserIsNotLinkedToYou": "Hedef kullanıcı sizinle bağlantılı değil!" "TheTargetUserIsNotLinkedToYou": "Hedef kullanıcı sizinle bağlantılı değil!"
} }
} }

1
modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json

@ -9,6 +9,7 @@
"InvalidUserNameOrPassword": "用户名或密码错误!", "InvalidUserNameOrPassword": "用户名或密码错误!",
"LoginIsNotAllowed": "无法登录!你需要验证邮箱地址/手机号.", "LoginIsNotAllowed": "无法登录!你需要验证邮箱地址/手机号.",
"InvalidUsername": "用户名或密码错误!", "InvalidUsername": "用户名或密码错误!",
"InvalidAuthenticatorCode": "验证码无效!",
"TheTargetUserIsNotLinkedToYou": "目标用户未和你有关联!" "TheTargetUserIsNotLinkedToYou": "目标用户未和你有关联!"
} }
} }

224
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs

@ -64,141 +64,160 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
[UnitOfWork] [UnitOfWork]
public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{ {
var clientId = context.Request?.Client?.ClientId; using (var scope = ServiceScopeFactory.CreateScope())
using var scope = ServiceScopeFactory.CreateScope();
await ReplaceEmailToUsernameOfInputIfNeeds(context);
IdentityUser user = null;
async Task SetSuccessResultAsync()
{ {
var sub = await UserManager.GetUserIdAsync(user); await ReplaceEmailToUsernameOfInputIfNeeds(context);
Logger.LogInformation("Credentials validated for username: {username}", context.UserName); IdentityUser user = null;
var additionalClaims = new List<Claim>(); if (AbpIdentityOptions.ExternalLoginProviders.Any())
{
await AddCustomClaimsAsync(additionalClaims, user, context); foreach (var externalLoginProviderInfo in AbpIdentityOptions.ExternalLoginProviders.Values)
{
var externalLoginProvider = (IExternalLoginProvider) scope.ServiceProvider
.GetRequiredService(externalLoginProviderInfo.Type);
context.Result = new GrantValidationResult( if (await externalLoginProvider.TryAuthenticateAsync(context.UserName, context.Password))
sub, {
OidcConstants.AuthenticationMethods.Password, user = await UserManager.FindByNameAsync(context.UserName);
additionalClaims.ToArray() if (user == null)
); {
user = await externalLoginProvider.CreateUserAsync(context.UserName, externalLoginProviderInfo.Name);
}
else
{
await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name);
}
await IdentitySecurityLogManager.SaveAsync( await SetSuccessResultAsync(context, user);
new IdentitySecurityLogContext return;
{ }
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = IdentityServerSecurityLogActionConsts.LoginSucceeded,
UserName = context.UserName,
ClientId = clientId
} }
); }
}
if (AbpIdentityOptions.ExternalLoginProviders.Any()) user = await UserManager.FindByNameAsync(context.UserName);
{ string errorDescription;
foreach (var externalLoginProviderInfo in AbpIdentityOptions.ExternalLoginProviders.Values) if (user != null)
{ {
var externalLoginProvider = (IExternalLoginProvider) scope.ServiceProvider await IdentityOptions.SetAsync();
.GetRequiredService(externalLoginProviderInfo.Type); var result = await SignInManager.CheckPasswordSignInAsync(user, context.Password, true);
if (result.Succeeded)
if (await externalLoginProvider.TryAuthenticateAsync(context.UserName, context.Password))
{ {
user = await UserManager.FindByNameAsync(context.UserName); if (await IsTfaEnabledAsync(user))
if (user == null)
{ {
user = await externalLoginProvider.CreateUserAsync(context.UserName, externalLoginProviderInfo.Name); await HandleTwoFactorLoginAsync(context, user);
} }
else else
{ {
await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name); await SetSuccessResultAsync(context, user);
} }
await SetSuccessResultAsync();
return; return;
} }
}
}
user = await UserManager.FindByNameAsync(context.UserName); if (result.IsLockedOut)
string errorDescription;
if (user != null)
{
await IdentityOptions.SetAsync();
var result = await SignInManager.CheckPasswordSignInAsync(user, context.Password, true);
if (result.Succeeded)
{
if (await IsTfaEnabled(user))
{ {
var twoFactorProvider = context.Request?.Raw?["TwoFactorProvider"]; Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", context.UserName);
var twoFactorCode = context.Request?.Raw?["TwoFactorCode"]; errorDescription = Localizer["UserLockedOut"];
if (!twoFactorProvider.IsNullOrWhiteSpace() && !twoFactorCode.IsNullOrWhiteSpace()) }
{ else if (result.IsNotAllowed)
var providers = await UserManager.GetValidTwoFactorProvidersAsync(user); {
if (providers.Contains(twoFactorProvider) && Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName);
await UserManager.VerifyTwoFactorTokenAsync(user, twoFactorProvider, twoFactorCode)) errorDescription = Localizer["LoginIsNotAllowed"];
{ }
await SetSuccessResultAsync(); else
return; {
} Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", context.UserName);
} errorDescription = Localizer["InvalidUserNameOrPassword"];
Logger.LogInformation("Authentication failed for username: {username}, reason: RequiresTwoFactor", context.UserName);
var twoFactorToken = await UserManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, nameof(SignInResult.RequiresTwoFactor));
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, nameof(SignInResult.RequiresTwoFactor),
new Dictionary<string, object>()
{
{"userId", user.Id},
{"twoFactorToken", twoFactorToken}
});
return;
} }
await SetSuccessResultAsync(); await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
return; {
} Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
else if (result.IsLockedOut) Action = result.ToIdentitySecurityLogAction(),
{ UserName = context.UserName,
Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", context.UserName); ClientId = await FindClientIdAsync(context)
errorDescription = Localizer["UserLockedOut"]; });
}
else if (result.IsNotAllowed)
{
Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName);
errorDescription = Localizer["LoginIsNotAllowed"];
} }
else else
{ {
Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", context.UserName); Logger.LogInformation("No user found matching username: {username}", context.UserName);
errorDescription = Localizer["InvalidUserNameOrPassword"]; errorDescription = Localizer["InvalidUsername"];
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
{
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = IdentityServerSecurityLogActionConsts.LoginInvalidUserName,
UserName = context.UserName,
ClientId = await FindClientIdAsync(context)
});
} }
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, errorDescription);
}
}
protected virtual async Task HandleTwoFactorLoginAsync(ResourceOwnerPasswordValidationContext context, IdentityUser user)
{
var twoFactorProvider = context.Request?.Raw?["TwoFactorProvider"];
var twoFactorCode = context.Request?.Raw?["TwoFactorCode"];
if (!twoFactorProvider.IsNullOrWhiteSpace() && !twoFactorCode.IsNullOrWhiteSpace())
{
var providers = await UserManager.GetValidTwoFactorProvidersAsync(user);
if (providers.Contains(twoFactorProvider) && await UserManager.VerifyTwoFactorTokenAsync(user, twoFactorProvider, twoFactorCode))
{ {
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, await SetSuccessResultAsync(context, user);
Action = result.ToIdentitySecurityLogAction(), return;
UserName = context.UserName, }
ClientId = clientId
}); Logger.LogInformation("Authentication failed for username: {username}, reason: InvalidAuthenticatorCode", context.UserName);
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, Localizer["InvalidAuthenticatorCode"]);
} }
else else
{ {
Logger.LogInformation("No user found matching username: {username}", context.UserName); Logger.LogInformation("Authentication failed for username: {username}, reason: RequiresTwoFactor", context.UserName);
errorDescription = Localizer["InvalidUsername"]; var twoFactorToken = await UserManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, nameof(SignInResult.RequiresTwoFactor));
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, nameof(SignInResult.RequiresTwoFactor),
new Dictionary<string, object>()
{
{"userId", user.Id},
{"twoFactorToken", twoFactorToken}
});
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
{ {
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = IdentityServerSecurityLogActionConsts.LoginInvalidUserName, Action = IdentityServerSecurityLogActionConsts.LoginRequiresTwoFactor,
UserName = context.UserName, UserName = context.UserName,
ClientId = clientId ClientId = await FindClientIdAsync(context)
}); });
} }
}
protected virtual async Task SetSuccessResultAsync(ResourceOwnerPasswordValidationContext context, IdentityUser user)
{
var sub = await UserManager.GetUserIdAsync(user);
Logger.LogInformation("Credentials validated for username: {username}", context.UserName);
var additionalClaims = new List<Claim>();
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, errorDescription); await AddCustomClaimsAsync(additionalClaims, user, context);
context.Result = new GrantValidationResult(
sub,
OidcConstants.AuthenticationMethods.Password,
additionalClaims.ToArray()
);
await IdentitySecurityLogManager.SaveAsync(
new IdentitySecurityLogContext
{
Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer,
Action = IdentityServerSecurityLogActionConsts.LoginSucceeded,
UserName = context.UserName,
ClientId = await FindClientIdAsync(context)
}
);
} }
protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds(ResourceOwnerPasswordValidationContext context) protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds(ResourceOwnerPasswordValidationContext context)
@ -223,7 +242,12 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity
context.UserName = userByEmail.UserName; context.UserName = userByEmail.UserName;
} }
protected virtual async Task<bool> IsTfaEnabled(IdentityUser user) protected virtual Task<string> FindClientIdAsync(ResourceOwnerPasswordValidationContext context)
{
return Task.FromResult(context.Request?.Client?.ClientId);
}
protected virtual async Task<bool> IsTfaEnabledAsync(IdentityUser user)
=> UserManager.SupportsUserTwoFactor && => UserManager.SupportsUserTwoFactor &&
await UserManager.GetTwoFactorEnabledAsync(user) && await UserManager.GetTwoFactorEnabledAsync(user) &&
(await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0; (await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0;

Loading…
Cancel
Save