|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | 3 | #nullable disable |
4 | 4 |
|
5 | | -using Fido2Identity; |
6 | 5 | using Microsoft.AspNetCore.Authentication; |
| 6 | +using Microsoft.AspNetCore.Authorization; |
7 | 7 | using Microsoft.AspNetCore.Identity; |
8 | 8 | using Microsoft.AspNetCore.Mvc; |
9 | 9 | using Microsoft.AspNetCore.Mvc.RazorPages; |
10 | | -using OpeniddictServer.Data; |
| 10 | +using IdentityProvider.Data; |
11 | 11 | using System.ComponentModel.DataAnnotations; |
12 | 12 |
|
13 | | -namespace OpeniddictServer.Areas.Identity.Pages.Account |
| 13 | +namespace IdentityProvider.Areas.Identity.Pages.Account; |
| 14 | + |
| 15 | +[AllowAnonymous] |
| 16 | +public class LoginModel : PageModel |
14 | 17 | { |
15 | | - public class LoginModel : PageModel |
16 | | - { |
17 | | - private readonly SignInManager<ApplicationUser> _signInManager; |
18 | | - private readonly Fido2Store _fido2Store; |
19 | | - private readonly ILogger<LoginModel> _logger; |
| 18 | + private readonly SignInManager<ApplicationUser> _signInManager; |
| 19 | + private readonly ILogger<LoginModel> _logger; |
20 | 20 |
|
21 | | - public LoginModel(SignInManager<ApplicationUser> signInManager, |
22 | | - Fido2Store fido2Store, |
23 | | - ILogger<LoginModel> logger) |
24 | | - { |
25 | | - _signInManager = signInManager; |
26 | | - _fido2Store = fido2Store; |
27 | | - _logger = logger; |
28 | | - } |
| 21 | + public LoginModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger) |
| 22 | + { |
| 23 | + _signInManager = signInManager; |
| 24 | + _logger = logger; |
| 25 | + } |
29 | 26 |
|
| 27 | + /// <summary> |
| 28 | + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
| 29 | + /// directly from your code. This API may change or be removed in future releases. |
| 30 | + /// </summary> |
| 31 | + [BindProperty] |
| 32 | + public InputModel Input { get; set; } |
| 33 | + |
| 34 | + /// <summary> |
| 35 | + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
| 36 | + /// directly from your code. This API may change or be removed in future releases. |
| 37 | + /// </summary> |
| 38 | + public IList<AuthenticationScheme> ExternalLogins { get; set; } |
| 39 | + |
| 40 | + /// <summary> |
| 41 | + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
| 42 | + /// directly from your code. This API may change or be removed in future releases. |
| 43 | + /// </summary> |
| 44 | + public string ReturnUrl { get; set; } |
| 45 | + |
| 46 | + /// <summary> |
| 47 | + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
| 48 | + /// directly from your code. This API may change or be removed in future releases. |
| 49 | + /// </summary> |
| 50 | + [TempData] |
| 51 | + public string ErrorMessage { get; set; } |
| 52 | + |
| 53 | + /// <summary> |
| 54 | + /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
| 55 | + /// directly from your code. This API may change or be removed in future releases. |
| 56 | + /// </summary> |
| 57 | + public class InputModel |
| 58 | + { |
30 | 59 | /// <summary> |
31 | 60 | /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
32 | 61 | /// directly from your code. This API may change or be removed in future releases. |
33 | 62 | /// </summary> |
34 | | - [BindProperty] |
35 | | - public InputModel Input { get; set; } |
| 63 | + [Required] |
| 64 | + [EmailAddress] |
| 65 | + public string Email { get; set; } |
36 | 66 |
|
37 | 67 | /// <summary> |
38 | 68 | /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
39 | 69 | /// directly from your code. This API may change or be removed in future releases. |
40 | 70 | /// </summary> |
41 | | - public IList<AuthenticationScheme> ExternalLogins { get; set; } |
| 71 | + [Required] |
| 72 | + [DataType(DataType.Password)] |
| 73 | + public string Password { get; set; } |
42 | 74 |
|
43 | 75 | /// <summary> |
44 | 76 | /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
45 | 77 | /// directly from your code. This API may change or be removed in future releases. |
46 | 78 | /// </summary> |
47 | | - public string ReturnUrl { get; set; } |
| 79 | + [Display(Name = "Remember me?")] |
| 80 | + public bool RememberMe { get; set; } |
48 | 81 |
|
49 | | - /// <summary> |
50 | | - /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
51 | | - /// directly from your code. This API may change or be removed in future releases. |
52 | | - /// </summary> |
53 | | - [TempData] |
54 | | - public string ErrorMessage { get; set; } |
| 82 | + public PasskeyInputModel Passkey { get; set; } |
| 83 | + } |
55 | 84 |
|
56 | | - /// <summary> |
57 | | - /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
58 | | - /// directly from your code. This API may change or be removed in future releases. |
59 | | - /// </summary> |
60 | | - public class InputModel |
| 85 | + public async Task OnGetAsync(string returnUrl = null) |
| 86 | + { |
| 87 | + if (!string.IsNullOrEmpty(ErrorMessage)) |
61 | 88 | { |
62 | | - /// <summary> |
63 | | - /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
64 | | - /// directly from your code. This API may change or be removed in future releases. |
65 | | - /// </summary> |
66 | | - [Required] |
67 | | - [EmailAddress] |
68 | | - public string Email { get; set; } |
69 | | - |
70 | | - /// <summary> |
71 | | - /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
72 | | - /// directly from your code. This API may change or be removed in future releases. |
73 | | - /// </summary> |
74 | | - [Required] |
75 | | - [DataType(DataType.Password)] |
76 | | - public string Password { get; set; } |
77 | | - |
78 | | - /// <summary> |
79 | | - /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used |
80 | | - /// directly from your code. This API may change or be removed in future releases. |
81 | | - /// </summary> |
82 | | - [Display(Name = "Remember me?")] |
83 | | - public bool RememberMe { get; set; } |
| 89 | + ModelState.AddModelError(string.Empty, ErrorMessage); |
84 | 90 | } |
85 | 91 |
|
86 | | - public async Task OnGetAsync(string returnUrl = null) |
87 | | - { |
88 | | - if (!string.IsNullOrEmpty(ErrorMessage)) |
89 | | - { |
90 | | - ModelState.AddModelError(string.Empty, ErrorMessage); |
91 | | - } |
| 92 | + returnUrl ??= Url.Content("~/"); |
| 93 | + |
| 94 | + // Clear the existing external cookie to ensure a clean login process |
| 95 | + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); |
| 96 | + |
| 97 | + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); |
| 98 | + |
| 99 | + ReturnUrl = returnUrl; |
| 100 | + } |
| 101 | + |
| 102 | + public async Task<IActionResult> OnPostAsync(string returnUrl = null) |
| 103 | + { |
| 104 | + returnUrl ??= Url.Content("~/"); |
92 | 105 |
|
93 | | - returnUrl ??= Url.Content("~/"); |
| 106 | + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); |
94 | 107 |
|
95 | | - // Clear the existing external cookie to ensure a clean login process |
96 | | - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); |
| 108 | + Microsoft.AspNetCore.Identity.SignInResult result = null; |
97 | 109 |
|
98 | | - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); |
| 110 | + if (!string.IsNullOrEmpty(Input.Passkey?.CredentialJson)) |
| 111 | + { |
| 112 | + // When performing passkey sign-in, don't perform form validation. |
| 113 | + ModelState.Clear(); |
99 | 114 |
|
100 | | - ReturnUrl = returnUrl; |
| 115 | + result = await _signInManager.PasskeySignInAsync(Input.Passkey.CredentialJson); |
| 116 | + } |
| 117 | + else if (ModelState.IsValid) |
| 118 | + { |
| 119 | + // This doesn't count login failures towards account lockout |
| 120 | + // To enable password failures to trigger account lockout, set lockoutOnFailure: true |
| 121 | + result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); |
101 | 122 | } |
102 | 123 |
|
103 | | - public async Task<IActionResult> OnPostAsync(string returnUrl = null) |
| 124 | + if (result.Succeeded) |
| 125 | + { |
| 126 | + _logger.LogInformation("User logged in."); |
| 127 | + return LocalRedirect(returnUrl); |
| 128 | + } |
| 129 | + if (result.RequiresTwoFactor) |
| 130 | + { |
| 131 | + return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); |
| 132 | + } |
| 133 | + if (result.IsLockedOut) |
| 134 | + { |
| 135 | + _logger.LogWarning("User account locked out."); |
| 136 | + return RedirectToPage("./Lockout"); |
| 137 | + } |
| 138 | + else |
104 | 139 | { |
105 | | - returnUrl ??= Url.Content("~/"); |
106 | | - |
107 | | - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); |
108 | | - |
109 | | - if (ModelState.IsValid) |
110 | | - { |
111 | | - // This doesn't count login failures towards account lockout |
112 | | - // To enable password failures to trigger account lockout, set lockoutOnFailure: true |
113 | | - var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); |
114 | | - if (result.Succeeded) |
115 | | - { |
116 | | - _logger.LogInformation("User logged in."); |
117 | | - return LocalRedirect(returnUrl); |
118 | | - } |
119 | | - if (result.RequiresTwoFactor) |
120 | | - { |
121 | | - var fido2ItemExistsForUser = await _fido2Store.GetCredentialsByUserNameAsync(Input.Email); |
122 | | - if (fido2ItemExistsForUser.Count > 0) |
123 | | - { |
124 | | - return RedirectToPage("./LoginFido2Mfa", new { ReturnUrl = returnUrl, Input.RememberMe }); |
125 | | - } |
126 | | - |
127 | | - return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); |
128 | | - } |
129 | | - if (result.IsLockedOut) |
130 | | - { |
131 | | - _logger.LogWarning("User account locked out."); |
132 | | - return RedirectToPage("./Lockout"); |
133 | | - } |
134 | | - else |
135 | | - { |
136 | | - ModelState.AddModelError(string.Empty, "Invalid login attempt."); |
137 | | - return Page(); |
138 | | - } |
139 | | - } |
140 | | - |
141 | | - // If we got this far, something failed, redisplay form |
| 140 | + ModelState.AddModelError(string.Empty, "Invalid login attempt."); |
142 | 141 | return Page(); |
143 | 142 | } |
144 | 143 | } |
|
0 commit comments