Skip to content

Instantly share code, notes, and snippets.

@Abubakr0904
Created March 27, 2022 18:24
Show Gist options
  • Select an option

  • Save Abubakr0904/fdf6215fda6a798b5a1f8280fb7ee2f9 to your computer and use it in GitHub Desktop.

Select an option

Save Abubakr0904/fdf6215fda6a798b5a1f8280fb7ee2f9 to your computer and use it in GitHub Desktop.
namespace Ilmhub.Auth.Controllers
{
[Authorize]
[SecurityHeaders(
FontSources = SecurityHeadersFontSources.FONTAWESOME + SecurityHeadersFontSources.GLUWASTATIC,
ScriptSources = SecurityHeadersScriptSources.JQUERY + SecurityHeadersScriptSources.BOOTSTRAP + SecurityHeadersScriptSources.INTERNAL,
StyleSources = SecurityHeadersStyleSources.BOOTSTRAP + SecurityHeadersStyleSources.FONTAWESOME + SecurityHeadersStyleSources.GOOGLEFONTS)]
public class AccountController : Controller
{
private readonly MessagingClient mMessagingClient;
private readonly UserManager mUserManager;
private readonly SignInManager mSignInManager;
private readonly MessageStringProvider mMessageStringProvider;
private readonly ILogger mLogger;
private readonly IIdentityServerInteractionService mInteractionService;
private readonly IStringLocalizer<AccountController> mLocalizer;
private readonly ClientStore mClientStore;
private readonly Options mOptions;
public AccountController(
MessagingClient messagingClient,
UserManager userManager,
SignInManager signInManager,
MessageStringProvider messageStringProvider,
ILogger<AccountController> logger,
IIdentityServerInteractionService interactionService,
IStringLocalizer<AccountController> localizer,
ClientStore clientStore,
IOptionsSnapshot<Options> optionsSnapshot)
{
mMessagingClient = messagingClient;
mUserManager = userManager;
mSignInManager = signInManager;
mMessageStringProvider = messageStringProvider;
mLogger = logger;
mInteractionService = interactionService;
mLocalizer = localizer;
mClientStore = clientStore;
mOptions = optionsSnapshot.Value;
}
//
// GET: /Account/Signup
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Signup()
{
if (mSignInManager.IsSignedIn(User))
{
await mSignInManager.SignOutAsync();
}
return View();
}
//
// POST: /Account/Signup
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Signup(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = new User
{
UserName = model.Username,
Email = model.Email
};
var createUserResult = await mUserManager.CreateAsync(user, model.Password);
if (!createUserResult.Succeeded)
{
foreach (var createUserError in createUserResult.Errors)
{
switch (createUserError.Code)
{
case "DuplicateUserName":
ModelState.AddModelError(nameof(model.Username), mLocalizer["Duplicate Username"]);
break;
case "InvalidUserName":
ModelState.AddModelError(nameof(model.Username), mLocalizer["Invalid Username"]);
break;
case "RestrictedUsername":
ModelState.AddModelError(nameof(model.Username), mLocalizer["Restricted Username"]);
break;
case "DuplicateEmail":
ModelState.AddModelError(nameof(model.Email), mLocalizer["This email is already taken"]);
break;
case "InvalidEmail":
ModelState.AddModelError(nameof(model.Email), mLocalizer["Invalid Email"]);
break;
case "PasswordTooShort":
ModelState.AddModelError(nameof(model.Password), mLocalizer["Password too short"]);
break;
case "PasswordRequiresNonAlphanumeric":
ModelState.AddModelError(nameof(model.Password), mLocalizer["Password requires non-alphanumeric characters"]);
break;
case "PasswordRequiresDigit":
ModelState.AddModelError(nameof(model.Password), mLocalizer["Password requires digit"]);
break;
case "PasswordRequiresLower":
ModelState.AddModelError(nameof(model.Password), mLocalizer["Password requires lower case characters"]);
break;
case "PasswordRequiresUpper":
ModelState.AddModelError(nameof(model.Password), mLocalizer["Password requires upper case characters"]);
break;
default:
ModelState.AddModelError(string.Empty, mLocalizer["Something went wrong"]);
break;
}
}
return View(model);
}
string code = await mUserManager.GenerateEmailConfirmationTokenAsync(user);
string link = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code }, protocol: "https");
string emailSubject = mMessageStringProvider.GetSignupConfirmEmailSubject();
string emailBody = mMessageStringProvider.GetEmailVerificationMessage(user.UserName, link);
var emailRequest = new SendEmailRequest(
userAccountID: Guid.Parse(user.Id),
messageType: EMessageType.AccountVerificationRequest,
messageSender: EMessageSender.Auth,
email: model.Email,
subject: emailSubject,
message: emailBody);
#pragma warning disable 4014
mMessagingClient.SendEmailAsync(emailRequest);
#pragma warning restore 4014
return RedirectToAction("ConfirmEmailInstruction");
}
//
// GET: /Account/ConfirmEmailInstrucion
[HttpGet]
[AllowAnonymous]
public IActionResult ConfirmEmailInstruction()
{
ViewData["GluwaProDashboardBaseUrl"] = mOptions.GluwaProDashboardBaseUrl;
return View();
}
//
// GET: /Account/Login
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null)
{
if (mSignInManager.IsSignedIn(User))
{
await mSignInManager.SignOutAsync();
var logoutID = await mInteractionService.CreateLogoutContextAsync();
var signoutContext = await mInteractionService.GetLogoutContextAsync(logoutID);
var signinContext = await mInteractionService.GetAuthorizationContextAsync(returnUrl);
var client = await mClientStore.FindClientByIdAsync(signinContext?.ClientId);
LoggedoutViewModel loggedOutViewModel = new LoggedoutViewModel(
automaticRedirectAfterSignOut: true,
clientName: client?.ClientName,
signOutIframeUrl: signoutContext?.SignOutIFrameUrl,
postLogoutRedirectUri: signinContext?.RedirectUri);
return View("LoggedOut", loggedOutViewModel);
}
ViewData["ReturnUrl"] = returnUrl;
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (!ModelState.IsValid)
{
return View(model);
}
var emailValidator = new EmailAddressAttribute();
var isValidEmail = emailValidator.IsValid(model.Email);
User user;
if (isValidEmail)
{
user = await mUserManager.FindByEmailAsync(model.Email);
}
else
{
user = await mUserManager.FindByNameAsync(model.Email);
}
if (user != null)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await mSignInManager.PasswordSignInAsync(user, model.Password, isPersistent: false, lockoutOnFailure: true);
if (result.Succeeded)
{
mLogger.LogInformation(1, "User logged in.");
string redirectUrl = string.IsNullOrWhiteSpace(returnUrl) ? mOptions.GluwaProDashboardBaseUrl : returnUrl;
return Redirect(redirectUrl);
}
if (result.IsLockedOut)
{
mLogger.LogWarning(2, "User account locked out.");
ModelState.AddModelError(string.Empty, mLocalizer["You have tried too many times. Try again after {0} minutes.", 30]);
return View(model);
}
if (user.EmailConfirmed)
{
ModelState.AddModelError(string.Empty, mLocalizer["Credentials you entered don't match our records."]);
return View(model);
}
}
ModelState.AddModelError(string.Empty, mLocalizer["Credentials you entered don't match our records or your email requires confirmation."]);
return View(model);
}
[HttpGet]
public async Task<IActionResult> Logout(string logoutID)
{
var context = await mInteractionService.GetLogoutContextAsync(logoutID);
var model = new LogoutViewModel
{
LogoutID = logoutID
};
if (!context.ShowSignoutPrompt)
{
return await Logout(model);
}
ViewData["GluwaProDashboardBaseUrl"] = mOptions.GluwaProDashboardBaseUrl;
return View(model);
}
//
// POST: /Account/Logout
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutViewModel model)
{
var logout = await mInteractionService.GetLogoutContextAsync(model.LogoutID);
var loggedOutModel = new LoggedoutViewModel
(
automaticRedirectAfterSignOut: !string.IsNullOrWhiteSpace(logout?.PostLogoutRedirectUri),
clientName: logout?.ClientName,
signOutIframeUrl: logout?.SignOutIFrameUrl,
postLogoutRedirectUri: logout?.PostLogoutRedirectUri
);
await mSignInManager.SignOutAsync();
mLogger.LogInformation(4, "User logged out.");
return View("LoggedOut", loggedOutModel);
}
//
// GET: /Account/ConfirmEmail
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ConfirmEmail(string userId, string code, int? rp)
{
if (userId == null || code == null)
{
return View("Error");
}
var user = await mUserManager.FindByIdIncludeAllStatusAsync(new Guid(userId));
if (user == null)
{
mLogger.LogWarning($"User with {userId} not found.");
return View("Error");
}
if (user.EmailConfirmed)
{
ViewData["GluwaProDashboardBaseUrl"] = mOptions.GluwaProDashboardBaseUrl;
return View("EmailAlreadyConfirmed");
}
var result = await mUserManager.ConfirmEmailAsync(user, code);
if (!result.Succeeded)
{
mLogger.LogWarning($"Error while confirming email {user.Email}: {result.Errors.ToJson()}");
return View("Error");
}
// Send reset password email if this email confirmation has been prompted by a password reset request
var bDisplayResetPasswordMessage = false;
if (rp.HasValue && rp == 1)
{
var resetPasswordCode = await mUserManager.GeneratePasswordResetTokenAsync(user);
var resetPasswordCallbackUrl = Url.Action(nameof(ResetPassword), "Account", new { code = resetPasswordCode }, protocol: "https");
var emailRequest = new SendEmailRequest(
userAccountID: Guid.Parse(user.Id),
messageType: EMessageType.ResetPasswordRequest,
messageSender: EMessageSender.Auth,
email: user.Email,
subject: mMessageStringProvider.GetResetPasswordEmailSubject(),
message: mMessageStringProvider.GetResetPasswordEmailMessage(user.UserName, resetPasswordCallbackUrl));
var sendEmailResult = await mMessagingClient.SendEmailAsync(emailRequest);
if (sendEmailResult.IsSuccess)
{
bDisplayResetPasswordMessage = true;
}
}
ViewData["GluwaProDashboardBaseUrl"] = mOptions.GluwaProDashboardBaseUrl;
return View("ConfirmEmail", bDisplayResetPasswordMessage);
}
//
// GET: /Account/ForgotPassword
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPassword()
{
ViewData["Title"] = mLocalizer["Forgot your password?"];
ViewData["GluwaProDashboardBaseUrl"] = $"{mOptions.GluwaProDashboardBaseUrl}/ApiKey";
return View("ForgotPassword");
}
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
ViewData["GluwaProDashboardBaseUrl"] = $"{mOptions.GluwaProDashboardBaseUrl}/ApiKey";
if (!ModelState.IsValid)
{
return View(model);
}
var user = await mUserManager.FindByEmailAsync(model.Email);
if (user == null || !(await mUserManager.IsEmailConfirmedAsync(user)))
{
ModelState.AddModelError(nameof(model.Email), mLocalizer["This email does not exist in our system."]);
return View(model);
}
var code = await mUserManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action(nameof(ResetPassword), "Account", new { code }, protocol: HttpContext.Request.Scheme);
var emailRequest = new SendEmailRequest(
userAccountID: Guid.Parse(user.Id),
messageType: EMessageType.ResetPasswordRequest,
messageSender: EMessageSender.Auth,
email: model.Email,
subject: mMessageStringProvider.GetResetPasswordEmailSubject(),
message: mMessageStringProvider.GetResetPasswordEmailMessage(user.UserName, callbackUrl));
var result = await mMessagingClient.SendEmailAsync(emailRequest);
if (!result.IsSuccess)
{
switch (result.Error.Code)
{
case EEmailErrorCode.TooManyRequests:
ModelState.AddModelError(string.Empty, mLocalizer["You have tried too many times. Try again after {0} minutes.", 60]);
return View(model);
default:
ModelState.AddModelError(string.Empty, mLocalizer["Something went wrong"]);
return View(model);
}
}
return View("ForgotPasswordConfirmation", model);
}
//
// GET: /Account/ResetPassword
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPassword(string code = null)
{
return code == null ? View("Error") : View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model, [FromServices] PersistedGrantDbContext dbContext)
{
if (!ModelState.IsValid)
{
return View(model);
}
User user;
var emailValidator = new EmailAddressAttribute();
if (emailValidator.IsValid(model.UsernameOrEmail))
{
user = await mUserManager.FindByEmailAsync(model.UsernameOrEmail);
}
else
{
user = await mUserManager.FindByNameAsync(model.UsernameOrEmail);
}
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
}
var result = await mUserManager.ResetPasswordAsync(user, model.Code, model.Password);
if (result.Succeeded)
{
await removePersistantGrantsInternalAsync(dbContext, user.Id.ToLowerInvariant());
if (string.IsNullOrWhiteSpace(user.Email))
{
var smsRequest = new SendSmsRequest(
userAccountID: Guid.Parse(user.Id),
messageType: EMessageType.ResetPasswordNotification,
messageSender: EMessageSender.Auth,
phoneNumber: user.PhoneNumber,
message: mMessageStringProvider.GetPasswordChangedNotificationSms());
await mMessagingClient.SendSmsAsync(smsRequest);
}
else
{
var emailRequest = new SendEmailRequest(
userAccountID: Guid.Parse(user.Id),
messageType: EMessageType.ResetPasswordNotification,
messageSender: EMessageSender.Auth,
email: user.Email,
subject: mMessageStringProvider.GetPasswordChangedEmailSubject(),
message: mMessageStringProvider.GetPasswordChangedNotificationEmailMessage(user.UserName));
await mMessagingClient.SendEmailAsync(emailRequest);
}
return RedirectToAction(nameof(AccountController.ResetPasswordConfirmation), "Account");
}
addErrors(result);
return View();
}
//
// GET: /Account/ResetPasswordConfirmation
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPasswordConfirmation()
{
ViewData["GluwaProDashboardBaseUrl"] = mOptions.GluwaProDashboardBaseUrl;
return View();
}
//
// GET: /Account/ChangeEmailConfirmation
[HttpGet]
[AllowAnonymous]
public IActionResult ChangeEmailConfirmation()
{
ViewData["GluwaProDashboardBaseUrl"] = mOptions.GluwaProDashboardBaseUrl;
return View();
}
//
// GET /Account/AccessDenied
[HttpGet]
[AllowAnonymous]
public IActionResult AccessDenied()
{
return View();
}
#region Helpers
private async Task removePersistantGrantsInternalAsync(PersistedGrantDbContext dbContext, string normalizedUserID)
{
var grants = dbContext.PersistedGrants.Where(g => g.SubjectId == normalizedUserID && g.Type == GrantTypes.RefreshToken);
dbContext.PersistedGrants.RemoveRange(grants);
await dbContext.SaveChangesAsync();
}
private void addErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment