source: PostgreSqlDotnetCore/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs

main
Last change on this file was 2aea0fd, checked in by ElenaMoskova <elena.moskova99@…>, 2 months ago

init commit Elena

  • Property mode set to 100644
File size: 7.3 KB
Line 
1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3#nullable disable
4
5using System;
6using System.ComponentModel.DataAnnotations;
7using System.Globalization;
8using System.Linq;
9using System.Text;
10using System.Text.Encodings.Web;
11using System.Threading.Tasks;
12using Microsoft.AspNetCore.Identity;
13using Microsoft.AspNetCore.Mvc;
14using Microsoft.AspNetCore.Mvc.RazorPages;
15using Microsoft.Extensions.Logging;
16
17namespace PostgreSqlDotnetCore.Areas.Identity.Pages.Account.Manage
18{
19 public class EnableAuthenticatorModel : PageModel
20 {
21 private readonly UserManager<IdentityUser> _userManager;
22 private readonly ILogger<EnableAuthenticatorModel> _logger;
23 private readonly UrlEncoder _urlEncoder;
24
25 private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
26
27 public EnableAuthenticatorModel(
28 UserManager<IdentityUser> userManager,
29 ILogger<EnableAuthenticatorModel> logger,
30 UrlEncoder urlEncoder)
31 {
32 _userManager = userManager;
33 _logger = logger;
34 _urlEncoder = urlEncoder;
35 }
36
37 /// <summary>
38 /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
39 /// directly from your code. This API may change or be removed in future releases.
40 /// </summary>
41 public string SharedKey { get; set; }
42
43 /// <summary>
44 /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
45 /// directly from your code. This API may change or be removed in future releases.
46 /// </summary>
47 public string AuthenticatorUri { get; set; }
48
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[] RecoveryCodes { get; set; }
55
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 [TempData]
61 public string StatusMessage { get; set; }
62
63 /// <summary>
64 /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
65 /// directly from your code. This API may change or be removed in future releases.
66 /// </summary>
67 [BindProperty]
68 public InputModel Input { 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 public class InputModel
75 {
76 /// <summary>
77 /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
78 /// directly from your code. This API may change or be removed in future releases.
79 /// </summary>
80 [Required]
81 [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
82 [DataType(DataType.Text)]
83 [Display(Name = "Verification Code")]
84 public string Code { get; set; }
85 }
86
87 public async Task<IActionResult> OnGetAsync()
88 {
89 var user = await _userManager.GetUserAsync(User);
90 if (user == null)
91 {
92 return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
93 }
94
95 await LoadSharedKeyAndQrCodeUriAsync(user);
96
97 return Page();
98 }
99
100 public async Task<IActionResult> OnPostAsync()
101 {
102 var user = await _userManager.GetUserAsync(User);
103 if (user == null)
104 {
105 return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
106 }
107
108 if (!ModelState.IsValid)
109 {
110 await LoadSharedKeyAndQrCodeUriAsync(user);
111 return Page();
112 }
113
114 // Strip spaces and hyphens
115 var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
116
117 var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
118 user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
119
120 if (!is2faTokenValid)
121 {
122 ModelState.AddModelError("Input.Code", "Verification code is invalid.");
123 await LoadSharedKeyAndQrCodeUriAsync(user);
124 return Page();
125 }
126
127 await _userManager.SetTwoFactorEnabledAsync(user, true);
128 var userId = await _userManager.GetUserIdAsync(user);
129 _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId);
130
131 StatusMessage = "Your authenticator app has been verified.";
132
133 if (await _userManager.CountRecoveryCodesAsync(user) == 0)
134 {
135 var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
136 RecoveryCodes = recoveryCodes.ToArray();
137 return RedirectToPage("./ShowRecoveryCodes");
138 }
139 else
140 {
141 return RedirectToPage("./TwoFactorAuthentication");
142 }
143 }
144
145 private async Task LoadSharedKeyAndQrCodeUriAsync(IdentityUser user)
146 {
147 // Load the authenticator key & QR code URI to display on the form
148 var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
149 if (string.IsNullOrEmpty(unformattedKey))
150 {
151 await _userManager.ResetAuthenticatorKeyAsync(user);
152 unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
153 }
154
155 SharedKey = FormatKey(unformattedKey);
156
157 var email = await _userManager.GetEmailAsync(user);
158 AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey);
159 }
160
161 private string FormatKey(string unformattedKey)
162 {
163 var result = new StringBuilder();
164 int currentPosition = 0;
165 while (currentPosition + 4 < unformattedKey.Length)
166 {
167 result.Append(unformattedKey.AsSpan(currentPosition, 4)).Append(' ');
168 currentPosition += 4;
169 }
170 if (currentPosition < unformattedKey.Length)
171 {
172 result.Append(unformattedKey.AsSpan(currentPosition));
173 }
174
175 return result.ToString().ToLowerInvariant();
176 }
177
178 private string GenerateQrCodeUri(string email, string unformattedKey)
179 {
180 return string.Format(
181 CultureInfo.InvariantCulture,
182 AuthenticatorUriFormat,
183 _urlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"),
184 _urlEncoder.Encode(email),
185 unformattedKey);
186 }
187 }
188}
Note: See TracBrowser for help on using the repository browser.