52 |
53 | ## This project isn't easy! :grinning:
54 |
55 |
56 |
--------------------------------------------------------------------------------
/services/AddressStateService.cs:
--------------------------------------------------------------------------------
1 | using ApplicationCore.Data;
2 | using ApplicationCore.Data.Entities;
3 | using Microsoft.AspNetCore.Http;
4 | using Microsoft.AspNetCore.Identity;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.Extensions.Logging;
7 | using Persistence;
8 | using services.contracts;
9 | using Domain.ViewModels;
10 | using System;
11 | using System.Collections.Generic;
12 | using System.Linq;
13 | using System.Text;
14 | using System.Threading.Tasks;
15 | using Domain.Observers;
16 |
17 | namespace services
18 | {
19 | public class AddressStateService : IAddressStateService
20 | {
21 | private readonly ApplicationDbContext _context;
22 | private readonly UserManager _userManager;
23 | private IHttpContextAccessor _contextAccessor;
24 | private readonly ILogger _logger;
25 |
26 | public AddressStateService(ApplicationDbContext context, UserManager userManager, IHttpContextAccessor contextAccessor, ILogger logger)
27 | {
28 | _context = context;
29 | _userManager = userManager;
30 | _contextAccessor = contextAccessor;
31 | _logger = logger;
32 | }
33 |
34 |
35 | public List Observers = new List();
36 |
37 | public void UpdateOrder(AddressState State)
38 | {
39 | Notify(State);
40 | }
41 |
42 | public void Attach(IStateObserver observer)
43 | {
44 | Observers.Add(observer);
45 | }
46 |
47 | public void Detach(IStateObserver observer)
48 | {
49 | Observers.Remove(observer);
50 | }
51 |
52 | public void Notify(AddressState order)
53 | {
54 | foreach (var observer in Observers)
55 | {
56 | observer.Update(order);
57 | }
58 | }
59 |
60 |
61 |
62 | public async Task GetAddress(Guid AddressStateId)
63 | {
64 | var addressState = await _context.AddressState.Include(c => c.UpdatedBy).FirstOrDefaultAsync(m => m.StateId == AddressStateId);
65 | if (addressState == null)
66 | {
67 | return null;
68 | }
69 | return addressState;
70 | }
71 |
72 | public async Task> GetAllAddresses()
73 | {
74 | List statesList = new List();
75 | var AddressStateList = await _context.AddressState.Include(c => c.UpdatedBy).ToListAsync();
76 |
77 | foreach(var addressState in AddressStateList)
78 | {
79 | statesList.Add(new StateViewModel() {StateId= addressState.StateId, AddressState = addressState, CreatedBy = addressState.UpdatedBy.Email, Country = "USA" });
80 | }
81 | return statesList;
82 | }
83 |
84 | public async Task SetAddress(AddressState AddressState)
85 | {
86 | AddressState.CreatedBy = await _userManager.GetUserAsync(_contextAccessor.HttpContext.User);
87 | AddressState.UpdatedBy = await _userManager.GetUserAsync(_contextAccessor.HttpContext.User);
88 | AddressState.CreatedOn = DateTime.UtcNow;
89 | AddressState.UpdatedOn = DateTime.UtcNow;
90 | AddressState.StateId = new Guid();
91 |
92 | _context.AddressState.Add(AddressState);
93 | await _context.SaveChangesAsync();
94 |
95 | return AddressState;
96 | }
97 |
98 | public async Task UpdateAddress(AddressState AddressState)
99 | {
100 | AddressState.UpdatedBy = await _userManager.GetUserAsync(_contextAccessor.HttpContext.User);
101 | AddressState.UpdatedOn = DateTime.UtcNow;
102 |
103 | _context.Update(AddressState);
104 | await _context.SaveChangesAsync();
105 |
106 | Notify(AddressState);
107 |
108 | return AddressState;
109 | }
110 |
111 | public async Task DeleteAddressState(Guid AddressStateId)
112 | {
113 | var addressState = await _context.AddressState.FindAsync(AddressStateId).ConfigureAwait(true);
114 | _context.AddressState.Remove(addressState);
115 | await _context.SaveChangesAsync();
116 | return true;
117 | }
118 |
119 | public bool IsAddressStateExist(Guid AddressStateId)
120 | {
121 | return _context.AddressState.Any(e => e.StateId == AddressStateId);
122 | }
123 |
124 | public async Task DeleteAddressStateByAppUserId(Guid AppUserId)
125 | {
126 | var addressStates = await _context.AddressState.Include(x => x.CreatedBy).Where(x => x.CreatedBy.Id == AppUserId).ToListAsync().ConfigureAwait(true);
127 |
128 | foreach(var state in addressStates)
129 | {
130 | _context.AddressState.Remove(state);
131 | await _context.SaveChangesAsync();
132 | }
133 | return true;
134 | }
135 | }
136 | }
--------------------------------------------------------------------------------
/services/Domain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/services/EmailSender.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity.UI.Services;
2 | using Microsoft.Extensions.Options;
3 | using SendGrid;
4 | using SendGrid.Helpers.Mail;
5 | using System;
6 | using System.Collections.Specialized;
7 | using System.IO;
8 | using System.Net;
9 | using System.Net.Mail;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace services
14 | {
15 | public class AuthMessageSenderOptions
16 | {
17 | public string AuthCode { get; set; }
18 | public string PrivateKey { get; set; }
19 | }
20 |
21 | public class EmailSender : IEmailSender
22 | {
23 | public EmailSender(IOptions optionsAccessor)
24 | {
25 | Options = optionsAccessor.Value;
26 | }
27 |
28 | public AuthMessageSenderOptions Options { get; set; }
29 |
30 | public Task SendEmailAsync(string email, string EmailType, string CallBackURL)
31 | {
32 | string subject = string.Empty;
33 | string message = string.Empty;
34 |
35 | switch (EmailType)
36 | {
37 | case "CONFIRM":
38 | subject = "Confirm Your Email";
39 |
40 | message = "To verify : $EmailValue$ , Click $VerificationEmailValue$";
41 | message = message.Replace("$EmailValue$", email).Replace("$VerificationEmailValue$", CallBackURL);
42 | break;
43 | case "RESET":
44 | subject = "Reset Your Password";
45 | message = "Click here : $ResetLinkValue$ To Reset";
46 | message = message.Replace("$ResetLinkValue$", CallBackURL);
47 | break;
48 | default:
49 | subject = "";
50 | break;
51 | }
52 | return SendEmail(Options.AuthCode, Options.PrivateKey, subject, message, email);
53 | }
54 |
55 | public Task SendEmailConfirmationAsync(string email, string subject, string EmailConfirmationLink, string EmailToConfirm)
56 | {
57 |
58 | string message = string.Empty;
59 |
60 | //REad from placeHolder File
61 | message = message.Replace("$EmailValue$", EmailToConfirm).Replace("$VerificationEmailValue$", EmailConfirmationLink);
62 |
63 | return SendEmail(Options.AuthCode,Options.PrivateKey , subject, message, email);
64 | }
65 |
66 | ///
67 | /// Sending Email using Emailyt email service
68 | /// which is free!
69 | ///
70 | ///
71 | ///
72 | ///
73 | ///
74 | ///
75 | public Task SendEmail(string authCode,string privateKey, string subject, string message, string email)
76 | {
77 |
78 | string baseURL = "https://emailyt.com/";
79 |
80 | string api = "DispatchEmail";
81 |
82 | using var wb = new WebClient();
83 |
84 | var data = new NameValueCollection();
85 |
86 | data["AuthCode"] = authCode;
87 | data["PrivateKey"] = privateKey;
88 |
89 | data["ToEmail"] = email;
90 |
91 | data["Subject"] = subject;
92 | data["MailBody"] = message;
93 |
94 | var response = wb.UploadValues(baseURL + api, "POST", data);
95 | return Task.CompletedTask;
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/services/Enums.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Domain
6 | {
7 | public class Enums
8 | {
9 |
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/services/Observers/EmailObserver.cs:
--------------------------------------------------------------------------------
1 | using ApplicationCore.Data.Entities;
2 | using Microsoft.AspNetCore.Identity.UI.Services;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace Domain.Observers
8 | {
9 | public class EmailObserver : IEmailObserver,IStateObserver
10 | {
11 | private readonly IEmailSender _emailSender;
12 |
13 | public EmailObserver(IEmailSender emailSender)
14 | {
15 | _emailSender = emailSender;
16 | }
17 |
18 | public void Update(AddressState State)
19 | {
20 | //Console.WriteLine("State Id '{0}' is updated to '{1}'. An email sent to customer.", State.StateId, State.Name);
21 | _emailSender.SendEmailAsync("cleanarch@naveenalavilli.dev", "State Updated", string.Format("State Id '{0}' is updated to '{1}'. An email sent to customer.", State.StateId, State.Name));
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/services/Observers/IEmailObserver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Domain.Observers
6 | {
7 | public interface IEmailObserver : IStateObserver
8 | {
9 |
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/services/Observers/IStateNotifier.cs:
--------------------------------------------------------------------------------
1 | using ApplicationCore.Data.Entities;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Domain.Observers
7 | {
8 | public interface IStateNotifier
9 | {
10 | // Attach an order observer to the subject.
11 | void Attach(IStateObserver observer);
12 |
13 | // Detach an order observer from the subject.
14 | void Detach(IStateObserver observer);
15 |
16 | // Notify all order observers about an event.
17 | void Notify(AddressState State);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/services/Observers/IStateObserver.cs:
--------------------------------------------------------------------------------
1 | using ApplicationCore.Data.Entities;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Domain.Observers
7 | {
8 | public interface IStateObserver
9 | {
10 | void Update(AddressState State);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/services/ReadMe.md:
--------------------------------------------------------------------------------
1 | Contains Business level service/logic
--------------------------------------------------------------------------------
/services/ViewModels/StatesViewModel.cs:
--------------------------------------------------------------------------------
1 | using ApplicationCore.Data.Entities;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel.DataAnnotations;
5 | using System.Text;
6 |
7 | namespace Domain.ViewModels
8 | {
9 | public class StateViewModel
10 | {
11 | [Key]
12 | public Guid StateId { get; set; }
13 | public AddressState AddressState
14 | {
15 | set; get;
16 | }
17 | public string CreatedBy
18 | {
19 | get; set;
20 | }
21 | public string Country
22 | {
23 | get; set;
24 | }
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/services/contracts/IAddressStateService.cs:
--------------------------------------------------------------------------------
1 | using ApplicationCore.Data.Entities;
2 | using Domain.Observers;
3 | using Domain.ViewModels;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Threading.Tasks;
7 |
8 | namespace services.contracts
9 | {
10 | public interface IAddressStateService:IStateNotifier
11 | {
12 | bool IsAddressStateExist(Guid AddressStateId);
13 |
14 | Task> GetAllAddresses();
15 | Task GetAddress(Guid AddressStateId);
16 |
17 | Task SetAddress(AddressState AddressState);
18 |
19 | Task UpdateAddress(AddressState AddressState);
20 |
21 | Task DeleteAddressState(Guid AddressStateId);
22 | Task DeleteAddressStateByAppUserId(Guid AppUserId);
23 |
24 | //void UpdateOrder(AddressState State);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/web/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "dotnet-ef": {
6 | "version": "3.1.1",
7 | "commands": [
8 | "dotnet-ef"
9 | ]
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/IdentityHostingStartup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ApplicationCore.Data;
3 | using ApplicationCore.Data.Entities;
4 | using Microsoft.AspNetCore.Hosting;
5 | using Microsoft.AspNetCore.Identity;
6 | using Microsoft.AspNetCore.Identity.UI;
7 | using Microsoft.EntityFrameworkCore;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.DependencyInjection;
10 |
11 | [assembly: HostingStartup(typeof(web.Areas.Identity.IdentityHostingStartup))]
12 | namespace web.Areas.Identity
13 | {
14 | public class IdentityHostingStartup : IHostingStartup
15 | {
16 | public void Configure(IWebHostBuilder builder)
17 | {
18 | builder.ConfigureServices((context, services) => {
19 | });
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/AccessDenied.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model AccessDeniedModel
3 | @{
4 | ViewData["Title"] = "Access denied";
5 | }
6 |
7 |
8 | @ViewData["Title"]
9 | You do not have access to this resource.
10 |
11 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc.RazorPages;
6 |
7 | namespace web.Areas.Identity.Pages.Account
8 | {
9 | public class AccessDeniedModel : PageModel
10 | {
11 | public void OnGet()
12 | {
13 |
14 | }
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ConfirmEmailModel
3 | @{
4 | ViewData["Title"] = "Confirm email";
5 | }
6 |
7 | Email Confirmation.
8 |
9 |
10 |
11 |
12 | Please click here to proceed to your account.
13 |
14 |
15 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Authorization;
7 | using ApplicationCore.Data.Entities;
8 | using Microsoft.AspNetCore.Identity;
9 | using Microsoft.AspNetCore.Mvc;
10 | using Microsoft.AspNetCore.Mvc.RazorPages;
11 | using Microsoft.AspNetCore.WebUtilities;
12 | using System.Security.Cryptography;
13 | using System.IO;
14 |
15 | namespace web.Areas.Identity.Pages.Account
16 | {
17 | [AllowAnonymous]
18 | public class ConfirmEmailModel : PageModel
19 | {
20 | private readonly UserManager _userManager;
21 |
22 | public ConfirmEmailModel(UserManager userManager)
23 | {
24 | _userManager = userManager;
25 | }
26 |
27 | [TempData]
28 | public string StatusMessage { get; set; }
29 |
30 | public async Task OnGetAsync(string userId, string code)
31 | {
32 | if (userId == null || code == null)
33 | {
34 | return RedirectToPage("/Index");
35 | }
36 |
37 | var user = await _userManager.FindByIdAsync(userId);
38 | if (user == null)
39 | {
40 | return NotFound($"Unable to load user with ID '{userId}'.");
41 | }
42 |
43 | code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
44 |
45 | if (DecryptString("b14ca5898a4e4133bbce2ea2315a1916", code) == user.Email)
46 | {
47 | user.EmailConfirmed = true;
48 | await _userManager.UpdateAsync(user).ConfigureAwait(false);
49 | StatusMessage = "Thank you for confirming your email.";
50 | }
51 | else
52 | {
53 | StatusMessage = "Error confirming your email.";
54 | }
55 |
56 | //var result = await _userManager.ConfirmEmailAsync(user, code);
57 | //StatusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email.";
58 | return Page();
59 | }
60 |
61 | private string DecryptString(string key, string cipherText)
62 | {
63 | byte[] iv = new byte[16];
64 | byte[] buffer = Convert.FromBase64String(cipherText);
65 |
66 | using (Aes aes = Aes.Create())
67 | {
68 | aes.Key = Encoding.UTF8.GetBytes(key);
69 | aes.IV = iv;
70 | ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
71 |
72 | using (MemoryStream memoryStream = new MemoryStream(buffer))
73 | {
74 | using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
75 | {
76 | using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
77 | {
78 | return streamReader.ReadToEnd();
79 | }
80 | }
81 | }
82 | }
83 | }
84 |
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ConfirmEmailChangeModel
3 | @{
4 | ViewData["Title"] = "Confirm email change";
5 | }
6 |
7 | @ViewData["Title"]
8 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Authorization;
7 | using ApplicationCore.Data.Entities;
8 | using Microsoft.AspNetCore.Identity;
9 | using Microsoft.AspNetCore.Mvc;
10 | using Microsoft.AspNetCore.Mvc.RazorPages;
11 | using Microsoft.AspNetCore.WebUtilities;
12 |
13 | namespace web.Areas.Identity.Pages.Account
14 | {
15 | [AllowAnonymous]
16 | public class ConfirmEmailChangeModel : PageModel
17 | {
18 | private readonly UserManager _userManager;
19 | private readonly SignInManager _signInManager;
20 |
21 | public ConfirmEmailChangeModel(UserManager userManager, SignInManager signInManager)
22 | {
23 | _userManager = userManager;
24 | _signInManager = signInManager;
25 | }
26 |
27 | [TempData]
28 | public string StatusMessage { get; set; }
29 |
30 | public async Task OnGetAsync(string userId, string email, string code)
31 | {
32 | if (userId == null || email == null || code == null)
33 | {
34 | return RedirectToPage("/Index");
35 | }
36 |
37 | var user = await _userManager.FindByIdAsync(userId);
38 | if (user == null)
39 | {
40 | return NotFound($"Unable to load user with ID '{userId}'.");
41 | }
42 |
43 | code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
44 | var result = await _userManager.ChangeEmailAsync(user, email, code);
45 | if (!result.Succeeded)
46 | {
47 | StatusMessage = "Error changing email.";
48 | return Page();
49 | }
50 |
51 | // In our UI email and user name are one and the same, so when we update the email
52 | // we need to update the user name.
53 | var setUserNameResult = await _userManager.SetUserNameAsync(user, email);
54 | if (!setUserNameResult.Succeeded)
55 | {
56 | StatusMessage = "Error changing user name.";
57 | return Page();
58 | }
59 |
60 | await _signInManager.RefreshSignInAsync(user);
61 | StatusMessage = "Thank you for confirming your email change.";
62 | return Page();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ExternalLogin.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ExternalLoginModel
3 | @{
4 | ViewData["Title"] = "Register";
5 | }
6 |
7 | @ViewData["Title"]
8 | Associate your @Model.LoginProvider account.
9 |
10 |
11 |
12 | You've successfully authenticated with @Model.LoginProvider.
13 | Please update these few more details below and click the Register button to finish
14 | logging in.
15 |
16 |
17 |
18 |
19 |
60 |
61 |
62 |
63 | @section Scripts {
64 |
65 |
72 |
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ForgotPassword.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ForgotPasswordModel
3 | @{
4 | ViewData["Title"] = "Forgot your password?";
5 | }
6 |
7 | @ViewData["Title"]
8 | Enter your email.
9 |
10 |
11 |
12 |
21 |
22 |
23 |
24 | @section Scripts {
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Text.Encodings.Web;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Microsoft.AspNetCore.Authorization;
8 | using ApplicationCore.Data.Entities;
9 | using Microsoft.AspNetCore.Identity;
10 | using Microsoft.AspNetCore.Identity.UI.Services;
11 | using Microsoft.AspNetCore.Mvc;
12 | using Microsoft.AspNetCore.Mvc.RazorPages;
13 | using Microsoft.AspNetCore.WebUtilities;
14 |
15 | namespace web.Areas.Identity.Pages.Account
16 | {
17 | [AllowAnonymous]
18 | public class ForgotPasswordModel : PageModel
19 | {
20 | private readonly UserManager _userManager;
21 | private readonly IEmailSender _emailSender;
22 |
23 | public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender)
24 | {
25 | _userManager = userManager;
26 | _emailSender = emailSender;
27 | }
28 |
29 | [BindProperty]
30 | public InputModel Input { get; set; }
31 |
32 | public class InputModel
33 | {
34 | [Required]
35 | [EmailAddress]
36 | public string Email { get; set; }
37 | }
38 |
39 | public async Task OnPostAsync()
40 | {
41 | if (ModelState.IsValid)
42 | {
43 | var user = await _userManager.FindByEmailAsync(Input.Email);
44 | if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
45 | {
46 | // Don't reveal that the user does not exist or is not confirmed
47 | return RedirectToPage("./ForgotPasswordConfirmation");
48 | }
49 |
50 | // For more information on how to enable account confirmation and password reset please
51 | // visit https://go.microsoft.com/fwlink/?LinkID=532713
52 | var code = await _userManager.GeneratePasswordResetTokenAsync(user);
53 | code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
54 | var callbackUrl = Url.Page(
55 | "/Account/ResetPassword",
56 | pageHandler: null,
57 | values: new { area = "Identity", code },
58 | protocol: Request.Scheme);
59 |
60 | await _emailSender.SendEmailAsync(
61 | Input.Email,
62 | "RESET",
63 | $"{HtmlEncoder.Default.Encode(callbackUrl)}").ConfigureAwait(false);
64 |
65 | return RedirectToPage("./ForgotPasswordConfirmation");
66 | }
67 |
68 | return Page();
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ForgotPasswordConfirmation
3 | @{
4 | ViewData["Title"] = "Forgot password confirmation";
5 | }
6 |
7 | @ViewData["Title"]
8 |
9 | Please check your email to reset your password. Check you spam inbox , just in case..
10 |
11 | (You will receive the password reset email , only if you already confirmed your email address when you registered.)
12 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc.RazorPages;
6 |
7 | namespace web.Areas.Identity.Pages.Account
8 | {
9 | [AllowAnonymous]
10 | public class ForgotPasswordConfirmation : PageModel
11 | {
12 | public void OnGet()
13 | {
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Lockout.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model LockoutModel
3 | @{
4 | ViewData["Title"] = "Locked out";
5 | }
6 |
7 |
8 | Your account has been locked!
9 |
10 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Lockout.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Mvc.RazorPages;
7 |
8 | namespace web.Areas.Identity.Pages.Account
9 | {
10 | [AllowAnonymous]
11 | public class LockoutModel : PageModel
12 | {
13 | public void OnGet()
14 | {
15 |
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/LoginWith2fa.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model LoginWith2faModel
3 | @{
4 | ViewData["Title"] = "Two-factor authentication";
5 | }
6 |
7 | @ViewData["Title"]
8 |
9 | Your login is protected with an authenticator app. Enter your authenticator code below.
10 |
11 |
12 |
32 |
33 |
34 |
35 | Don't have access to your authenticator device? You can
36 | log in with a recovery code.
37 |
38 |
39 | @section Scripts {
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Authorization;
7 | using ApplicationCore.Data.Entities;
8 | using Microsoft.AspNetCore.Identity;
9 | using Microsoft.AspNetCore.Mvc;
10 | using Microsoft.AspNetCore.Mvc.RazorPages;
11 | using Microsoft.Extensions.Logging;
12 |
13 | namespace web.Areas.Identity.Pages.Account
14 | {
15 | [AllowAnonymous]
16 | public class LoginWith2faModel : PageModel
17 | {
18 | private readonly SignInManager _signInManager;
19 | private readonly ILogger _logger;
20 |
21 | public LoginWith2faModel(SignInManager signInManager, ILogger logger)
22 | {
23 | _signInManager = signInManager;
24 | _logger = logger;
25 | }
26 |
27 | [BindProperty]
28 | public InputModel Input { get; set; }
29 |
30 | public bool RememberMe { get; set; }
31 |
32 | public string ReturnUrl { get; set; }
33 |
34 | public class InputModel
35 | {
36 | [Required]
37 | [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
38 | [DataType(DataType.Text)]
39 | [Display(Name = "Authenticator code")]
40 | public string TwoFactorCode { get; set; }
41 |
42 | [Display(Name = "Remember this machine")]
43 | public bool RememberMachine { get; set; }
44 | }
45 |
46 | public async Task OnGetAsync(bool rememberMe, string returnUrl = null)
47 | {
48 | // Ensure the user has gone through the username & password screen first
49 | var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
50 |
51 | if (user == null)
52 | {
53 | throw new InvalidOperationException($"Unable to load two-factor authentication user.");
54 | }
55 |
56 | ReturnUrl = returnUrl;
57 | RememberMe = rememberMe;
58 |
59 | return Page();
60 | }
61 |
62 | public async Task OnPostAsync(bool rememberMe, string returnUrl = null)
63 | {
64 | if (!ModelState.IsValid)
65 | {
66 | return Page();
67 | }
68 |
69 | returnUrl = returnUrl ?? Url.Content("~/");
70 |
71 | var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
72 | if (user == null)
73 | {
74 | throw new InvalidOperationException($"Unable to load two-factor authentication user.");
75 | }
76 |
77 | var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);
78 |
79 | var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine);
80 |
81 | if (result.Succeeded)
82 | {
83 | _logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id);
84 | return LocalRedirect(returnUrl);
85 | }
86 | else if (result.IsLockedOut)
87 | {
88 | _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
89 | return RedirectToPage("./Lockout");
90 | }
91 | else
92 | {
93 | _logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id);
94 | ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
95 | return Page();
96 | }
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model LoginWithRecoveryCodeModel
3 | @{
4 | ViewData["Title"] = "Recovery code verification";
5 | }
6 |
7 | @ViewData["Title"]
8 |
9 |
10 | You have requested to log in with a recovery code. This login will not be remembered until you provide
11 | an authenticator app code at log in or disable 2FA and log in again.
12 |
13 |
14 |
15 |
24 |
25 |
26 |
27 | @section Scripts {
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Authorization;
7 | using ApplicationCore.Data.Entities;
8 | using Microsoft.AspNetCore.Identity;
9 | using Microsoft.AspNetCore.Mvc;
10 | using Microsoft.AspNetCore.Mvc.RazorPages;
11 | using Microsoft.Extensions.Logging;
12 |
13 | namespace web.Areas.Identity.Pages.Account
14 | {
15 | [AllowAnonymous]
16 | public class LoginWithRecoveryCodeModel : PageModel
17 | {
18 | private readonly SignInManager _signInManager;
19 | private readonly ILogger _logger;
20 |
21 | public LoginWithRecoveryCodeModel(SignInManager signInManager, ILogger logger)
22 | {
23 | _signInManager = signInManager;
24 | _logger = logger;
25 | }
26 |
27 | [BindProperty]
28 | public InputModel Input { get; set; }
29 |
30 | public string ReturnUrl { get; set; }
31 |
32 | public class InputModel
33 | {
34 | [BindProperty]
35 | [Required]
36 | [DataType(DataType.Text)]
37 | [Display(Name = "Recovery Code")]
38 | public string RecoveryCode { get; set; }
39 | }
40 |
41 | public async Task OnGetAsync(string returnUrl = null)
42 | {
43 | // Ensure the user has gone through the username & password screen first
44 | var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
45 | if (user == null)
46 | {
47 | throw new InvalidOperationException($"Unable to load two-factor authentication user.");
48 | }
49 |
50 | ReturnUrl = returnUrl;
51 |
52 | return Page();
53 | }
54 |
55 | public async Task OnPostAsync(string returnUrl = null)
56 | {
57 | if (!ModelState.IsValid)
58 | {
59 | return Page();
60 | }
61 |
62 | var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
63 | if (user == null)
64 | {
65 | throw new InvalidOperationException($"Unable to load two-factor authentication user.");
66 | }
67 |
68 | var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty);
69 |
70 | var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
71 |
72 | if (result.Succeeded)
73 | {
74 | _logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id);
75 | return LocalRedirect(returnUrl ?? Url.Content("~/"));
76 | }
77 | if (result.IsLockedOut)
78 | {
79 | _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
80 | return RedirectToPage("./Lockout");
81 | }
82 | else
83 | {
84 | _logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id);
85 | ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
86 | return Page();
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Logout.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model LogoutModel
3 | @{
4 | ViewData["Title"] = "Log out";
5 | }
6 |
7 |
8 | @ViewData["Title"]
9 | You have successfully logged out.
10 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Logout.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Authorization;
6 | using ApplicationCore.Data.Entities;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.AspNetCore.Mvc;
9 | using Microsoft.AspNetCore.Mvc.RazorPages;
10 | using Microsoft.Extensions.Logging;
11 |
12 | namespace web.Areas.Identity.Pages.Account
13 | {
14 | [AllowAnonymous]
15 | [IgnoreAntiforgeryToken]
16 | public class LogoutModel : PageModel
17 | {
18 | private readonly SignInManager _signInManager;
19 | private readonly ILogger _logger;
20 |
21 | public LogoutModel(SignInManager signInManager, ILogger logger)
22 | {
23 | _signInManager = signInManager;
24 | _logger = logger;
25 | }
26 |
27 | public void OnGet()
28 | {
29 | }
30 |
31 |
32 | public async Task OnPost(string returnUrl = null)
33 | {
34 | await _signInManager.SignOutAsync();
35 | _logger.LogInformation("User logged out.");
36 | if (returnUrl != null)
37 | {
38 | return LocalRedirect(returnUrl);
39 | }
40 | else
41 | {
42 | return RedirectToPage();
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ChangePasswordModel
3 | @{
4 | ViewData["Title"] = "Change password";
5 | ViewData["ActivePage"] = ManageNavPages.ChangePassword;
6 | }
7 |
8 | @ViewData["Title"]
9 |
10 |
11 |
12 |
31 |
32 |
33 |
34 | @section Scripts {
35 |
36 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using ApplicationCore.Data.Entities;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.AspNetCore.Mvc;
9 | using Microsoft.AspNetCore.Mvc.RazorPages;
10 | using Microsoft.Extensions.Logging;
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class ChangePasswordModel : PageModel
14 | {
15 | private readonly UserManager _userManager;
16 | private readonly SignInManager _signInManager;
17 | private readonly ILogger _logger;
18 |
19 | public ChangePasswordModel(
20 | UserManager userManager,
21 | SignInManager signInManager,
22 | ILogger logger)
23 | {
24 | _userManager = userManager;
25 | _signInManager = signInManager;
26 | _logger = logger;
27 | }
28 |
29 | [BindProperty]
30 | public InputModel Input { get; set; }
31 |
32 | [TempData]
33 | public string StatusMessage { get; set; }
34 |
35 | public class InputModel
36 | {
37 | [Required]
38 | [DataType(DataType.Password)]
39 | [Display(Name = "Current password")]
40 | public string OldPassword { get; set; }
41 |
42 | [Required]
43 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
44 | [DataType(DataType.Password)]
45 | [Display(Name = "New password")]
46 | public string NewPassword { get; set; }
47 |
48 | [DataType(DataType.Password)]
49 | [Display(Name = "Confirm new password")]
50 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
51 | public string ConfirmPassword { get; set; }
52 | }
53 |
54 | public async Task OnGetAsync()
55 | {
56 | var user = await _userManager.GetUserAsync(User);
57 | if (user == null)
58 | {
59 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
60 | }
61 |
62 | var hasPassword = await _userManager.HasPasswordAsync(user);
63 | if (!hasPassword)
64 | {
65 | return RedirectToPage("./SetPassword");
66 | }
67 |
68 | return Page();
69 | }
70 |
71 | public async Task OnPostAsync()
72 | {
73 | if (!ModelState.IsValid)
74 | {
75 | return Page();
76 | }
77 |
78 | var user = await _userManager.GetUserAsync(User);
79 | if (user == null)
80 | {
81 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
82 | }
83 |
84 | var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword);
85 | if (!changePasswordResult.Succeeded)
86 | {
87 | foreach (var error in changePasswordResult.Errors)
88 | {
89 | ModelState.AddModelError(string.Empty, error.Description);
90 | }
91 | return Page();
92 | }
93 |
94 | await _signInManager.RefreshSignInAsync(user);
95 | _logger.LogInformation("User changed their password successfully.");
96 | StatusMessage = "Your password has been changed.";
97 |
98 | return RedirectToPage();
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model DeletePersonalDataModel
3 | @{
4 | ViewData["Title"] = "Delete Personal Data";
5 | ViewData["ActivePage"] = ManageNavPages.PersonalData;
6 | }
7 |
8 | Delete my account.
9 |
10 |
11 |
12 | Delete all my content and unsubscribe from MyApp.
13 |
14 |
15 |
16 |
17 |
29 |
30 |
31 | @section Scripts {
32 |
33 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.Threading.Tasks;
4 | using ApplicationCore.Data.Entities;
5 | using Microsoft.AspNetCore.Identity;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.AspNetCore.Mvc.RazorPages;
8 | using Microsoft.Extensions.Logging;
9 | using services.contracts;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class DeletePersonalDataModel : PageModel
14 | {
15 | private readonly UserManager _userManager;
16 | private readonly SignInManager _signInManager;
17 | private readonly ILogger _logger;
18 | private readonly IAddressStateService _addressStateService;
19 |
20 | public DeletePersonalDataModel(
21 | UserManager userManager,
22 | SignInManager signInManager,
23 | ILogger logger,
24 | IAddressStateService addressStateService)
25 | {
26 | _userManager = userManager;
27 | _signInManager = signInManager;
28 | _logger = logger;
29 | _addressStateService = addressStateService;
30 | }
31 |
32 | [BindProperty]
33 | public InputModel Input { get; set; }
34 |
35 | public class InputModel
36 | {
37 | [Required]
38 | [DataType(DataType.Password)]
39 | public string Password { get; set; }
40 | }
41 |
42 | public bool RequirePassword { get; set; }
43 |
44 | public async Task OnGet()
45 | {
46 | var user = await _userManager.GetUserAsync(User);
47 | if (user == null)
48 | {
49 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
50 | }
51 |
52 | RequirePassword = await _userManager.HasPasswordAsync(user);
53 | return Page();
54 | }
55 |
56 | public async Task OnPostAsync()
57 | {
58 | var user = await _userManager.GetUserAsync(User);
59 | if (user == null)
60 | {
61 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
62 | }
63 |
64 | RequirePassword = await _userManager.HasPasswordAsync(user);
65 | if (RequirePassword)
66 | {
67 | if (!await _userManager.CheckPasswordAsync(user, Input.Password))
68 | {
69 | ModelState.AddModelError(string.Empty, "Incorrect password.");
70 | return Page();
71 | }
72 | }
73 | await _addressStateService.DeleteAddressStateByAppUserId(user.Id);
74 |
75 | var result = await _userManager.DeleteAsync(user);
76 | var userId = await _userManager.GetUserIdAsync(user);
77 | if (!result.Succeeded)
78 | {
79 | throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'.");
80 | }
81 |
82 |
83 | await _signInManager.SignOutAsync();
84 |
85 | _logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
86 |
87 | return Redirect("~/");
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model Disable2faModel
3 | @{
4 | ViewData["Title"] = "Disable two-factor authentication (2FA)";
5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
6 | }
7 |
8 |
9 | @ViewData["Title"]
10 |
11 |
12 |
13 | This action only disables 2FA.
14 |
15 |
16 | Disabling 2FA does not change the keys used in authenticator apps. If you wish to change the key
17 | used in an authenticator app you should reset your authenticator keys.
18 |
19 |
20 |
21 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using ApplicationCore.Data.Entities;
6 | using Microsoft.AspNetCore.Identity;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.AspNetCore.Mvc.RazorPages;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class Disable2faModel : PageModel
14 | {
15 | private readonly UserManager _userManager;
16 | private readonly ILogger _logger;
17 |
18 | public Disable2faModel(
19 | UserManager userManager,
20 | ILogger logger)
21 | {
22 | _userManager = userManager;
23 | _logger = logger;
24 | }
25 |
26 | [TempData]
27 | public string StatusMessage { get; set; }
28 |
29 | public async Task OnGet()
30 | {
31 | var user = await _userManager.GetUserAsync(User);
32 | if (user == null)
33 | {
34 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
35 | }
36 |
37 | if (!await _userManager.GetTwoFactorEnabledAsync(user))
38 | {
39 | throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled.");
40 | }
41 |
42 | return Page();
43 | }
44 |
45 | public async Task OnPostAsync()
46 | {
47 | var user = await _userManager.GetUserAsync(User);
48 | if (user == null)
49 | {
50 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
51 | }
52 |
53 | var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false);
54 | if (!disable2faResult.Succeeded)
55 | {
56 | throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'.");
57 | }
58 |
59 | _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User));
60 | StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app";
61 | return RedirectToPage("./TwoFactorAuthentication");
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model DownloadPersonalDataModel
3 | @{
4 | ViewData["Title"] = "Download Your Data";
5 | ViewData["ActivePage"] = ManageNavPages.PersonalData;
6 | }
7 |
8 | @ViewData["Title"]
9 |
10 | @section Scripts {
11 |
12 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using ApplicationCore.Data.Entities;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.AspNetCore.Mvc;
9 | using Microsoft.AspNetCore.Mvc.RazorPages;
10 | using Microsoft.Extensions.Logging;
11 | using Newtonsoft.Json;
12 |
13 | namespace web.Areas.Identity.Pages.Account.Manage
14 | {
15 | public class DownloadPersonalDataModel : PageModel
16 | {
17 | private readonly UserManager _userManager;
18 | private readonly ILogger _logger;
19 |
20 | public DownloadPersonalDataModel(
21 | UserManager userManager,
22 | ILogger logger)
23 | {
24 | _userManager = userManager;
25 | _logger = logger;
26 | }
27 |
28 | public async Task OnPostAsync()
29 | {
30 | var user = await _userManager.GetUserAsync(User);
31 | if (user == null)
32 | {
33 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
34 | }
35 |
36 | _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User));
37 |
38 | // Only include personal data for download
39 | var personalData = new Dictionary();
40 | var personalDataProps = typeof(ApplicationUser).GetProperties().Where(
41 | prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute)));
42 | foreach (var p in personalDataProps)
43 | {
44 | personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null");
45 | }
46 |
47 | Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json");
48 | return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json");
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/Email.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model EmailModel
3 | @{
4 | ViewData["Title"] = "Manage Email";
5 | ViewData["ActivePage"] = ManageNavPages.Email;
6 | }
7 |
8 | @ViewData["Title"]
9 |
10 |
11 |
12 |
38 |
39 |
40 |
41 | @section Scripts {
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Text;
5 | using System.Text.Encodings.Web;
6 | using System.Linq;
7 | using System.Threading.Tasks;
8 | using ApplicationCore.Data.Entities;
9 | using Microsoft.AspNetCore.Identity;
10 | using Microsoft.AspNetCore.Identity.UI.Services;
11 | using Microsoft.AspNetCore.Mvc;
12 | using Microsoft.AspNetCore.Mvc.RazorPages;
13 | using Microsoft.AspNetCore.WebUtilities;
14 |
15 | namespace web.Areas.Identity.Pages.Account.Manage
16 | {
17 | public partial class EmailModel : PageModel
18 | {
19 | private readonly UserManager _userManager;
20 | private readonly SignInManager _signInManager;
21 | private readonly IEmailSender _emailSender;
22 |
23 | public EmailModel(
24 | UserManager userManager,
25 | SignInManager signInManager,
26 | IEmailSender emailSender)
27 | {
28 | _userManager = userManager;
29 | _signInManager = signInManager;
30 | _emailSender = emailSender;
31 | }
32 |
33 | public string Username { get; set; }
34 |
35 | public string Email { get; set; }
36 |
37 | public bool IsEmailConfirmed { get; set; }
38 |
39 | [TempData]
40 | public string StatusMessage { get; set; }
41 |
42 | [BindProperty]
43 | public InputModel Input { get; set; }
44 |
45 | public class InputModel
46 | {
47 | [Required]
48 | [EmailAddress]
49 | [Display(Name = "New email")]
50 | public string NewEmail { get; set; }
51 | }
52 |
53 | private async Task LoadAsync(ApplicationUser user)
54 | {
55 | var email = await _userManager.GetEmailAsync(user);
56 | Email = email;
57 |
58 | Input = new InputModel
59 | {
60 | NewEmail = email,
61 | };
62 |
63 | IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
64 | }
65 |
66 | public async Task OnGetAsync()
67 | {
68 | var user = await _userManager.GetUserAsync(User);
69 | if (user == null)
70 | {
71 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
72 | }
73 |
74 | await LoadAsync(user);
75 | return Page();
76 | }
77 |
78 | public async Task OnPostChangeEmailAsync()
79 | {
80 | var user = await _userManager.GetUserAsync(User);
81 | if (user == null)
82 | {
83 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
84 | }
85 |
86 | if (!ModelState.IsValid)
87 | {
88 | await LoadAsync(user);
89 | return Page();
90 | }
91 |
92 | var email = await _userManager.GetEmailAsync(user);
93 | if (Input.NewEmail != email)
94 | {
95 | var userId = await _userManager.GetUserIdAsync(user);
96 | var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail);
97 | var callbackUrl = Url.Page(
98 | "/Account/ConfirmEmailChange",
99 | pageHandler: null,
100 | values: new { userId = userId, email = Input.NewEmail, code = code },
101 | protocol: Request.Scheme);
102 | await _emailSender.SendEmailAsync(
103 | Input.NewEmail,
104 | "CONFIRM",
105 | $"{HtmlEncoder.Default.Encode(callbackUrl)}");
106 |
107 | StatusMessage = "Confirmation link to change email sent. Please check your email.";
108 | return RedirectToPage();
109 | }
110 |
111 | StatusMessage = "Your email is unchanged.";
112 | return RedirectToPage();
113 | }
114 |
115 | public async Task OnPostSendVerificationEmailAsync()
116 | {
117 | var user = await _userManager.GetUserAsync(User);
118 | if (user == null)
119 | {
120 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
121 | }
122 |
123 | if (!ModelState.IsValid)
124 | {
125 | await LoadAsync(user);
126 | return Page();
127 | }
128 |
129 | var userId = await _userManager.GetUserIdAsync(user);
130 | var email = await _userManager.GetEmailAsync(user);
131 | var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
132 | code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
133 | var callbackUrl = Url.Page(
134 | "/Account/ConfirmEmail",
135 | pageHandler: null,
136 | values: new { area = "Identity", userId = userId, code = code },
137 | protocol: Request.Scheme);
138 | await _emailSender.SendEmailAsync(
139 | email,
140 | "CONFIRM",
141 | $"{HtmlEncoder.Default.Encode(callbackUrl)}");
142 |
143 | StatusMessage = "Verification email sent. Please check your email.";
144 | return RedirectToPage();
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model EnableAuthenticatorModel
3 | @{
4 | ViewData["Title"] = "Configure authenticator app";
5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
6 | }
7 |
8 |
9 | @ViewData["Title"]
10 |
11 | To use an authenticator app go through the following steps:
12 |
13 | -
14 |
15 | Download a two-factor authenticator app like Microsoft Authenticator for
16 | Windows Phone,
17 | Android and
18 | iOS or
19 | Google Authenticator for
20 | Android and
21 | iOS.
22 |
23 |
24 | -
25 |
Scan the QR Code or enter this key @Model.SharedKey into your two factor authenticator app. Spaces and casing do not matter.
26 | Learn how to enable QR code generation.
27 |
28 |
29 |
30 | -
31 |
32 | Once you have scanned the QR code or input the key above, your two factor authentication app will provide you
33 | with a unique code. Enter the code in the confirmation box below.
34 |
35 |
36 |
37 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | @section Scripts {
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ExternalLoginsModel
3 | @{
4 | ViewData["Title"] = "Manage your external logins";
5 | ViewData["ActivePage"] = ManageNavPages.ExternalLogins;
6 | }
7 |
8 |
9 | @if (Model.CurrentLogins?.Count > 0)
10 | {
11 | Registered Logins
12 |
13 |
14 | @foreach (var login in Model.CurrentLogins)
15 | {
16 |
17 | @login.ProviderDisplayName
18 |
19 | @if (Model.ShowRemoveButton)
20 | {
21 |
28 | }
29 | else
30 | {
31 | @:
32 | }
33 |
34 |
35 | }
36 |
37 |
38 | }
39 | @if (Model.OtherLogins?.Count > 0)
40 | {
41 | Add your social accounts.
42 |
43 |
67 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using ApplicationCore.Data.Entities;
6 | using Microsoft.AspNetCore.Authentication;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.AspNetCore.Mvc;
9 | using Microsoft.AspNetCore.Mvc.RazorPages;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class ExternalLoginsModel : PageModel
14 | {
15 | private readonly UserManager _userManager;
16 | private readonly SignInManager _signInManager;
17 |
18 | public ExternalLoginsModel(
19 | UserManager userManager,
20 | SignInManager signInManager)
21 | {
22 | _userManager = userManager;
23 | _signInManager = signInManager;
24 | }
25 |
26 | public IList CurrentLogins { get; set; }
27 |
28 | public IList OtherLogins { get; set; }
29 |
30 | public bool ShowRemoveButton { get; set; }
31 |
32 | [TempData]
33 | public string StatusMessage { get; set; }
34 |
35 | public async Task OnGetAsync()
36 | {
37 | var user = await _userManager.GetUserAsync(User);
38 | if (user == null)
39 | {
40 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
41 | }
42 |
43 | CurrentLogins = await _userManager.GetLoginsAsync(user);
44 | OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
45 | .Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider))
46 | .ToList();
47 | ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1;
48 | return Page();
49 | }
50 |
51 | public async Task OnPostRemoveLoginAsync(string loginProvider, string providerKey)
52 | {
53 | var user = await _userManager.GetUserAsync(User);
54 | if (user == null)
55 | {
56 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
57 | }
58 |
59 | var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey);
60 | if (!result.Succeeded)
61 | {
62 | StatusMessage = "The external login was not removed.";
63 | return RedirectToPage();
64 | }
65 |
66 | await _signInManager.RefreshSignInAsync(user);
67 | StatusMessage = "The external login was removed.";
68 | return RedirectToPage();
69 | }
70 |
71 | public async Task OnPostLinkLoginAsync(string provider)
72 | {
73 | // Clear the existing external cookie to ensure a clean login process
74 | await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
75 |
76 | // Request a redirect to the external login provider to link a login for the current user
77 | var redirectUrl = Url.Page("./ExternalLogins", pageHandler: "LinkLoginCallback");
78 | var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
79 | return new ChallengeResult(provider, properties);
80 | }
81 |
82 | public async Task OnGetLinkLoginCallbackAsync()
83 | {
84 | var user = await _userManager.GetUserAsync(User);
85 | if (user == null)
86 | {
87 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
88 | }
89 |
90 | var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
91 | if (info == null)
92 | {
93 | throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'.");
94 | }
95 |
96 | var result = await _userManager.AddLoginAsync(user, info);
97 | if (!result.Succeeded)
98 | {
99 | StatusMessage = "The external login was not added. External logins can only be associated with one account.";
100 | return RedirectToPage();
101 | }
102 |
103 | // Clear the existing external cookie to ensure a clean login process
104 | await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
105 |
106 | StatusMessage = "The external login was added.";
107 | return RedirectToPage();
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model GenerateRecoveryCodesModel
3 | @{
4 | ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes";
5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
6 | }
7 |
8 |
9 | @ViewData["Title"]
10 |
11 |
12 |
13 | Put these codes in a safe place.
14 |
15 |
16 | If you lose your device and don't have the recovery codes you will lose access to your account.
17 |
18 |
19 | Generating new recovery codes does not change the keys used in authenticator apps. If you wish to change the key
20 | used in an authenticator app you should reset your authenticator keys.
21 |
22 |
23 |
24 |
27 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using ApplicationCore.Data.Entities;
6 | using Microsoft.AspNetCore.Identity;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.AspNetCore.Mvc.RazorPages;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class GenerateRecoveryCodesModel : PageModel
14 | {
15 | private readonly UserManager _userManager;
16 | private readonly ILogger _logger;
17 |
18 | public GenerateRecoveryCodesModel(
19 | UserManager userManager,
20 | ILogger logger)
21 | {
22 | _userManager = userManager;
23 | _logger = logger;
24 | }
25 |
26 | [TempData]
27 | public string[] RecoveryCodes { get; set; }
28 |
29 | [TempData]
30 | public string StatusMessage { get; set; }
31 |
32 | public async Task OnGetAsync()
33 | {
34 | var user = await _userManager.GetUserAsync(User);
35 | if (user == null)
36 | {
37 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
38 | }
39 |
40 | var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
41 | if (!isTwoFactorEnabled)
42 | {
43 | var userId = await _userManager.GetUserIdAsync(user);
44 | throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled.");
45 | }
46 |
47 | return Page();
48 | }
49 |
50 | public async Task OnPostAsync()
51 | {
52 | var user = await _userManager.GetUserAsync(User);
53 | if (user == null)
54 | {
55 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
56 | }
57 |
58 | var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
59 | var userId = await _userManager.GetUserIdAsync(user);
60 | if (!isTwoFactorEnabled)
61 | {
62 | throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled.");
63 | }
64 |
65 | var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
66 | RecoveryCodes = recoveryCodes.ToArray();
67 |
68 | _logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId);
69 | StatusMessage = "You have generated new recovery codes.";
70 | return RedirectToPage("./ShowRecoveryCodes");
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/Index.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model IndexModel
3 | @{
4 | ViewData["Title"] = "My Profile";
5 | ViewData["ActivePage"] = ManageNavPages.Index;
6 | }
7 |
8 | @ViewData["Title"]
9 |
10 |
11 |
56 |
57 | @section Scripts {
58 |
59 |
70 | }
71 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using ApplicationCore.Data.Entities;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.AspNetCore.Mvc;
9 | using Microsoft.AspNetCore.Mvc.RazorPages;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public partial class IndexModel : PageModel
14 | {
15 | private readonly UserManager _userManager;
16 | private readonly SignInManager _signInManager;
17 |
18 | public IndexModel(
19 | UserManager userManager,
20 | SignInManager signInManager)
21 | {
22 | _userManager = userManager;
23 | _signInManager = signInManager;
24 | }
25 |
26 | public string Username { get; set; }
27 |
28 | [TempData]
29 | public string StatusMessage { get; set; }
30 |
31 | [BindProperty]
32 | public InputModel Input { get; set; }
33 |
34 | public class InputModel
35 | {
36 | [Required]
37 | [DataType(DataType.Text)]
38 | [Display(Name = "First Name")]
39 | public string FirstName { get; set; }
40 |
41 | [Required]
42 | [DataType(DataType.Text)]
43 | [Display(Name = "Last Name")]
44 | public string LastName { get; set; }
45 | }
46 |
47 | private async Task LoadAsync(ApplicationUser user)
48 | {
49 | var userName = await _userManager.GetUserNameAsync(user);
50 | var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
51 |
52 | Username = userName;
53 |
54 | Input = new InputModel
55 | {
56 | FirstName= user.FirstName ,
57 | LastName=user.LastName ,
58 | };
59 | }
60 |
61 | public async Task OnGetAsync()
62 | {
63 | var user = await _userManager.GetUserAsync(User);
64 | if (user == null)
65 | {
66 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
67 | }
68 |
69 | await LoadAsync(user);
70 | return Page();
71 | }
72 |
73 | public async Task OnPostAsync()
74 | {
75 | var user = await _userManager.GetUserAsync(User);
76 | if (user == null)
77 | {
78 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
79 | }
80 |
81 | if (!ModelState.IsValid)
82 | {
83 | await LoadAsync(user);
84 | return Page();
85 | }
86 |
87 | if (Input.FirstName != user.FirstName)
88 | {
89 | user.FirstName = Input.FirstName;
90 | }
91 |
92 |
93 | if (Input.LastName != user.LastName )
94 | {
95 | user.LastName = Input.LastName ;
96 | }
97 |
98 |
99 | await _userManager.UpdateAsync(user);
100 |
101 | await _signInManager.RefreshSignInAsync(user);
102 | StatusMessage = "Your profile has been updated";
103 | return RedirectToPage();
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc.Rendering;
6 |
7 | namespace web.Areas.Identity.Pages.Account.Manage
8 | {
9 | public static class ManageNavPages
10 | {
11 | public static string Index => "Index";
12 |
13 | public static string Email => "Email";
14 |
15 | public static string ChangePassword => "ChangePassword";
16 |
17 | public static string ExternalLogins => "ExternalLogins";
18 |
19 | public static string PersonalData => "PersonalData";
20 |
21 | public static string TwoFactorAuthentication => "TwoFactorAuthentication";
22 |
23 | public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
24 |
25 | public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email);
26 |
27 | public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword);
28 |
29 | public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins);
30 |
31 | public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData);
32 |
33 | public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);
34 |
35 | private static string PageNavClass(ViewContext viewContext, string page)
36 | {
37 | var activePage = viewContext.ViewData["ActivePage"] as string
38 | ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName);
39 | return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model PersonalDataModel
3 | @{
4 | ViewData["Title"] = "Personal Data";
5 | ViewData["ActivePage"] = ManageNavPages.PersonalData;
6 | }
7 |
8 | @ViewData["Title"]
9 |
10 |
11 |
12 | @*Your account contains personal data that you have given us. This page allows you to download or delete that data.
*@
13 |
14 | You are about to delete your account
15 |
16 | @**@
19 |
20 | Delete
21 |
22 |
23 |
24 |
25 | @section Scripts {
26 |
27 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using ApplicationCore.Data.Entities;
3 | using Microsoft.AspNetCore.Identity;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.AspNetCore.Mvc.RazorPages;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace web.Areas.Identity.Pages.Account.Manage
9 | {
10 | public class PersonalDataModel : PageModel
11 | {
12 | private readonly UserManager _userManager;
13 | private readonly ILogger _logger;
14 |
15 | public PersonalDataModel(
16 | UserManager userManager,
17 | ILogger logger)
18 | {
19 | _userManager = userManager;
20 | _logger = logger;
21 | }
22 |
23 | public async Task OnGet()
24 | {
25 | var user = await _userManager.GetUserAsync(User);
26 | if (user == null)
27 | {
28 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
29 | }
30 |
31 | return Page();
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ResetAuthenticatorModel
3 | @{
4 | ViewData["Title"] = "Reset authenticator key";
5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
6 | }
7 |
8 |
9 | @ViewData["Title"]
10 |
11 |
12 |
13 | If you reset your authenticator key your authenticator app will not work until you reconfigure it.
14 |
15 |
16 | This process disables 2FA until you verify your authenticator app.
17 | If you do not complete your authenticator app configuration you may lose access to your account.
18 |
19 |
20 |
21 |
24 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using ApplicationCore.Data.Entities;
6 | using Microsoft.AspNetCore.Identity;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.AspNetCore.Mvc.RazorPages;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class ResetAuthenticatorModel : PageModel
14 | {
15 | UserManager _userManager;
16 | private readonly SignInManager _signInManager;
17 | ILogger _logger;
18 |
19 | public ResetAuthenticatorModel(
20 | UserManager userManager,
21 | SignInManager signInManager,
22 | ILogger logger)
23 | {
24 | _userManager = userManager;
25 | _signInManager = signInManager;
26 | _logger = logger;
27 | }
28 |
29 | [TempData]
30 | public string StatusMessage { get; set; }
31 |
32 | public async Task OnGet()
33 | {
34 | var user = await _userManager.GetUserAsync(User);
35 | if (user == null)
36 | {
37 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
38 | }
39 |
40 | return Page();
41 | }
42 |
43 | public async Task OnPostAsync()
44 | {
45 | var user = await _userManager.GetUserAsync(User);
46 | if (user == null)
47 | {
48 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
49 | }
50 |
51 | await _userManager.SetTwoFactorEnabledAsync(user, false);
52 | await _userManager.ResetAuthenticatorKeyAsync(user);
53 | _logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id);
54 |
55 | await _signInManager.RefreshSignInAsync(user);
56 | StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key.";
57 |
58 | return RedirectToPage("./EnableAuthenticator");
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model SetPasswordModel
3 | @{
4 | ViewData["Title"] = "Set password";
5 | ViewData["ActivePage"] = ManageNavPages.ChangePassword;
6 | }
7 |
8 | Set your password
9 |
10 |
11 | You do not have a local username/password for this site. Add a local
12 | account so you can log in without an external login.
13 |
14 |
15 |
16 |
30 |
31 |
32 |
33 | @section Scripts {
34 |
35 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using ApplicationCore.Data.Entities;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.AspNetCore.Mvc;
9 | using Microsoft.AspNetCore.Mvc.RazorPages;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class SetPasswordModel : PageModel
14 | {
15 | private readonly UserManager _userManager;
16 | private readonly SignInManager _signInManager;
17 |
18 | public SetPasswordModel(
19 | UserManager userManager,
20 | SignInManager signInManager)
21 | {
22 | _userManager = userManager;
23 | _signInManager = signInManager;
24 | }
25 |
26 | [BindProperty]
27 | public InputModel Input { get; set; }
28 |
29 | [TempData]
30 | public string StatusMessage { get; set; }
31 |
32 | public class InputModel
33 | {
34 | [Required]
35 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
36 | [DataType(DataType.Password)]
37 | [Display(Name = "New password")]
38 | public string NewPassword { get; set; }
39 |
40 | [DataType(DataType.Password)]
41 | [Display(Name = "Confirm new password")]
42 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
43 | public string ConfirmPassword { get; set; }
44 | }
45 |
46 | public async Task OnGetAsync()
47 | {
48 | var user = await _userManager.GetUserAsync(User);
49 | if (user == null)
50 | {
51 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
52 | }
53 |
54 | var hasPassword = await _userManager.HasPasswordAsync(user);
55 |
56 | if (hasPassword)
57 | {
58 | return RedirectToPage("./ChangePassword");
59 | }
60 |
61 | return Page();
62 | }
63 |
64 | public async Task OnPostAsync()
65 | {
66 | if (!ModelState.IsValid)
67 | {
68 | return Page();
69 | }
70 |
71 | var user = await _userManager.GetUserAsync(User);
72 | if (user == null)
73 | {
74 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
75 | }
76 |
77 | var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword);
78 | if (!addPasswordResult.Succeeded)
79 | {
80 | foreach (var error in addPasswordResult.Errors)
81 | {
82 | ModelState.AddModelError(string.Empty, error.Description);
83 | }
84 | return Page();
85 | }
86 |
87 | await _signInManager.RefreshSignInAsync(user);
88 | StatusMessage = "Your password has been set.";
89 |
90 | return RedirectToPage();
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ShowRecoveryCodesModel
3 | @{
4 | ViewData["Title"] = "Recovery codes";
5 | ViewData["ActivePage"] = "TwoFactorAuthentication";
6 | }
7 |
8 |
9 | @ViewData["Title"]
10 |
11 |
12 | Put these codes in a safe place.
13 |
14 |
15 | If you lose your device and don't have the recovery codes you will lose access to your account.
16 |
17 |
18 |
19 |
20 | @for (var row = 0; row < Model.RecoveryCodes.Length; row += 2)
21 | {
22 | @Model.RecoveryCodes[row]
@Model.RecoveryCodes[row + 1]
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using ApplicationCore.Data.Entities;
6 | using Microsoft.AspNetCore.Identity;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.AspNetCore.Mvc.RazorPages;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class ShowRecoveryCodesModel : PageModel
14 | {
15 | [TempData]
16 | public string[] RecoveryCodes { get; set; }
17 |
18 | [TempData]
19 | public string StatusMessage { get; set; }
20 |
21 | public IActionResult OnGet()
22 | {
23 | if (RecoveryCodes == null || RecoveryCodes.Length == 0)
24 | {
25 | return RedirectToPage("./TwoFactorAuthentication");
26 | }
27 |
28 | return Page();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model TwoFactorAuthenticationModel
3 | @{
4 | ViewData["Title"] = "Two-factor authentication (2FA)";
5 | ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication;
6 | }
7 |
8 |
9 | @ViewData["Title"]
10 | @if (Model.Is2faEnabled)
11 | {
12 | if (Model.RecoveryCodesLeft == 0)
13 | {
14 |
15 | You have no recovery codes left.
16 | You must generate a new set of recovery codes before you can log in with a recovery code.
17 |
18 | }
19 | else if (Model.RecoveryCodesLeft == 1)
20 | {
21 |
22 | You have 1 recovery code left.
23 | You can generate a new set of recovery codes.
24 |
25 | }
26 | else if (Model.RecoveryCodesLeft <= 3)
27 | {
28 |
29 | You have @Model.RecoveryCodesLeft recovery codes left.
30 | You should generate a new set of recovery codes.
31 |
32 | }
33 |
34 | if (Model.IsMachineRemembered)
35 | {
36 |
39 | }
40 | Disable 2FA
41 | Reset recovery codes
42 | }
43 |
44 | Authenticator app
45 | @if (!Model.HasAuthenticator)
46 | {
47 | Add authenticator app
48 | }
49 | else
50 | {
51 | Setup authenticator app
52 | Reset authenticator app
53 | }
54 |
55 | @section Scripts {
56 |
57 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using ApplicationCore.Data.Entities;
6 | using Microsoft.AspNetCore.Identity;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.AspNetCore.Mvc.RazorPages;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace web.Areas.Identity.Pages.Account.Manage
12 | {
13 | public class TwoFactorAuthenticationModel : PageModel
14 | {
15 | private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}";
16 |
17 | private readonly UserManager _userManager;
18 | private readonly SignInManager _signInManager;
19 | private readonly ILogger _logger;
20 |
21 | public TwoFactorAuthenticationModel(
22 | UserManager userManager,
23 | SignInManager signInManager,
24 | ILogger logger)
25 | {
26 | _userManager = userManager;
27 | _signInManager = signInManager;
28 | _logger = logger;
29 | }
30 |
31 | public bool HasAuthenticator { get; set; }
32 |
33 | public int RecoveryCodesLeft { get; set; }
34 |
35 | [BindProperty]
36 | public bool Is2faEnabled { get; set; }
37 |
38 | public bool IsMachineRemembered { get; set; }
39 |
40 | [TempData]
41 | public string StatusMessage { get; set; }
42 |
43 | public async Task OnGet()
44 | {
45 | var user = await _userManager.GetUserAsync(User);
46 | if (user == null)
47 | {
48 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
49 | }
50 |
51 | HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null;
52 | Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
53 | IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user);
54 | RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user);
55 |
56 | return Page();
57 | }
58 |
59 | public async Task OnPost()
60 | {
61 | var user = await _userManager.GetUserAsync(User);
62 | if (user == null)
63 | {
64 | return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
65 | }
66 |
67 | await _signInManager.ForgetTwoFactorClientAsync();
68 | StatusMessage = "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code.";
69 | return RedirectToPage();
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/_Layout.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = "~/Views/Shared/_Layout.cshtml";//"/Areas/Identity/Pages/_Layout.cshtml";
3 | }
4 |
5 |
6 |
7 |
8 |
9 |
10 | @**@
11 |
12 |
13 |
14 |
15 | @**@
16 |
17 |
18 | @**@
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | @**@
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | My Account
50 |
51 |
52 | @*Change my account settings
*@
53 |
54 |
55 |
56 |
57 |
58 |
59 | @RenderBody()
60 |
61 |
62 |
63 |
64 |
65 | @section Scripts {
66 | @RenderSection("Scripts", required: false)
67 | }
68 |
69 |
70 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml:
--------------------------------------------------------------------------------
1 | @inject SignInManager SignInManager
2 | @{
3 | var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any();
4 | }
5 |
7 |
8 | @Model
9 |
10 | }
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using web.Areas.Identity.Pages.Account.Manage
2 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model RegisterConfirmationModel
3 | @{
4 | ViewData["Title"] = "Register confirmation";
5 | }
6 |
7 | Registration Confirmation.
8 | @{
9 | @*if (@Model.DisplayConfirmAccountLink)
10 | {
11 |
12 | This app does not currently have a real email sender registered, see these docs for how to configure a real email sender.
13 | Normally this would be emailed: Click here to confirm your account
14 |
15 | }
16 | else*@
17 | {
18 |
19 | Thank you for registering at MyApp.
20 |
21 |
22 | You will be receiving an email to confirm your account. Please and follow the instructions in the email.
23 |
24 |
25 | Please check the email in your Spam, just in case..
26 |
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using System.Text;
3 | using System.Threading.Tasks;
4 | using ApplicationCore.Data.Entities;
5 | using Microsoft.AspNetCore.Identity.UI.Services;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.AspNetCore.Mvc.RazorPages;
8 | using Microsoft.AspNetCore.WebUtilities;
9 | using Microsoft.AspNetCore.Identity;
10 |
11 | namespace web.Areas.Identity.Pages.Account
12 | {
13 | [AllowAnonymous]
14 | public class RegisterConfirmationModel : PageModel
15 | {
16 | private readonly UserManager _userManager;
17 | private readonly IEmailSender _sender;
18 |
19 | public RegisterConfirmationModel(UserManager userManager, IEmailSender sender)
20 | {
21 | _userManager = userManager;
22 | _sender = sender;
23 | }
24 |
25 | public string Email { get; set; }
26 |
27 | public bool DisplayConfirmAccountLink { get; set; }
28 |
29 | public string EmailConfirmationUrl { get; set; }
30 |
31 | public async Task OnGetAsync(string email)
32 | {
33 | if (email == null)
34 | {
35 | return RedirectToPage("/Index");
36 | }
37 |
38 | var user = await _userManager.FindByEmailAsync(email);
39 | if (user == null)
40 | {
41 | return NotFound($"Unable to load user with email '{email}'.");
42 | }
43 |
44 | Email = email;
45 | // Once you add a real email sender, you should remove this code that lets you confirm the account
46 | // DisplayConfirmAccountLink = true;
47 | if (DisplayConfirmAccountLink)
48 | {
49 | var userId = await _userManager.GetUserIdAsync(user);
50 | var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
51 | code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
52 | EmailConfirmationUrl = Url.Page(
53 | "/Account/ConfirmEmail",
54 | pageHandler: null,
55 | values: new { area = "Identity", userId = userId, code = code },
56 | protocol: Request.Scheme);
57 | }
58 |
59 | return Page();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ResendVerification.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model web.Areas.Identity.Pages.Account.ResendVerificationModel
3 | @{
4 | ViewData["Title"] = "Resend Email Verification";
5 | }
6 |
7 | @ViewData["Title"]
8 | Enter your email.
9 |
10 |
11 |
12 |
21 |
22 |
23 |
24 | @section Scripts {
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ResendVerification.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Text.Encodings.Web;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Microsoft.AspNetCore.Authorization;
8 | using ApplicationCore.Data.Entities;
9 | using Microsoft.AspNetCore.Identity;
10 | using Microsoft.AspNetCore.Identity.UI.Services;
11 | using Microsoft.AspNetCore.Mvc;
12 | using Microsoft.AspNetCore.Mvc.RazorPages;
13 | using Microsoft.AspNetCore.WebUtilities;
14 | using System.Security.Cryptography;
15 | using System.IO;
16 |
17 | namespace web.Areas.Identity.Pages.Account
18 | {
19 | public class ResendVerificationModel : PageModel
20 | {
21 | private readonly UserManager _userManager;
22 | private readonly IEmailSender _emailSender;
23 |
24 | public ResendVerificationModel(UserManager userManager, IEmailSender emailSender)
25 | {
26 | _userManager = userManager;
27 | _emailSender = emailSender;
28 | }
29 |
30 | [BindProperty]
31 | public InputModel Input { get; set; }
32 |
33 | public class InputModel
34 | {
35 | [Required]
36 | [EmailAddress]
37 | public string Email { get; set; }
38 | }
39 |
40 | public async Task OnPostAsync()
41 | {
42 | if (ModelState.IsValid)
43 | {
44 | var user = await _userManager.FindByEmailAsync(Input.Email);
45 | if (user == null)
46 | {
47 | ModelState.AddModelError(string.Empty, "Please Contact Customer Support.");
48 | }
49 |
50 | var userId = await _userManager.GetUserIdAsync(user);
51 | var code = EncryptString("b14ca5898a4e4133bbce2ea2315a1916", user.Email);
52 | code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
53 |
54 | var callbackUrl = Url.Page(
55 | "/Account/ConfirmEmail",
56 | pageHandler: null,
57 | values: new { userId = userId, code = code },
58 | protocol: Request.Scheme);
59 | await _emailSender.SendEmailAsync(
60 | Input.Email,
61 | "CONFIRM",
62 | $"{HtmlEncoder.Default.Encode(callbackUrl)}");
63 |
64 | ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
65 | return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
66 |
67 | }
68 |
69 | return Page();
70 | }
71 | private string EncryptString(string key, string plainText)
72 | {
73 | byte[] iv = new byte[16];
74 | byte[] array;
75 |
76 | using (Aes aes = Aes.Create())
77 | {
78 | aes.Key = Encoding.UTF8.GetBytes(key);
79 | aes.IV = iv;
80 |
81 | ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
82 |
83 | using (MemoryStream memoryStream = new MemoryStream())
84 | {
85 | using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
86 | {
87 | using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
88 | {
89 | streamWriter.Write(plainText);
90 | }
91 |
92 | array = memoryStream.ToArray();
93 | }
94 | }
95 | }
96 |
97 | return Convert.ToBase64String(array);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ResetPassword.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ResetPasswordModel
3 | @{
4 | ViewData["Title"] = "Reset password";
5 | }
6 |
7 | Reset your password.
8 |
9 |
10 |
11 |
31 |
32 |
33 |
34 | @section Scripts {
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Microsoft.AspNetCore.Authorization;
8 | using ApplicationCore.Data.Entities;
9 | using Microsoft.AspNetCore.Identity;
10 | using Microsoft.AspNetCore.Mvc;
11 | using Microsoft.AspNetCore.Mvc.RazorPages;
12 | using Microsoft.AspNetCore.WebUtilities;
13 |
14 | namespace web.Areas.Identity.Pages.Account
15 | {
16 | [AllowAnonymous]
17 | public class ResetPasswordModel : PageModel
18 | {
19 | private readonly UserManager _userManager;
20 |
21 | public ResetPasswordModel(UserManager userManager)
22 | {
23 | _userManager = userManager;
24 | }
25 |
26 | [BindProperty]
27 | public InputModel Input { get; set; }
28 |
29 | public class InputModel
30 | {
31 | [Required]
32 | [EmailAddress]
33 | public string Email { get; set; }
34 |
35 | [Required]
36 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
37 | [DataType(DataType.Password)]
38 | public string Password { get; set; }
39 |
40 | [DataType(DataType.Password)]
41 | [Display(Name = "Confirm password")]
42 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
43 | public string ConfirmPassword { get; set; }
44 |
45 | public string Code { get; set; }
46 | }
47 |
48 | public IActionResult OnGet(string code = null)
49 | {
50 | if (code == null)
51 | {
52 | return BadRequest("A code must be supplied for password reset.");
53 | }
54 | else
55 | {
56 | Input = new InputModel
57 | {
58 | Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code))
59 | };
60 | return Page();
61 | }
62 | }
63 |
64 | public async Task OnPostAsync()
65 | {
66 | if (!ModelState.IsValid)
67 | {
68 | return Page();
69 | }
70 |
71 | var user = await _userManager.FindByEmailAsync(Input.Email);
72 | if (user == null)
73 | {
74 | // Don't reveal that the user does not exist
75 | return RedirectToPage("./ResetPasswordConfirmation");
76 | }
77 |
78 | var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password);
79 | if (result.Succeeded)
80 | {
81 | return RedirectToPage("./ResetPasswordConfirmation");
82 | }
83 |
84 | foreach (var error in result.Errors)
85 | {
86 | ModelState.AddModelError(string.Empty, error.Description);
87 | }
88 | return Page();
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ResetPasswordConfirmationModel
3 | @{
4 | ViewData["Title"] = "Reset password confirmation";
5 | }
6 |
7 | @ViewData["Title"]
8 |
9 | Your password has been reset. Please click here to log in.
10 |
11 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Mvc.RazorPages;
7 |
8 | namespace web.Areas.Identity.Pages.Account
9 | {
10 | [AllowAnonymous]
11 | public class ResetPasswordConfirmationModel : PageModel
12 | {
13 | public void OnGet()
14 | {
15 |
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/_StatusMessage.cshtml:
--------------------------------------------------------------------------------
1 | @model string
2 |
3 | @if (!String.IsNullOrEmpty(Model))
4 | {
5 | var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success";
6 |
7 |
8 | @Model
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using web.Areas.Identity.Pages.Account
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Error.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ErrorModel
3 | @{
4 | ViewData["Title"] = "Error";
5 | }
6 |
7 | Error.
8 | An error occurred while processing your request.
9 |
10 | @if (Model.ShowRequestId)
11 | {
12 |
13 | Request ID: @Model.RequestId
14 |
15 | }
16 |
17 | Development Mode
18 |
19 | Swapping to Development environment will display more detailed information about the error that occurred.
20 |
21 |
22 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application.
23 |
24 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Error.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using Microsoft.AspNetCore.Authorization;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Microsoft.AspNetCore.Mvc.RazorPages;
5 |
6 | namespace web.Areas.Identity.Pages
7 | {
8 | [AllowAnonymous]
9 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
10 | public class ErrorModel : PageModel
11 | {
12 | public string RequestId { get; set; }
13 |
14 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
15 |
16 | public void OnGet()
17 | {
18 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
18 |
19 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Identity
2 | @using web.Areas.Identity
3 | @using web.Areas.Identity.Pages
4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
5 | @using ApplicationCore.Data.Entities
6 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = "/Views/Shared/_Layout.cshtml";
3 | }
4 |
--------------------------------------------------------------------------------
/web/Controllers/AddressStatesController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.AspNetCore.Mvc.Rendering;
7 | using Microsoft.EntityFrameworkCore;
8 | using ApplicationCore.Data;
9 | using ApplicationCore.Data.Entities;
10 | using services;
11 | using services.contracts;
12 |
13 | using Microsoft.AspNetCore.Authorization;
14 | using Microsoft.AspNetCore.Identity;
15 | using Domain.Observers;
16 |
17 | namespace web.Controllers
18 | {
19 | [Authorize]
20 | public class AddressStatesController : Controller
21 | {
22 | private readonly IAddressStateService _addressStateService;
23 | private readonly UserManager _userManager;
24 | private readonly IEmailObserver _emailObserver;
25 |
26 |
27 | public AddressStatesController(UserManager userManager, IAddressStateService addressStateService, IEmailObserver emailObserver)
28 | {
29 | _userManager = userManager;
30 | _addressStateService = addressStateService;
31 | _emailObserver = emailObserver; ;
32 | }
33 |
34 | // GET: AddressStates
35 | public async Task Index()
36 | {
37 | return View(await _addressStateService.GetAllAddresses());
38 | }
39 |
40 | // GET: AddressStates/Details/5
41 | public async Task Details(Guid? id)
42 | {
43 | if (id == null)
44 | {
45 | return NotFound();
46 | }
47 | return View(await _addressStateService.GetAddress(id.Value));
48 | }
49 |
50 | // GET: AddressStates/Create
51 | public IActionResult Create()
52 | {
53 | return View();
54 | }
55 |
56 | // POST: AddressStates/Create
57 | // To protect from overposting attacks, please enable the specific properties you want to bind to, for
58 | // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
59 | [HttpPost]
60 | [ValidateAntiForgeryToken]
61 | public async Task Create([Bind("StateId,Name,StateAbbreviation,CreatedOn,UpdatedOn")] AddressState addressState)
62 | {
63 | if (ModelState.IsValid)
64 | {
65 | await _addressStateService.SetAddress(addressState);
66 | return RedirectToAction(nameof(Index));
67 | }
68 | return View(addressState);
69 | }
70 |
71 | // GET: AddressStates/Edit/5
72 | public async Task Edit(Guid? id)
73 | {
74 | if (id == null)
75 | {
76 | return NotFound();
77 | }
78 |
79 | var addressState = await _addressStateService.GetAddress(id.Value);
80 | if (addressState == null)
81 | {
82 | return NotFound();
83 | }
84 | return View(addressState);
85 | }
86 |
87 | // POST: AddressStates/Edit/5
88 | // To protect from overposting attacks, please enable the specific properties you want to bind to, for
89 | // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
90 | [HttpPost]
91 | [ValidateAntiForgeryToken]
92 | public async Task Edit(Guid id, [Bind("StateId,Name,StateAbbreviation,CreatedOn,UpdatedOn")] AddressState addressState)
93 | {
94 | if (id != addressState.StateId)
95 | {
96 | return NotFound();
97 | }
98 |
99 | if (ModelState.IsValid)
100 | {
101 | try
102 | {
103 |
104 | // Attaching Observers..
105 | _addressStateService.Attach(_emailObserver);
106 |
107 | //Updating Order Status...
108 |
109 | await _addressStateService.UpdateAddress(addressState);
110 | }
111 | catch (DbUpdateConcurrencyException)
112 | {
113 | if (!AddressStateExists(addressState.StateId))
114 | {
115 | return NotFound();
116 | }
117 | else
118 | {
119 | throw;
120 | }
121 | }
122 | return RedirectToAction(nameof(Index));
123 | }
124 | return View(addressState);
125 | }
126 |
127 | // GET: AddressStates/Delete/5
128 | public async Task Delete(Guid? id)
129 | {
130 | if (id == null)
131 | {
132 | return NotFound();
133 | }
134 |
135 | var addressState = await _addressStateService.GetAddress(id.Value);
136 | if (addressState == null)
137 | {
138 | return NotFound();
139 | }
140 |
141 | return View(addressState);
142 | }
143 |
144 | // POST: AddressStates/Delete/5
145 | [HttpPost, ActionName("Delete")]
146 | [ValidateAntiForgeryToken]
147 | public async Task DeleteConfirmed(Guid id)
148 | {
149 | await _addressStateService.DeleteAddressState(id);
150 | return RedirectToAction(nameof(Index));
151 | }
152 |
153 | private bool AddressStateExists(Guid id)
154 | {
155 | return _addressStateService.IsAddressStateExist(id);
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/web/Controllers/ErrorController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace web.Controllers
8 | {
9 | public class ErrorController : Controller
10 | {
11 | [Route("Error/{statusCode}")]
12 | public IActionResult HttpStatusCodeError(int statusCode)
13 | {
14 | switch (statusCode)
15 | {
16 | case 404:
17 | return View("404");
18 | default:
19 | return View("Error");
20 | }
21 | }
22 |
23 | public IActionResult E404()
24 | {
25 | return View();
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/web/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using ApplicationCore.Data.Entities;
7 | using Microsoft.AspNetCore.Diagnostics;
8 | using Microsoft.AspNetCore.Identity;
9 | using Microsoft.AspNetCore.Mvc;
10 | using Microsoft.Extensions.Logging;
11 | using CleanArch.Models;
12 |
13 | namespace CleanArch.Controllers
14 | {
15 | public class HomeController : Controller
16 | {
17 | private readonly ILogger _logger;
18 | private readonly UserManager _userManager;
19 |
20 | public HomeController(UserManager userManager, ILogger logger)
21 | {
22 | _logger = logger;
23 | _userManager = userManager;
24 | }
25 |
26 | public IActionResult Index()
27 | {
28 | return View();
29 | }
30 |
31 | public IActionResult About()
32 | {
33 | return View();
34 | }
35 |
36 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
37 | public IActionResult Error()
38 | {
39 | var exceptionHandlerPathFeature = HttpContext.Features.Get();
40 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
41 | }
42 |
43 |
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/web/Data/ApplicationDbContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace vgo.Data
8 | {
9 | public class ApplicationDbContext : IdentityDbContext
10 | {
11 | public ApplicationDbContext(DbContextOptions options)
12 | : base(options)
13 | {
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/web/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | // This file is used by Code Analysis to maintain SuppressMessage
2 | // attributes that are applied to this project.
3 | // Project-level suppressions either have no target or are given
4 | // a specific target and scoped to a namespace, type, member, etc.
5 |
6 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "", Scope = "member", Target = "~M:web.Controllers.RidesController.RideAmountPaid(System.String,System.String,System.String)~System.Threading.Tasks.Task{Microsoft.AspNetCore.Mvc.IActionResult}")]
7 |
--------------------------------------------------------------------------------
/web/Models/ErrorViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CleanArch.Models
4 | {
5 | public class ErrorViewModel
6 | {
7 | public string RequestId { get; set; }
8 |
9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/web/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Microsoft.AspNetCore.Hosting;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.Hosting;
10 | using Microsoft.Extensions.Logging;
11 | using Serilog;
12 | using Serilog.Events;
13 | using web;
14 | using web.contracts;
15 |
16 | namespace CleanArch
17 | {
18 | public class Program
19 | {
20 |
21 |
22 | public static void Main(string[] args)
23 | {
24 |
25 |
26 | var path = Directory.GetCurrentDirectory();
27 | var environmentName = "Development";// Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
28 |
29 | var configuration = new ConfigurationBuilder()
30 | .SetBasePath(path)
31 | .AddJsonFile($"appsettings.{environmentName}.json", optional: false, reloadOnChange: true)
32 | .Build();
33 |
34 | Log.Logger = new LoggerConfiguration()
35 | .Enrich.FromLogContext()
36 | .MinimumLevel.Debug()
37 | .MinimumLevel.Information()
38 | .ReadFrom.Configuration(configuration)
39 | .CreateLogger();
40 |
41 | try
42 | {
43 | Log.Information("Starting web host");
44 | CreateHostBuilder(args).UseSerilog().Build().Run();
45 | }
46 | catch (Exception ex)
47 | {
48 | Log.Fatal(ex, "Host terminated unexpectedly");
49 | }
50 | finally
51 | {
52 | Log.CloseAndFlush();
53 | }
54 | }
55 |
56 |
57 |
58 |
59 | public static IHostBuilder CreateHostBuilder(string[] args) =>
60 | Host.CreateDefaultBuilder(args)
61 | .ConfigureWebHostDefaults(webBuilder =>
62 | {
63 | webBuilder.UseStartup().UseSerilog();
64 | });
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/web/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:55139",
7 | "sslPort": 44327
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "CleanArch": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/web/StaticFiles/EmailPlaceHolders/emailConfirmation.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/StaticFiles/EmailPlaceHolders/emailConfirmation.txt
--------------------------------------------------------------------------------
/web/StaticFiles/EmailPlaceHolders/passwordReset.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/StaticFiles/EmailPlaceHolders/passwordReset.txt
--------------------------------------------------------------------------------
/web/Views/AddressStates/Create.cshtml:
--------------------------------------------------------------------------------
1 | @model ApplicationCore.Data.Entities.AddressState
2 |
3 | @{
4 | ViewData["Title"] = "Create";
5 | }
6 |
7 | Create
8 |
9 | AddressState
10 |
11 |
12 |
13 |
39 |
40 |
41 |
42 |
43 | Back to List
44 |
45 |
46 |
--------------------------------------------------------------------------------
/web/Views/AddressStates/Delete.cshtml:
--------------------------------------------------------------------------------
1 | @model ApplicationCore.Data.Entities.AddressState
2 |
3 | @{
4 | ViewData["Title"] = "Delete";
5 | }
6 |
7 | Delete
8 |
9 | Are you sure you want to delete this?
10 |
11 | AddressState
12 |
13 |
14 | -
15 | @Html.DisplayNameFor(model => model.Name)
16 |
17 | -
18 | @Html.DisplayFor(model => model.Name)
19 |
20 | -
21 | @Html.DisplayNameFor(model => model.StateAbbreviation)
22 |
23 | -
24 | @Html.DisplayFor(model => model.StateAbbreviation)
25 |
26 | -
27 | @Html.DisplayNameFor(model => model.CreatedOn)
28 |
29 | -
30 | @Html.DisplayFor(model => model.CreatedOn)
31 |
32 | -
33 | @Html.DisplayNameFor(model => model.UpdatedOn)
34 |
35 | -
36 | @Html.DisplayFor(model => model.UpdatedOn)
37 |
38 |
39 |
40 |
45 |
46 |
--------------------------------------------------------------------------------
/web/Views/AddressStates/Details.cshtml:
--------------------------------------------------------------------------------
1 | @model ApplicationCore.Data.Entities.AddressState
2 |
3 | @{
4 | ViewData["Title"] = "Details";
5 | }
6 |
7 | Details
8 |
9 |
10 | AddressState
11 |
12 |
13 | -
14 | @Html.DisplayNameFor(model => model.Name)
15 |
16 | -
17 | @Html.DisplayFor(model => model.Name)
18 |
19 | -
20 | @Html.DisplayNameFor(model => model.StateAbbreviation)
21 |
22 | -
23 | @Html.DisplayFor(model => model.StateAbbreviation)
24 |
25 | -
26 | @Html.DisplayNameFor(model => model.CreatedOn)
27 |
28 | -
29 | @Html.DisplayFor(model => model.CreatedOn)
30 |
31 | -
32 | @Html.DisplayNameFor(model => model.UpdatedOn)
33 |
34 | -
35 | @Html.DisplayFor(model => model.UpdatedOn)
36 |
37 |
38 |
39 |
40 | Edit |
41 | Back to List
42 |
43 |
--------------------------------------------------------------------------------
/web/Views/AddressStates/Edit.cshtml:
--------------------------------------------------------------------------------
1 | @model ApplicationCore.Data.Entities.AddressState
2 |
3 | @{
4 | ViewData["Title"] = "Edit";
5 | }
6 |
7 | Edit
8 |
9 | AddressState
10 |
11 |
12 |
13 |
40 |
41 |
42 |
43 |
44 | Back to List
45 |
46 |
47 |
--------------------------------------------------------------------------------
/web/Views/AddressStates/Index.cshtml:
--------------------------------------------------------------------------------
1 | @model IEnumerable
2 |
3 | @{
4 | ViewData["Title"] = "Index";
5 | }
6 |
7 | Index
8 |
9 |
10 | Create New
11 |
12 |
13 |
14 |
15 |
16 | Name
17 |
18 |
19 | State Abbreviation
20 |
21 |
22 | Created On
23 |
24 |
25 | Updated On
26 |
27 |
28 |
29 |
30 |
31 | @foreach (var item in Model)
32 | {
33 |
34 |
35 | @Html.DisplayFor(modelItem => item.AddressState.Name)
36 |
37 |
38 | @Html.DisplayFor(modelItem => item.AddressState.StateAbbreviation)
39 |
40 |
41 | @Html.DisplayFor(modelItem => item.AddressState.CreatedOn)
42 |
43 |
44 | @Html.DisplayFor(modelItem => item.AddressState.UpdatedOn)
45 |
46 |
47 | Edit |
48 | Details |
49 | Delete
50 |
51 |
52 | }
53 |
54 |
55 |
--------------------------------------------------------------------------------
/web/Views/Error/E404.cshtml:
--------------------------------------------------------------------------------
1 | @model ErrorViewModel
2 | @{
3 | ViewData["Title"] = "Error";
4 | }
5 |
6 |
7 |
13 |
14 |
15 | Uh Oh ! That's an error!
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/web/Views/Home/About.cshtml:
--------------------------------------------------------------------------------
1 |
2 | @{
3 | ViewData["Title"] = "About";
4 | Layout = "~/Views/Shared/_Layout.cshtml";
5 | }
6 |
7 |
12 |
8 | The description is pretty much on the home page itself.
9 |
--------------------------------------------------------------------------------
/web/Views/Home/Error.cshtml:
--------------------------------------------------------------------------------
1 | @model ErrorViewModel
2 | @{
3 | ViewData["Title"] = "Error";
4 | }
5 |
6 |
7 |
14 |
15 |
16 |
17 | Uh Oh ! That's an error !!
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/web/Views/Home/Home.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Home Page";
3 | }
4 |
--------------------------------------------------------------------------------
/web/Views/Home/Index.cshtml:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Identity
2 | @using ApplicationCore.Data.Entities
3 |
4 | @inject SignInManager
13 | SignInManager
5 | @inject UserManager UserManager
6 |
7 |
8 | @{
9 | ViewData["Title"] = "Clean Architecture";
10 | }
11 |
12 |
13 |
14 | Hello, Clean Architecture!
15 | Read about Clean Arhitecture here .
16 | Essentially, it is a multi-layered approach to design an application,
17 | where each layer is
Framework Independent, Testable, Independent of UI/Database/External Services.
18 | This is a .Net Core 3.1 MVC based Web Application, with in-built Bootstrap 4, Microsoft Identity, Entity Framework Core.
19 |
20 | This also supports PWA , so the web application can be converted to Mobile Apps easily.
21 |
22 | You can download the repo on GitHub:
23 | https://github.com/naveenalavilli/CleanArchitecture
24 |
25 |
26 |
27 |
28 |
29 |
30 | This project has these features built-in:
31 |
32 |
33 | - User Authentication, using Identity, and OAuth providers (Google,Facebook,LinkedIn)
34 | - SendGrid support, to send emails.
35 | - CRUD for States. This can be used as example to work on other Entities in any project.
36 | - PWA Support. The users can install this app on thier Mobiles , without getting from PlayStore/AppStore.
37 | - SeriLog Support , to log events/Exceptions in the database.
38 | - Uses JQuery, BootStrap 4 and Font Awesome
39 |
40 |
41 |
42 |
43 |
44 |
45 | How to setup:
46 |
47 |
48 | - Clone the project from GitHub
49 | - Configure Database in AppSettings.Json
50 | - Run update-database in Pakage Manager Console , with Default Project set to Persistence
51 | - Configure OAuth settings in AppSettings.json, if desired.
52 |
53 |
54 |
55 |
56 |
57 | Things to note:
58 |
59 |
60 | - The users are all activated, without confirming their Emails. Configure SendGrid to fix it.
61 | -
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | Thanks to :
70 |
71 |
72 | - Microsoft Docs
73 | - Tons of blogs, tutorials on Youtube.
74 | - Stack Overflow!
75 |
76 |
77 |
78 | @section Scripts {
79 |
80 |
85 | }
86 |
--------------------------------------------------------------------------------
/web/Views/Shared/ErrorWithRedirect.cshtml:
--------------------------------------------------------------------------------
1 | @model ErrorViewModel
2 | @{
3 | ViewData["Title"] = "Error";
4 | }
5 |
6 |
7 |
8 |
9 |
10 |
11 | Oops !
12 |
13 | Click here .
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/web/Views/Shared/_CookieConsentPartial.cshtml:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Http.Features
2 |
3 | @{
4 | var consentFeature = Context.Features.Get();
5 | var showBanner = !consentFeature?.CanTrack ?? false;
6 | var cookieString = consentFeature?.CreateConsentCookie();
7 | }
8 |
9 | @if (showBanner)
10 | {
11 | SignInManager
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Clean Architecture - Boilerplate App.
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | @RenderBody()
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | @RenderSection("Scripts", required: false)
83 |
84 |
85 |
86 |
87 |
88 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/web/Views/Shared/_LoginPartial.cshtml:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Identity
2 | @using ApplicationCore.Data.Entities
3 | @inject SignInManager SignInManager
4 | @inject UserManager UserManager
5 |
6 | ;Initial Catalog=CleanArch;Persist Security Info=True;User ID=;Password=;"
5 | },
6 | "Logging": {
7 | "LogLevel": {
8 | "Default": "Information",
9 | "Microsoft": "Warning",
10 | "Microsoft.Hosting.Lifetime": "Information"
11 | }
12 | },
13 | "AllowedHosts": "*",
14 | "GoogleAuth": {
15 | //Integrating Google Sign-In into your web app
16 | //https://developers.google.com/identity/sign-in/web/sign-in
17 | "ClientId": "",
18 | "ClientSecret": ""
19 | },
20 | "FacebookAuth": {
21 | //https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins?view=aspnetcore-3.1
22 | "AppId": "",
23 | "ClientSecret": ""
24 | },
25 | "TwitterAuth": {
26 | //https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/twitter-logins?view=aspnetcore-3.1
27 | "AppId": "",
28 | "AppSecret": ""
29 | },
30 | "LinkedInAuth": {
31 | //https://www.linkedin.com/developer/apps
32 | //https://docs.microsoft.com/en-us/linkedin/shared/authentication/authentication?context=linkedin/consumer/context
33 | "ClientId": "",
34 | "AppSecret": ""
35 | },
36 | // SendGrid is used here to send out emails.
37 | // https://app.sendgrid.com/
38 | //https://sendgrid.com/docs/API_Reference/api_v3.html
39 | "SendGrid": {
40 | "SendGridUser": "gdfgdgdfgg",
41 | "SendGridKey": "dfgdfgdfsgg"
42 | },
43 | "ApplicationName": "Clean Architecture With .Net Core 3.1",
44 |
45 | "Serilog": {
46 | "Using": [ "Serilog.Sinks.MSSqlServer" ],
47 | "MinimumLevel": "Warning",
48 | "WriteTo": [
49 | {
50 | "Name": "MSSqlServer",
51 | "Args": {
52 | "connectionString": "\"Data Source=;Initial Catalog=CleanArch;Persist Security Info=True;User ID=;Password=;",
53 | "tableName": "EventLogs",
54 | "autoCreateSqlTable": true
55 | }
56 | }
57 | ]
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/web/contracts/IAppThreads.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace web.contracts
7 | {
8 | public interface IAppThreads
9 | {
10 | void DoWork();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/web/libman.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0",
3 | "defaultProvider": "unpkg",
4 | "libraries": [
5 | {
6 | "library": "@microsoft/signalr@latest",
7 | "destination": "wwwroot/js/signalr/",
8 | "files": [
9 | "dist/browser/signalr.js",
10 | "dist/browser/signalr.min.js"
11 | ]
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/web/web.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 | aspnet-vgo-DAD0257E-A74D-44FB-8472-A2ABB5115CE4
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | true
34 | PreserveNewest
35 |
36 |
37 | Always
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | all
53 | runtime; build; native; contentfiles; analyzers; buildtransitive
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/web/wwwroot/.well-known/assetlinks.json:
--------------------------------------------------------------------------------
1 | //Insert SHA when Creating App.
--------------------------------------------------------------------------------
/web/wwwroot/css/site.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --input-padding-x: 1.5rem;
3 | --input-padding-y: .75rem;
4 | }
5 |
6 | body {
7 | background: #F8F8F8;
8 | }
9 |
10 |
11 | html,
12 | body {
13 | height: 100%;
14 | }
15 |
16 | #page-content {
17 | flex: 1 0 auto;
18 | }
19 |
20 |
21 | #sticky-footer {
22 | flex-shrink: none;
23 | }
--------------------------------------------------------------------------------
/web/wwwroot/css/site.min.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --input-padding-x: 1.5rem;
3 | --input-padding-y: .75rem;
4 | }
5 |
6 | body {
7 | background: #F8F8F8;
8 | }
9 |
10 |
11 | html,
12 | body {
13 | height: 100%;
14 | }
15 |
16 | #page-content {
17 | flex: 1 0 auto;
18 | }
19 |
20 |
21 | #sticky-footer {
22 | flex-shrink: none;
23 | }
--------------------------------------------------------------------------------
/web/wwwroot/images/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/android-chrome-192x192.png
--------------------------------------------------------------------------------
/web/wwwroot/images/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/android-chrome-512x512.png
--------------------------------------------------------------------------------
/web/wwwroot/images/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/apple-touch-icon.png
--------------------------------------------------------------------------------
/web/wwwroot/images/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/favicon-16x16.png
--------------------------------------------------------------------------------
/web/wwwroot/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/favicon-32x32.png
--------------------------------------------------------------------------------
/web/wwwroot/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/favicon.ico
--------------------------------------------------------------------------------
/web/wwwroot/images/icons8-facebook-480.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/icons8-facebook-480.png
--------------------------------------------------------------------------------
/web/wwwroot/images/icons8-google-480.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/icons8-google-480.png
--------------------------------------------------------------------------------
/web/wwwroot/images/icons8-linkedin-480.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/icons8-linkedin-480.png
--------------------------------------------------------------------------------
/web/wwwroot/images/icons8-twitter-480.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naveenalavilli/CleanArchitecture/0375cd2f02e69b01da346bbdefaa17fed2783da0/web/wwwroot/images/icons8-twitter-480.png
--------------------------------------------------------------------------------
/web/wwwroot/images/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
--------------------------------------------------------------------------------
/web/wwwroot/js/site.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/wwwroot/js/site.min.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/wwwroot/lib/bootstrap/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2018 Twitter, Inc.
4 | Copyright (c) 2011-2018 The Bootstrap Authors
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/web/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
3 | * Copyright 2011-2019 The Bootstrap Authors
4 | * Copyright 2011-2019 Twitter, Inc.
5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */
--------------------------------------------------------------------------------
/web/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) .NET Foundation. All rights reserved.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use
4 | these files except in compliance with the License. You may obtain a copy of the
5 | License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software distributed
10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the
12 | specific language governing permissions and limitations under the License.
13 |
--------------------------------------------------------------------------------
/web/wwwroot/lib/jquery-validation/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright Jörn Zaefferer
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/web/wwwroot/lib/jquery/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright JS Foundation and other contributors, https://js.foundation/
2 |
3 | This software consists of voluntary contributions made by many
4 | individuals. For exact contribution history, see the revision history
5 | available at https://github.com/jquery/jquery
6 |
7 | The following license applies to all parts of this software except as
8 | documented below:
9 |
10 | ====
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining
13 | a copy of this software and associated documentation files (the
14 | "Software"), to deal in the Software without restriction, including
15 | without limitation the rights to use, copy, modify, merge, publish,
16 | distribute, sublicense, and/or sell copies of the Software, and to
17 | permit persons to whom the Software is furnished to do so, subject to
18 | the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be
21 | included in all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 |
31 | ====
32 |
33 | All files located in the node_modules and external directories are
34 | externally maintained libraries used by this software which have their
35 | own licenses; we recommend you read them, as their terms may differ from
36 | the terms above.
37 |
--------------------------------------------------------------------------------
/web/wwwroot/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "MyApp",
3 | "short_name": "MyApp",
4 | "background_color": "#398E3C",
5 | "theme_color": "#FFFFFF",
6 | "description": "Your App Description",
7 | "icons": [
8 | {
9 | "src": "/images/android-chrome-192x192.png",
10 | "sizes": "192x192"
11 | },
12 | {
13 | "src": "/images/android-chrome-512x512.png",
14 | "sizes": "512x512"
15 | }
16 | ],
17 | "display": "standalone",
18 | "scope": "/",
19 | "start_url": "/"
20 | }
--------------------------------------------------------------------------------
/web/wwwroot/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 | https://github.com/naveenalavilli/CleanArchitecture
12 | 2020-08-07T03:17:04+00:00
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
46 |
--------------------------------------------------------------------------------
/web/Views/Shared/_ValidationScriptsPartial.cshtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/web/Views/Shared/_footer.cshtml:
--------------------------------------------------------------------------------
1 |
33 |
--------------------------------------------------------------------------------
/web/Views/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using CleanArch
2 | @using CleanArch.Models
3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
4 |
--------------------------------------------------------------------------------
/web/Views/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = "_Layout";
3 | }
4 |
--------------------------------------------------------------------------------
/web/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/web/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | // Set DB connection
4 | "DefaultConnection": "Data Source=
17 |
25 | }
--------------------------------------------------------------------------------
/web/Views/Shared/_Layout.cshtml:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Identity
2 | @using ApplicationCore.Data.Entities
3 | @inject Microsoft.AspNetCore.Hosting.IHostingEnvironment hostEnv
4 | @inject SignInManager
21 |
--------------------------------------------------------------------------------
/web/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml:
--------------------------------------------------------------------------------
1 | @model string
2 |
3 | @if (!String.IsNullOrEmpty(Model))
4 | {
5 | var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success";
6 |