Guest User

Untitled

a guest
Nov 3rd, 2023
141
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.28 KB | None | 0 0
  1. using Duende.IdentityServer.Events;
  2. using Duende.IdentityServer.Models;
  3. using Duende.IdentityServer.Services;
  4. using Duende.IdentityServer.Stores;
  5. using HeadhuntNow.Auth.Models;
  6. using Microsoft.AspNetCore.Authentication;
  7. using Microsoft.AspNetCore.Authorization;
  8. using Microsoft.AspNetCore.Identity;
  9. using Microsoft.AspNetCore.Mvc;
  10. using Microsoft.AspNetCore.Mvc.RazorPages;
  11.  
  12. namespace HeadhuntNow.Auth.Pages.Login;
  13.  
  14. [SecurityHeaders]
  15. [AllowAnonymous]
  16. public class Index : PageModel
  17. {
  18.     private readonly UserManager<ApplicationUser> _userManager;
  19.     private readonly SignInManager<ApplicationUser> _signInManager;
  20.     private readonly IIdentityServerInteractionService _interaction;
  21.     private readonly IEventService _events;
  22.     private readonly IAuthenticationSchemeProvider _schemeProvider;
  23.     private readonly IIdentityProviderStore _identityProviderStore;
  24.  
  25.     public ViewModel View { get; set; }
  26.  
  27.     [BindProperty] public InputModel Input { get; set; }
  28.  
  29.     public Index (
  30.         IIdentityServerInteractionService interaction,
  31.         IAuthenticationSchemeProvider schemeProvider,
  32.         IIdentityProviderStore identityProviderStore,
  33.         IEventService events,
  34.         UserManager<ApplicationUser> userManager,
  35.         SignInManager<ApplicationUser> signInManager)
  36.     {
  37.         _userManager = userManager;
  38.         _signInManager = signInManager;
  39.         _interaction = interaction;
  40.         _schemeProvider = schemeProvider;
  41.         _identityProviderStore = identityProviderStore;
  42.         _events = events;
  43.  
  44.     }
  45.     public async Task<IActionResult> OnGet (string returnUrl)
  46.     {
  47.         await BuildModelAsync(returnUrl);
  48.  
  49.         if (View.IsExternalLoginOnly)
  50.         {
  51.             // we only have one option for logging in and it's an external provider
  52.             return RedirectToPage("/ExternalLogin/Challenge", new { scheme = View.ExternalLoginScheme, returnUrl });
  53.         }
  54.         return Page();
  55.     }
  56.  
  57.     public async Task<IActionResult> OnPost()
  58.     {
  59.         var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);
  60.         if (context == null) return Redirect("~/");
  61.        
  62.         if (Input.Button != "login")
  63.         {
  64.             return await HandleUserCancellation(context);
  65.         }
  66.        
  67.         if (ModelState.IsValid)
  68.         {
  69.             return await PerformLogin(context);
  70.         }
  71.  
  72.         // something went wrong, show form with error
  73.         await BuildModelAsync(Input.ReturnUrl);
  74.         return Page();
  75.     }
  76.  
  77.     private async Task BuildModelAsync (string returnUrl)
  78.     {
  79.         Input = new InputModel
  80.         {
  81.             ReturnUrl = returnUrl
  82.         };
  83.  
  84.         var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
  85.         if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null)
  86.         {
  87.             var local = context.IdP == Duende.IdentityServer.IdentityServerConstants.LocalIdentityProvider;
  88.  
  89.             // this is meant to short circuit the UI and only trigger the one external IdP
  90.             View = new ViewModel
  91.             {
  92.                 EnableLocalLogin = local,
  93.             };
  94.  
  95.             Input.Username = context?.LoginHint;
  96.  
  97.             if (!local)
  98.             {
  99.                 View.ExternalProviders = new[] { new ViewModel.ExternalProvider { AuthenticationScheme = context?.IdP } };
  100.             }
  101.  
  102.             return;
  103.         }
  104.  
  105.         var schemes = await _schemeProvider.GetAllSchemesAsync();
  106.  
  107.         var providers = schemes
  108.             .Where(x => x.DisplayName != null)
  109.             .Select(
  110.                 x => new ViewModel.ExternalProvider
  111.                 {
  112.                     DisplayName = x.DisplayName ?? x.Name,
  113.                     AuthenticationScheme = x.Name
  114.                 }).ToList();
  115.  
  116.         var dyanmicSchemes = (await _identityProviderStore.GetAllSchemeNamesAsync())
  117.             .Where(x => x.Enabled)
  118.             .Select(
  119.                 x => new ViewModel.ExternalProvider
  120.                 {
  121.                     AuthenticationScheme = x.Scheme,
  122.                     DisplayName = x.DisplayName
  123.                 });
  124.         providers.AddRange(dyanmicSchemes);
  125.  
  126.  
  127.         var allowLocal = true;
  128.         var client = context?.Client;
  129.         if (client != null)
  130.         {
  131.             allowLocal = client.EnableLocalLogin;
  132.             if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any())
  133.             {
  134.                 providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList();
  135.             }
  136.         }
  137.  
  138.         View = new ViewModel
  139.         {
  140.             AllowRememberLogin = LoginOptions.AllowRememberLogin,
  141.             EnableLocalLogin = allowLocal && LoginOptions.AllowLocalLogin,
  142.             ExternalProviders = providers.ToArray()
  143.         };
  144.     }
  145.    
  146.     private async Task<IActionResult> HandleUserCancellation(AuthorizationRequest context)
  147.     {
  148.         if (context == null) return Redirect("~/");
  149.         // if the user cancels, send a result back into IdentityServer as if they
  150.         // denied the consent (even if this client does not require consent).
  151.         // this will send back an access denied OIDC error response to the client.
  152.         await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
  153.  
  154.         // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
  155.         return context.IsNativeClient() ?
  156.             // The client is native, so this change in how to return the response is for better UX for the end user.
  157.             this.LoadingPage(Input.ReturnUrl) : Redirect(Input.ReturnUrl);
  158.         // since we don't have a valid context, then we just go back to the home page
  159.     }
  160.  
  161.     private async Task<IActionResult> PerformLogin(AuthorizationRequest context)
  162.     {
  163.         var user = await _userManager.FindByNameAsync(Input.Username);
  164.        
  165.         if (user == null)
  166.         {
  167.             // If username is not found, try to find by email
  168.             user = await _userManager.FindByEmailAsync(Input.Username);
  169.             if (user == null)
  170.             {
  171.                 ModelState.AddModelError(string.Empty, "Invalid user");
  172.        
  173.                 await BuildModelAsync(Input.ReturnUrl);
  174.                 return Page();  
  175.             }
  176.         }
  177.  
  178.         if (!user.EmailConfirmed)
  179.         {
  180.             TempData["UserId"] = user.Id;
  181.             // Redirect to a confirmation page.
  182.             return RedirectToPage("/Account/ResendConfirm");
  183.         }
  184.  
  185.         if (user.UserName is null)
  186.         {
  187.             ModelState.AddModelError(string.Empty, "Invalid user");
  188.             return Page();
  189.         }
  190.         var result = await _signInManager.PasswordSignInAsync(user.UserName, Input.Password, Input.RememberLogin, lockoutOnFailure: true);
  191.  
  192.         if (result.Succeeded)
  193.         {
  194.             return await HandleLoginResult(context, user, result);
  195.         }
  196.  
  197.         // Add error and raise event in case of invalid credentials
  198.         ModelState.AddModelError(string.Empty, LoginOptions.InvalidCredentialsErrorMessage);
  199.         await _events.RaiseAsync(new UserLoginFailureEvent(Input.Username, "invalid credentials", clientId: context?.Client.ClientId));
  200.    
  201.         await BuildModelAsync(Input.ReturnUrl);
  202.         return Page();
  203.     }
  204.    
  205.     private async Task<IActionResult> HandleLoginResult(AuthorizationRequest context, ApplicationUser user, Microsoft.AspNetCore.Identity.SignInResult result)
  206.     {
  207.         if (result.Succeeded)
  208.         {
  209.             await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName, clientId: context?.Client.ClientId));
  210.            
  211.             if (context != null)
  212.             {
  213.                 return context.IsNativeClient()
  214.                     ?
  215.                     // The client is native, so this change in how to
  216.                     // return the response is for better UX for the end user.
  217.                     this.LoadingPage(Input.ReturnUrl)
  218.                     :
  219.                     // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
  220.                     Redirect(Input.ReturnUrl);
  221.             }
  222.  
  223.             // request for a local page
  224.             if (Url.IsLocalUrl(Input.ReturnUrl))
  225.             {
  226.                 return Redirect(Input.ReturnUrl);
  227.             }
  228.             if (string.IsNullOrEmpty(Input.ReturnUrl))
  229.             {
  230.                 return Redirect("~/");
  231.             }
  232.  
  233.             // throw new Exception("invalid return URL"); // user clicked a malicious link
  234.             // Logger.LogWarning("Invalid return URL redirect attempted to '{ReturnUrl}'. A potential Open Redirect Attack was detected and mitigation action was taken by redirecting user to the base/home URL.", Input.ReturnUrl);
  235.             return Redirect("~/");
  236.         }
  237.  
  238.         if (result.RequiresTwoFactor)
  239.         {
  240.             //TODO: Implement 2FA
  241.             // return RedirectToPage("./LoginWith2fa", new { ReturnUrl = Input.ReturnUrl, RememberMe = Input.RememberLogin });
  242.         }
  243.        
  244.         if (result.IsLockedOut)
  245.         {
  246.             //TODO: Implement lockout
  247.             return RedirectToPage("/Account/Lockedout");
  248.         }
  249.  
  250.         return null;
  251.     }
  252. }
  253.  
Advertisement
Add Comment
Please, Sign In to add comment