From 71acf5fad42cb83c540593baac665b8e80e4435f Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 1 Jun 2021 22:03:34 +0800 Subject: [PATCH] Add InvalidAuthenticatorCode response. --- .../Localization/Resources/en.json | 1 + .../Localization/Resources/tr.json | 1 + .../Localization/Resources/zh-Hans.json | 1 + .../AbpResourceOwnerPasswordValidator.cs | 224 ++++++++++-------- 4 files changed, 127 insertions(+), 100 deletions(-) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json index 87de63259f..31a8ebad04 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/en.json @@ -9,6 +9,7 @@ "InvalidUserNameOrPassword": "Invalid username or password!", "LoginIsNotAllowed": "You are not allowed to login! You need to confirm your email/phone number.", "InvalidUsername": "Invalid username or password!", + "InvalidAuthenticatorCode": "Invalid authenticator code!", "TheTargetUserIsNotLinkedToYou": "The target user is not linked to you!" } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json index 8a90e54172..b5c072c7e6 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/tr.json +++ b/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!", "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!", + "InvalidAuthenticatorCode": "Geçersiz kimlik doğrulama kodu!", "TheTargetUserIsNotLinkedToYou": "Hedef kullanıcı sizinle bağlantılı değil!" } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json index 0c5779717d..7b90b802e7 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/zh-Hans.json @@ -9,6 +9,7 @@ "InvalidUserNameOrPassword": "用户名或密码错误!", "LoginIsNotAllowed": "无法登录!你需要验证邮箱地址/手机号.", "InvalidUsername": "用户名或密码错误!", + "InvalidAuthenticatorCode": "验证码无效!", "TheTargetUserIsNotLinkedToYou": "目标用户未和你有关联!" } } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs index 81f2552e97..4d7d049949 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AspNetIdentity/AbpResourceOwnerPasswordValidator.cs @@ -64,141 +64,160 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity [UnitOfWork] public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { - var clientId = context.Request?.Client?.ClientId; - using var scope = ServiceScopeFactory.CreateScope(); - - await ReplaceEmailToUsernameOfInputIfNeeds(context); - - IdentityUser user = null; - - async Task SetSuccessResultAsync() + using (var scope = ServiceScopeFactory.CreateScope()) { - 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(); - - await AddCustomClaimsAsync(additionalClaims, user, context); + if (AbpIdentityOptions.ExternalLoginProviders.Any()) + { + foreach (var externalLoginProviderInfo in AbpIdentityOptions.ExternalLoginProviders.Values) + { + var externalLoginProvider = (IExternalLoginProvider) scope.ServiceProvider + .GetRequiredService(externalLoginProviderInfo.Type); - context.Result = new GrantValidationResult( - sub, - OidcConstants.AuthenticationMethods.Password, - additionalClaims.ToArray() - ); + if (await externalLoginProvider.TryAuthenticateAsync(context.UserName, context.Password)) + { + user = await UserManager.FindByNameAsync(context.UserName); + if (user == null) + { + user = await externalLoginProvider.CreateUserAsync(context.UserName, externalLoginProviderInfo.Name); + } + else + { + await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name); + } - await IdentitySecurityLogManager.SaveAsync( - new IdentitySecurityLogContext - { - Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, - Action = IdentityServerSecurityLogActionConsts.LoginSucceeded, - UserName = context.UserName, - ClientId = clientId + await SetSuccessResultAsync(context, user); + return; + } } - ); - } + } - if (AbpIdentityOptions.ExternalLoginProviders.Any()) - { - foreach (var externalLoginProviderInfo in AbpIdentityOptions.ExternalLoginProviders.Values) + user = await UserManager.FindByNameAsync(context.UserName); + string errorDescription; + if (user != null) { - var externalLoginProvider = (IExternalLoginProvider) scope.ServiceProvider - .GetRequiredService(externalLoginProviderInfo.Type); - - if (await externalLoginProvider.TryAuthenticateAsync(context.UserName, context.Password)) + await IdentityOptions.SetAsync(); + var result = await SignInManager.CheckPasswordSignInAsync(user, context.Password, true); + if (result.Succeeded) { - user = await UserManager.FindByNameAsync(context.UserName); - if (user == null) + if (await IsTfaEnabledAsync(user)) { - user = await externalLoginProvider.CreateUserAsync(context.UserName, externalLoginProviderInfo.Name); + await HandleTwoFactorLoginAsync(context, user); } else { - await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name); + await SetSuccessResultAsync(context, user); } - - await SetSuccessResultAsync(); return; } - } - } - user = await UserManager.FindByNameAsync(context.UserName); - string errorDescription; - if (user != null) - { - await IdentityOptions.SetAsync(); - var result = await SignInManager.CheckPasswordSignInAsync(user, context.Password, true); - if (result.Succeeded) - { - if (await IsTfaEnabled(user)) + if (result.IsLockedOut) { - 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)) - { - await SetSuccessResultAsync(); - return; - } - } - - 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() - { - {"userId", user.Id}, - {"twoFactorToken", twoFactorToken} - }); - return; + Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", context.UserName); + errorDescription = Localizer["UserLockedOut"]; + } + else if (result.IsNotAllowed) + { + Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName); + errorDescription = Localizer["LoginIsNotAllowed"]; + } + else + { + Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", context.UserName); + errorDescription = Localizer["InvalidUserNameOrPassword"]; } - await SetSuccessResultAsync(); - return; - } - else if (result.IsLockedOut) - { - Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", context.UserName); - errorDescription = Localizer["UserLockedOut"]; - } - else if (result.IsNotAllowed) - { - Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName); - errorDescription = Localizer["LoginIsNotAllowed"]; + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext + { + Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, + Action = result.ToIdentitySecurityLogAction(), + UserName = context.UserName, + ClientId = await FindClientIdAsync(context) + }); } else { - Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", context.UserName); - errorDescription = Localizer["InvalidUserNameOrPassword"]; + Logger.LogInformation("No user found matching username: {username}", context.UserName); + 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, - Action = result.ToIdentitySecurityLogAction(), - UserName = context.UserName, - ClientId = clientId - }); + await SetSuccessResultAsync(context, user); + return; + } + + Logger.LogInformation("Authentication failed for username: {username}, reason: InvalidAuthenticatorCode", context.UserName); + context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, Localizer["InvalidAuthenticatorCode"]); } else { - Logger.LogInformation("No user found matching username: {username}", context.UserName); - errorDescription = Localizer["InvalidUsername"]; + 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() + { + {"userId", user.Id}, + {"twoFactorToken", twoFactorToken} + }); - await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentityServerSecurityLogIdentityConsts.IdentityServer, - Action = IdentityServerSecurityLogActionConsts.LoginInvalidUserName, + Action = IdentityServerSecurityLogActionConsts.LoginRequiresTwoFactor, 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(); - 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) @@ -223,7 +242,12 @@ namespace Volo.Abp.IdentityServer.AspNetIdentity context.UserName = userByEmail.UserName; } - protected virtual async Task IsTfaEnabled(IdentityUser user) + protected virtual Task FindClientIdAsync(ResourceOwnerPasswordValidationContext context) + { + return Task.FromResult(context.Request?.Client?.ClientId); + } + + protected virtual async Task IsTfaEnabledAsync(IdentityUser user) => UserManager.SupportsUserTwoFactor && await UserManager.GetTwoFactorEnabledAsync(user) && (await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0;