@ -20,317 +20,315 @@ using Volo.Abp.Validation;
using IdentityUser = Volo . Abp . Identity . IdentityUser ;
using SignInResult = Microsoft . AspNetCore . Identity . SignInResult ;
namespace Volo.Abp.Account.Web.Pages.Account
{
public class LoginModel : AccountPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get ; set ; }
namespace Volo.Abp.Account.Web.Pages.Account ;
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get ; set ; }
[BindProperty]
public LoginInputModel LoginInput { get ; set ; }
public class LoginModel : AccountPageModel
{
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrl { get ; set ; }
public bool EnableLocalLogin { get ; set ; }
[HiddenInput]
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get ; set ; }
//TODO: Why there is an ExternalProviders if only the VisibleExternalProviders is used.
public IEnumerable < ExternalProviderModel > ExternalProviders { get ; set ; }
public IEnumerable < ExternalProviderModel > VisibleExternalProviders = > ExternalProviders . Where ( x = > ! String . IsNullOrWhiteSpace ( x . DisplayName ) ) ;
[BindProperty]
public LoginInputModel LoginInput { get ; set ; }
public bool IsExternalLoginOnly = > EnableLocalLogin = = false & & ExternalProviders ? . Count ( ) = = 1 ;
public string ExternalLoginScheme = > IsExternalLoginOnly ? ExternalProviders ? . SingleOrDefault ( ) ? . AuthenticationScheme : null ;
public bool EnableLocalLogin { get ; set ; }
//Optional IdentityServer services
//public IIdentityServerInteractionService Interaction { get; set; }
//public IClientStore ClientStore { get; set; }
//public IEventService IdentityServerEvents { get; set; }
//TODO: Why there is an ExternalProviders if only the VisibleExternalProviders is used.
public IEnumerable < ExternalProviderModel > ExternalProviders { get ; set ; }
public IEnumerable < ExternalProviderModel > VisibleExternalProviders = > ExternalProviders . Where ( x = > ! String . IsNullOrWhiteSpace ( x . DisplayName ) ) ;
protected IAuthenticationSchemeProvider SchemeProvider { get ; }
protected AbpAccountOptions AccountOptions { get ; }
protected IOptions < IdentityOptions > IdentityOptions { get ; }
public bool IsExternalLoginOnly = > EnableLocalLogin = = false & & ExternalProviders ? . Count ( ) = = 1 ;
public string ExternalLoginScheme = > IsExternalLoginOnly ? ExternalProviders ? . SingleOrDefault ( ) ? . AuthenticationScheme : null ;
public bool ShowCancelButton { get ; set ; }
//Optional IdentityServer services
//public IIdentityServerInteractionService Interaction { get; set; }
//public IClientStore ClientStore { get; set; }
//public IEventService IdentityServerEvents { get; set; }
public LoginModel (
IAuthenticationSchemeProvider schemeProvider ,
IOptions < AbpAccountOptions > accountOptions ,
IOptions < IdentityOptions > identityOptions )
{
SchemeProvider = schemeProvider ;
IdentityOptions = identityOptions ;
AccountOptions = accountOptions . Value ;
}
protected IAuthenticationSchemeProvider SchemeProvider { get ; }
protected AbpAccountOptions AccountOptions { get ; }
protected IOptions < IdentityOptions > IdentityOptions { get ; }
public virtual async Task < IActionResult > OnGetAsync ( )
{
LoginInput = new LoginInputModel ( ) ;
public bool ShowCancelButton { get ; set ; }
ExternalProviders = await GetExternalProviders ( ) ;
public LoginModel (
IAuthenticationSchemeProvider schemeProvider ,
IOptions < AbpAccountOptions > accountOptions ,
IOptions < IdentityOptions > identityOptions )
{
SchemeProvider = schemeProvider ;
IdentityOptions = identityOptions ;
AccountOptions = accountOptions . Value ;
}
EnableLocalLogin = await SettingProvider . IsTrueAsync ( AccountSettingNames . EnableLocalLogin ) ;
public virtual async Task < IActionResult > OnGetAsync ( )
{
LoginInput = new LoginInputModel ( ) ;
if ( IsExternalLoginOnly )
{
//return await ExternalLogin(vm.ExternalLoginScheme, returnUrl);
throw new NotImplementedException ( ) ;
}
ExternalProviders = await GetExternalProviders ( ) ;
return Page ( ) ;
}
EnableLocalLogin = await SettingProvider . IsTrueAsync ( AccountSettingNames . EnableLocalLogin ) ;
public virtual async Task < IActionResult > OnPostAsync ( string action )
if ( IsExternalLoginOnly )
{
await CheckLocalLoginAsync ( ) ;
ValidateModel ( ) ;
ExternalProviders = await GetExternalProviders ( ) ;
EnableLocalLogin = await SettingProvider . IsTrueAsync ( AccountSettingNames . EnableLocalLogin ) ;
await ReplaceEmailToUsernameOfInputIfNeeds ( ) ;
//return await ExternalLogin(vm.ExternalLoginScheme, returnUrl);
throw new NotImplementedException ( ) ;
}
await IdentityOptions . SetAsync ( ) ;
return Page ( ) ;
}
var result = await SignInManager . PasswordSignInAsync (
LoginInput . UserNameOrEmailAddress ,
LoginInput . Password ,
LoginInput . RememberMe ,
true
) ;
public virtual async Task < IActionResult > OnPostAsync ( string action )
{
await CheckLocalLoginAsync ( ) ;
await IdentitySecurityLogManager . SaveAsync ( new IdentitySecurityLogContext ( )
{
Identity = IdentitySecurityLogIdentityConsts . Identity ,
Action = result . ToIdentitySecurityLogAction ( ) ,
UserName = LoginInput . UserNameOrEmailAddress
} ) ;
ValidateModel ( ) ;
if ( result . RequiresTwoFactor )
{
return await TwoFactorLoginResultAsync ( ) ;
}
ExternalProviders = await GetExternalProviders ( ) ;
if ( result . IsLockedOut )
{
Alerts . Warning ( L [ "UserLockedOutMessage" ] ) ;
return Page ( ) ;
}
EnableLocalLogin = await SettingProvider . IsTrueAsync ( AccountSettingNames . EnableLocalLogin ) ;
if ( result . IsNotAllowed )
{
Alerts . Warning ( L [ "LoginIsNotAllowed" ] ) ;
return Page ( ) ;
}
await ReplaceEmailToUsernameOfInputIfNeeds ( ) ;
if ( ! result . Succeeded )
{
Alerts . Danger ( L [ "InvalidUserNameOrPassword" ] ) ;
return Page ( ) ;
}
await IdentityOptions . SetAsync ( ) ;
//TODO: Find a way of getting user's id from the logged in user and do not query it again like that!
var user = await UserManager . FindByNameAsync ( LoginInput . UserNameOrEmailAddress ) ? ?
await UserManager . FindByEmailAsync ( LoginInput . UserNameOrEmailAddress ) ;
var result = await SignInManager . PasswordSignInAsync (
LoginInput . UserNameOrEmailAddress ,
LoginInput . Password ,
LoginInput . RememberMe ,
true
) ;
Debug . Assert ( user ! = null , nameof ( user ) + " != null" ) ;
await IdentitySecurityLogManager . SaveAsync ( new IdentitySecurityLogContext ( )
{
Identity = IdentitySecurityLogIdentityConsts . Identity ,
Action = result . ToIdentitySecurityLogAction ( ) ,
UserName = LoginInput . UserNameOrEmailAddress
} ) ;
return RedirectSafely ( ReturnUrl , ReturnUrlHash ) ;
if ( result . RequiresTwoFactor )
{
return await TwoFactorLoginResultAsync ( ) ;
}
/// <summary>
/// Override this method to add 2FA for your application.
/// </summary>
protected virtual Task < IActionResult > TwoFactorLoginResultAsync ( )
if ( result . IsLockedOut )
{
throw new NotImplementedException ( ) ;
Alerts . Warning ( L [ "UserLockedOutMessage" ] ) ;
return Page ( ) ;
}
protected virtual async Task < List < ExternalProviderModel > > GetExternalProviders ( )
if ( result . IsNotAllowed )
{
var schemes = await SchemeProvider . GetAllSchemesAsync ( ) ;
return schemes
. Where ( x = > x . DisplayName ! = null | | x . Name . Equals ( AccountOptions . WindowsAuthenticationSchemeName , StringComparison . OrdinalIgnoreCase ) )
. Select ( x = > new ExternalProviderModel
{
DisplayName = x . DisplayName ,
AuthenticationScheme = x . Name
} )
. ToList ( ) ;
Alerts . Warning ( L [ "LoginIsNotAllowed" ] ) ;
return Page ( ) ;
}
public virtual async Task < IActionResult > OnPostExternalLogin ( string provider )
if ( ! result . Succeeded )
{
var redirectUrl = Url . Page ( "./Login" , pageHandler : "ExternalLoginCallback" , values : new { ReturnUrl , ReturnUrlHash } ) ;
var properties = SignInManager . ConfigureExternalAuthenticationProperties ( provider , redirectUrl ) ;
properties . Items [ "scheme" ] = provider ;
return await Task . FromResult ( Challenge ( properties , provider ) ) ;
Alerts . Danger ( L [ "InvalidUserNameOrPassword" ] ) ;
return Page ( ) ;
}
public virtual async Task < IActionResult > OnGetExternalLoginCallbackAsync ( string returnUrl = "" , string returnUrlHash = "" , string remoteError = null )
{
//TODO: Did not implemented Identity Server 4 sample for this method (see ExternalLoginCallback in Quickstart of IDS4 sample)
/ * Also did not implement these :
* - Logout ( string logoutId )
* /
//TODO: Find a way of getting user's id from the logged in user and do not query it again like that!
var user = await UserManager . FindByNameAsync ( LoginInput . UserNameOrEmailAddress ) ? ?
await UserManager . FindByEmailAsync ( LoginInput . UserNameOrEmailAddress ) ;
if ( remoteError ! = null )
{
Logger . LogWarning ( $"External login callback error: {remoteError}" ) ;
return RedirectToPage ( "./Login" ) ;
}
Debug . Assert ( user ! = null , nameof ( user ) + " != null" ) ;
await IdentityOptions . SetAsync ( ) ;
return RedirectSafely ( ReturnUrl , ReturnUrlHash ) ;
}
var loginInfo = await SignInManager . GetExternalLoginInfoAsync ( ) ;
if ( loginInfo = = null )
{
Logger . LogWarning ( "External login info is not available" ) ;
return RedirectToPage ( "./Login" ) ;
}
/// <summary>
/// Override this method to add 2FA for your application.
/// </summary>
protected virtual Task < IActionResult > TwoFactorLoginResultAsync ( )
{
throw new NotImplementedException ( ) ;
}
var result = await SignInManager . ExternalLoginSignInAsync (
loginInfo . LoginProvider ,
loginInfo . ProviderKey ,
isPersistent : false ,
bypassTwoFactor : true
) ;
protected virtual async Task < List < ExternalProviderModel > > GetExternalProviders ( )
{
var schemes = await SchemeProvider . GetAllSchemesAsync ( ) ;
if ( ! result . Succeeded )
return schemes
. Where ( x = > x . DisplayName ! = null | | x . Name . Equals ( AccountOptions . WindowsAuthenticationSchemeName , StringComparison . OrdinalIgnoreCase ) )
. Select ( x = > new ExternalProviderModel
{
await IdentitySecurityLogManager . SaveAsync ( new IdentitySecurityLogContext ( )
{
Identity = IdentitySecurityLogIdentityConsts . IdentityExternal ,
Action = "Login" + result
} ) ;
}
DisplayName = x . DisplayName ,
AuthenticationScheme = x . Name
} )
. ToList ( ) ;
}
if ( result . IsLockedOut )
{
Logger . LogWarning ( $"External login callback error: user is locked out!" ) ;
throw new UserFriendlyException ( "Cannot proceed because user is locked out!" ) ;
}
public virtual async Task < IActionResult > OnPostExternalLogin ( string provider )
{
var redirectUrl = Url . Page ( "./Login" , pageHandler : "ExternalLoginCallback" , values : new { ReturnUrl , ReturnUrlHash } ) ;
var properties = SignInManager . ConfigureExternalAuthenticationProperties ( provider , redirectUrl ) ;
properties . Items [ "scheme" ] = provider ;
if ( result . IsNotAllowed )
{
Logger . LogWarning ( $"External login callback error: user is not allowed!" ) ;
throw new UserFriendlyException ( "Cannot proceed because user is not allowed!" ) ;
}
return await Task . FromResult ( Challenge ( properties , provider ) ) ;
}
if ( result . Succeeded )
{
return RedirectSafely ( returnUrl , returnUrlHash ) ;
}
public virtual async Task < IActionResult > OnGetExternalLoginCallbackAsync ( string returnUrl = "" , string returnUrlHash = "" , string remoteError = null )
{
//TODO: Did not implemented Identity Server 4 sample for this method (see ExternalLoginCallback in Quickstart of IDS4 sample)
/ * Also did not implement these :
* - Logout ( string logoutId )
* /
//TODO: Handle other cases for result!
if ( remoteError ! = null )
{
Logger . LogWarning ( $"External login callback error: {remoteError}" ) ;
return RedirectToPage ( "./Login" ) ;
}
var email = loginInfo . Principal . FindFirstValue ( AbpClaimTypes . Email ) ;
if ( email . IsNullOrWhiteSpace ( ) )
{
return RedirectToPage ( "./Register" , new
{
IsExternalLogin = true ,
ExternalLoginAuthSchema = loginInfo . LoginProvider ,
ReturnUrl = returnUrl
} ) ;
}
await IdentityOptions . SetAsync ( ) ;
var user = await UserManager . FindByEmailAsync ( email ) ;
if ( user = = null )
{
user = await CreateExternalUserAsync ( loginInfo ) ;
}
else
{
if ( await UserManager . FindByLoginAsync ( loginInfo . LoginProvider , loginInfo . ProviderKey ) = = null )
{
CheckIdentityErrors ( await UserManager . AddLoginAsync ( user , loginInfo ) ) ;
}
}
var loginInfo = await SignInManager . GetExternalLoginInfoAsync ( ) ;
if ( loginInfo = = null )
{
Logger . LogWarning ( "External login info is not available" ) ;
return RedirectToPage ( "./Login" ) ;
}
await SignInManager . SignInAsync ( user , false ) ;
var result = await SignInManager . ExternalLoginSignInAsync (
loginInfo . LoginProvider ,
loginInfo . ProviderKey ,
isPersistent : false ,
bypassTwoFactor : true
) ;
if ( ! result . Succeeded )
{
await IdentitySecurityLogManager . SaveAsync ( new IdentitySecurityLogContext ( )
{
Identity = IdentitySecurityLogIdentityConsts . IdentityExternal ,
Action = result . ToIdentitySecurityLogAction ( ) ,
UserName = user . Name
Action = "Login" + result
} ) ;
return RedirectSafely ( returnUrl , returnUrlHash ) ;
}
protected virtual async Task < IdentityUser > CreateExternalUserAsync ( ExternalLoginInfo info )
if ( result . IsLockedOut )
{
await IdentityOptions . SetAsync ( ) ;
Logger . LogWarning ( $"External login callback error: user is locked out!" ) ;
throw new UserFriendlyException ( "Cannot proceed because user is locked out!" ) ;
}
var emailAddress = info . Principal . FindFirstValue ( AbpClaimTypes . Email ) ;
if ( result . IsNotAllowed )
{
Logger . LogWarning ( $"External login callback error: user is not allowed!" ) ;
throw new UserFriendlyException ( "Cannot proceed because user is not allowed!" ) ;
}
var user = new IdentityUser ( GuidGenerator . Create ( ) , emailAddress , emailAddress , CurrentTenant . Id ) ;
if ( result . Succeeded )
{
return RedirectSafely ( returnUrl , returnUrlHash ) ;
}
CheckIdentityErrors ( await UserManager . CreateAsync ( user ) ) ;
CheckIdentityErrors ( await UserManager . SetEmailAsync ( user , emailAddress ) ) ;
CheckIdentityErrors ( await UserManager . AddLoginAsync ( user , info ) ) ;
CheckIdentityErrors ( await UserManager . AddDefaultRolesAsync ( user ) ) ;
//TODO: Handle other cases for result!
return user ;
var email = loginInfo . Principal . FindFirstValue ( AbpClaimTypes . Email ) ;
if ( email . IsNullOrWhiteSpace ( ) )
{
return RedirectToPage ( "./Register" , new {
IsExternalLogin = true ,
ExternalLoginAuthSchema = loginInfo . LoginProvider ,
ReturnUrl = returnUrl
} ) ;
}
protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds ( )
var user = await UserManager . FindByEmailAsync ( email ) ;
if ( user = = null )
{
user = await CreateExternalUserAsync ( loginInfo ) ;
}
else
{
if ( ! ValidationHelper . IsValidEmailAddress ( LoginInput . UserNameOrEmailAddress ) )
if ( await UserManager . FindByLoginAsync ( loginInfo . LoginProvider , loginInfo . ProviderKey ) = = null )
{
return ;
CheckIdentityErrors ( await UserManager . AddLoginAsync ( user , loginInfo ) ) ;
}
}
var userByUsername = await UserManager . FindByNameAsync ( LoginInput . UserNameOrEmailAddress ) ;
if ( userByUsername ! = null )
{
return ;
}
await SignInManager . SignInAsync ( user , false ) ;
var userByEmail = await UserManager . FindByEmailAsync ( LoginInput . UserNameOrEmailAddress ) ;
if ( userByEmail = = null )
{
return ;
}
await IdentitySecurityLogManager . SaveAsync ( new IdentitySecurityLogContext ( )
{
Identity = IdentitySecurityLogIdentityConsts . IdentityExternal ,
Action = result . ToIdentitySecurityLogAction ( ) ,
UserName = user . Name
} ) ;
LoginInput . UserNameOrEmailAddress = userByEmail . UserName ;
}
return RedirectSafely ( returnUrl , returnUrlHash ) ;
}
protected virtual async Task < IdentityUser > CreateExternalUserAsync ( ExternalLoginInfo info )
{
await IdentityOptions . SetAsync ( ) ;
var emailAddress = info . Principal . FindFirstValue ( AbpClaimTypes . Email ) ;
protected virtual async Task CheckLocalLoginAsync ( )
var user = new IdentityUser ( GuidGenerator . Create ( ) , emailAddress , emailAddress , CurrentTenant . Id ) ;
CheckIdentityErrors ( await UserManager . CreateAsync ( user ) ) ;
CheckIdentityErrors ( await UserManager . SetEmailAsync ( user , emailAddress ) ) ;
CheckIdentityErrors ( await UserManager . AddLoginAsync ( user , info ) ) ;
CheckIdentityErrors ( await UserManager . AddDefaultRolesAsync ( user ) ) ;
return user ;
}
protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds ( )
{
if ( ! ValidationHelper . IsValidEmailAddress ( LoginInput . UserNameOrEmailAddress ) )
{
if ( ! await SettingProvider . IsTrueAsync ( AccountSettingNames . EnableLocalLogin ) )
{
throw new UserFriendlyException ( L [ "LocalLoginDisabledMessage" ] ) ;
}
return ;
}
public class LoginInputModel
var userByUsername = await UserManager . FindByNameAsync ( LoginInput . UserNameOrEmailAddress ) ;
if ( userByUsername ! = null )
{
[Required]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))]
public string UserNameOrEmailAddress { get ; set ; }
[Required]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
[DataType(DataType.Password)]
[DisableAuditing]
public string Password { get ; set ; }
return ;
}
public bool RememberMe { get ; set ; }
var userByEmail = await UserManager . FindByEmailAsync ( LoginInput . UserNameOrEmailAddress ) ;
if ( userByEmail = = null )
{
return ;
}
public class ExternalProviderModel
LoginInput . UserNameOrEmailAddress = userByEmail . UserName ;
}
protected virtual async Task CheckLocalLoginAsync ( )
{
if ( ! await SettingProvider . IsTrueAsync ( AccountSettingNames . EnableLocalLogin ) )
{
public string DisplayName { get ; set ; }
public string AuthenticationScheme { get ; set ; }
throw new UserFriendlyException ( L [ "LocalLoginDisabledMessage" ] ) ;
}
}
public class LoginInputModel
{
[Required]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))]
public string UserNameOrEmailAddress { get ; set ; }
[Required]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
[DataType(DataType.Password)]
[DisableAuditing]
public string Password { get ; set ; }
public bool RememberMe { get ; set ; }
}
public class ExternalProviderModel
{
public string DisplayName { get ; set ; }
public string AuthenticationScheme { get ; set ; }
}
}