diff --git a/SecurityService.UserInterface/Pages/Account/Login/Index.cshtml.cs b/SecurityService.UserInterface/Pages/Account/Login/Index.cshtml.cs index 64f7d757..b240a8de 100644 --- a/SecurityService.UserInterface/Pages/Account/Login/Index.cshtml.cs +++ b/SecurityService.UserInterface/Pages/Account/Login/Index.cshtml.cs @@ -11,6 +11,7 @@ namespace IdentityServerHost.Pages.Login; +using Polly; using System; using System.Linq; using System.Threading.Tasks; @@ -63,7 +64,7 @@ public async Task OnGet(string returnUrl) public async Task OnPost() { // check if we are in the context of an authorization request - var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl); + AuthorizationRequest? context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl); // the user clicked the "cancel" button if (Input.Button == "forgotpassword") { @@ -71,78 +72,78 @@ public async Task OnPost() } // the user clicked the "cancel" button - if (Input.Button != "login") + if (Input.Button != "login") { + return await HandleCancelButton(context); + } + + if (ModelState.IsValid) { + return await HandleLoginButton(context); + } + + // something went wrong, show form with error + await BuildModelAsync(Input.ReturnUrl); + return Page(); + } + + private async Task HandleCancelButton(AuthorizationRequest? context) { + if (context != null) { - if (context != null) + // if the user cancels, send a result back into IdentityServer as if they + // denied the consent (even if this client does not require consent). + // this will send back an access denied OIDC error response to the client. + await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); + + // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null + if (context.IsNativeClient()) { - // if the user cancels, send a result back into IdentityServer as if they - // denied the consent (even if this client does not require consent). - // this will send back an access denied OIDC error response to the client. - await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); + // The client is native, so this change in how to + // return the response is for better UX for the end user. + return this.LoadingPage(Input.ReturnUrl); + } - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - if (context.IsNativeClient()) - { + return Redirect(Input.ReturnUrl); + } + + // since we don't have a valid context, then we just go back to the home page + return Redirect("~/"); + } + + private async Task HandleLoginButton(AuthorizationRequest? context) { + var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true); + if (result.Succeeded) { + var user = await _userManager.FindByNameAsync(Input.Username); + await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName, clientId: context?.Client.ClientId)); + + if (context != null) { + if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage(Input.ReturnUrl); } + // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return Redirect(Input.ReturnUrl); } - else - { - // since we don't have a valid context, then we just go back to the home page - return Redirect("~/"); - } - } - if (ModelState.IsValid) - { - var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, Input.RememberLogin, lockoutOnFailure: true); - if (result.Succeeded) - { - var user = await _userManager.FindByNameAsync(Input.Username); - await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName, clientId: context?.Client.ClientId)); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage(Input.ReturnUrl); - } - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - return Redirect(Input.ReturnUrl); - } + // request for a local page + if (Url.IsLocalUrl(Input.ReturnUrl)) { + return Redirect(Input.ReturnUrl); + } - // request for a local page - if (Url.IsLocalUrl(Input.ReturnUrl)) - { - return Redirect(Input.ReturnUrl); - } - else if (string.IsNullOrEmpty(Input.ReturnUrl)) - { - return Redirect("~/"); - } - else - { - // user might have clicked on a malicious link - should be logged - throw new ArgumentException("invalid return URL"); - } + if (string.IsNullOrEmpty(Input.ReturnUrl)) { + return Redirect("~/"); } - await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId:context?.Client.ClientId)); - ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage); + // user might have clicked on a malicious link - should be logged + throw new ArgumentException("invalid return URL"); } - // something went wrong, show form with error - await BuildModelAsync(Input.ReturnUrl); - return Page(); + await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId)); + ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage); + + return this.Page(); } - + private async Task BuildModelAsync(string returnUrl) { Input = new InputModel