-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from alexs0ff/4-add-windows-login
Added windows login
- Loading branch information
Showing
24 changed files
with
661 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System; | ||
using IdentityServer4WebApp.Data; | ||
using IdentityServer4WebApp.Models; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.AspNetCore.Identity; | ||
using Microsoft.AspNetCore.Identity.UI; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
[assembly: HostingStartup(typeof(IdentityServer4WebApp.Areas.Identity.IdentityHostingStartup))] | ||
namespace IdentityServer4WebApp.Areas.Identity | ||
{ | ||
public class IdentityHostingStartup : IHostingStartup | ||
{ | ||
public void Configure(IWebHostBuilder builder) | ||
{ | ||
builder.ConfigureServices((context, services) => { | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
@page | ||
@model ExternalLoginModel | ||
@{ | ||
ViewData["Title"] = "Register"; | ||
} | ||
|
||
<h1>@ViewData["Title"]</h1> | ||
<h4>Associate your @Model.LoginProvider account.</h4> | ||
<hr /> | ||
|
||
<p class="text-info"> | ||
You've successfully authenticated with <strong>@Model.LoginProvider</strong>. | ||
Please enter an email address for this site below and click the Register button to finish | ||
logging in. | ||
</p> | ||
|
||
<div class="row"> | ||
<div class="col-md-4"> | ||
<form asp-page-handler="Confirmation" asp-route-returnUrl="@Model.ReturnUrl" method="post"> | ||
<div asp-validation-summary="All" class="text-danger"></div> | ||
<div class="form-group"> | ||
<label asp-for="Input.Email"></label> | ||
<input asp-for="Input.Email" class="form-control" /> | ||
<span asp-validation-for="Input.Email" class="text-danger"></span> | ||
</div> | ||
<button type="submit" class="btn btn-primary">Register</button> | ||
</form> | ||
</div> | ||
</div> | ||
|
||
@section Scripts { | ||
<partial name="_ValidationScriptsPartial" /> | ||
} |
208 changes: 208 additions & 0 deletions
208
src/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel.DataAnnotations; | ||
using System.Linq; | ||
using System.Security.Claims; | ||
using System.Security.Principal; | ||
using System.Threading.Tasks; | ||
using IdentityModel; | ||
using Microsoft.AspNetCore.Authorization; | ||
using IdentityServer4WebApp.Models; | ||
using Microsoft.AspNetCore.Authentication; | ||
using Microsoft.AspNetCore.Identity; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
using Microsoft.AspNetCore.Server.IISIntegration; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace IdentityServer4WebApp.Areas.Identity.Pages.Account | ||
{ | ||
[AllowAnonymous] | ||
public class ExternalLoginModel : PageModel | ||
{ | ||
private readonly SignInManager<ApplicationUser> _signInManager; | ||
private readonly UserManager<ApplicationUser> _userManager; | ||
private readonly ILogger<ExternalLoginModel> _logger; | ||
|
||
public ExternalLoginModel( | ||
SignInManager<ApplicationUser> signInManager, | ||
UserManager<ApplicationUser> userManager, | ||
ILogger<ExternalLoginModel> logger) | ||
{ | ||
_signInManager = signInManager; | ||
_userManager = userManager; | ||
_logger = logger; | ||
} | ||
|
||
[BindProperty] | ||
public InputModel Input { get; set; } | ||
|
||
public string LoginProvider { get; set; } | ||
|
||
public string ReturnUrl { get; set; } | ||
|
||
[TempData] | ||
public string ErrorMessage { get; set; } | ||
|
||
public class InputModel | ||
{ | ||
[Required] | ||
[EmailAddress] | ||
public string Email { get; set; } | ||
} | ||
|
||
public IActionResult OnGetAsync() | ||
{ | ||
return RedirectToPage("./Login"); | ||
} | ||
|
||
public async Task<IActionResult> OnPost(string provider, string returnUrl = null) | ||
{ | ||
if (IISDefaults.AuthenticationScheme == provider) | ||
{ | ||
return await ProcessWindowsLoginAsync(returnUrl); | ||
} | ||
|
||
var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl }); | ||
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); | ||
return new ChallengeResult(provider, properties); | ||
} | ||
|
||
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null) | ||
{ | ||
returnUrl = returnUrl ?? Url.Content("~/"); | ||
if (remoteError != null) | ||
{ | ||
ErrorMessage = $"Error from external provider: {remoteError}"; | ||
return RedirectToPage("./Login", new {ReturnUrl = returnUrl }); | ||
} | ||
var info = await _signInManager.GetExternalLoginInfoAsync(); | ||
if (info == null) | ||
{ | ||
ErrorMessage = "Error loading external login information."; | ||
return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); | ||
} | ||
|
||
// Sign in the user with this external login provider if the user already has a login. | ||
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true); | ||
if (result.Succeeded) | ||
{ | ||
var user = await _userManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey); | ||
|
||
await UpdateClaims(info, user, "hasUsersGroup"); | ||
|
||
_logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider); | ||
return LocalRedirect(returnUrl); | ||
} | ||
if (result.IsLockedOut) | ||
{ | ||
return RedirectToPage("./Lockout"); | ||
} | ||
else | ||
{ | ||
// If the user does not have an account, then ask the user to create an account. | ||
ReturnUrl = returnUrl; | ||
LoginProvider = info.LoginProvider; | ||
if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) | ||
{ | ||
Input = new InputModel | ||
{ | ||
Email = info.Principal.FindFirstValue(ClaimTypes.Email) | ||
}; | ||
} | ||
return Page(); | ||
} | ||
} | ||
|
||
public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null) | ||
{ | ||
returnUrl = returnUrl ?? Url.Content("~/"); | ||
// Get the information about the user from the external login provider | ||
var info = await _signInManager.GetExternalLoginInfoAsync(); | ||
if (info == null) | ||
{ | ||
ErrorMessage = "Error loading external login information during confirmation."; | ||
return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); | ||
} | ||
|
||
if (ModelState.IsValid) | ||
{ | ||
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; | ||
var result = await _userManager.CreateAsync(user); | ||
if (result.Succeeded) | ||
{ | ||
result = await _userManager.AddLoginAsync(user, info); | ||
if (result.Succeeded) | ||
{ | ||
await _signInManager.SignInAsync(user, isPersistent: false); | ||
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); | ||
await UpdateClaims(info, user, "hasUsersGroup"); | ||
return LocalRedirect(returnUrl); | ||
} | ||
} | ||
foreach (var error in result.Errors) | ||
{ | ||
ModelState.AddModelError(string.Empty, error.Description); | ||
} | ||
} | ||
|
||
LoginProvider = info.LoginProvider; | ||
ReturnUrl = returnUrl; | ||
return Page(); | ||
} | ||
|
||
private async Task<IActionResult> ProcessWindowsLoginAsync(string returnUrl) | ||
{ | ||
var result = await HttpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme); | ||
if (result?.Principal is WindowsPrincipal wp) | ||
{ | ||
var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl }); | ||
|
||
var props = _signInManager.ConfigureExternalAuthenticationProperties(IISDefaults.AuthenticationScheme, redirectUrl); | ||
props.Items["scheme"] = IISDefaults.AuthenticationScheme; | ||
|
||
var id = new ClaimsIdentity(IISDefaults.AuthenticationScheme); | ||
id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name)); | ||
id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name)); | ||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, wp.Identity.Name)); | ||
|
||
var wi = wp.Identity as WindowsIdentity; | ||
var groups = wi.Groups.Translate(typeof(NTAccount)); | ||
var hasUsersGroup = groups.Any(i => i.Value.Contains(@"BUILTIN\Users", StringComparison.OrdinalIgnoreCase)); | ||
|
||
id.AddClaim(new Claim("hasUsersGroup", hasUsersGroup.ToString())); | ||
|
||
await HttpContext.SignInAsync(IdentityConstants.ExternalScheme, new ClaimsPrincipal(id), props); | ||
|
||
return Redirect(props.RedirectUri); | ||
} | ||
|
||
return Challenge(IISDefaults.AuthenticationScheme); | ||
} | ||
|
||
private async Task UpdateClaims(ExternalLoginInfo info, ApplicationUser user, params string[] claimTypes) | ||
{ | ||
if (claimTypes == null) | ||
{ | ||
return; | ||
} | ||
|
||
var claimTypesHash = new HashSet<string>(claimTypes); | ||
|
||
var claims = (await _userManager.GetClaimsAsync(user)).Where(c => claimTypesHash.Contains(c.Type)).ToList(); | ||
|
||
await _userManager.RemoveClaimsAsync(user, claims); | ||
|
||
foreach (var claimType in claimTypes) | ||
{ | ||
if (info.Principal.HasClaim(c => c.Type == claimType)) | ||
{ | ||
claims = info.Principal.FindAll(claimType).ToList(); | ||
await _userManager.AddClaimsAsync(user, claims); | ||
} | ||
} | ||
} | ||
|
||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
@page | ||
@model LoginModel | ||
|
||
@{ | ||
ViewData["Title"] = "Log in"; | ||
} | ||
|
||
<h1>@ViewData["Title"]</h1> | ||
<div class="row"> | ||
<div class="col-md-4"> | ||
<section> | ||
<form id="account" method="post"> | ||
<h4>Use a local account to log in.</h4> | ||
<hr /> | ||
<div asp-validation-summary="All" class="text-danger"></div> | ||
<div class="form-group"> | ||
<label asp-for="Input.Email"></label> | ||
<input asp-for="Input.Email" class="form-control" /> | ||
<span asp-validation-for="Input.Email" class="text-danger"></span> | ||
</div> | ||
<div class="form-group"> | ||
<label asp-for="Input.Password"></label> | ||
<input asp-for="Input.Password" class="form-control" /> | ||
<span asp-validation-for="Input.Password" class="text-danger"></span> | ||
</div> | ||
<div class="form-group"> | ||
<div class="checkbox"> | ||
<label asp-for="Input.RememberMe"> | ||
<input asp-for="Input.RememberMe" /> | ||
@Html.DisplayNameFor(m => m.Input.RememberMe) | ||
</label> | ||
</div> | ||
</div> | ||
<div class="form-group"> | ||
<button type="submit" class="btn btn-primary">Log in</button> | ||
</div> | ||
<div class="form-group"> | ||
<p> | ||
<a id="forgot-password" asp-page="./ForgotPassword">Forgot your password?</a> | ||
</p> | ||
<p> | ||
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a> | ||
</p> | ||
</div> | ||
</form> | ||
</section> | ||
</div> | ||
<div class="col-md-6 col-md-offset-2"> | ||
<section> | ||
<h4>Use another service to log in.</h4> | ||
<hr /> | ||
@{ | ||
if ((Model.ExternalLogins?.Count ?? 0) == 0) | ||
{ | ||
<div> | ||
<p> | ||
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a> | ||
for details on setting up this ASP.NET application to support logging in via external services. | ||
</p> | ||
</div> | ||
} | ||
else | ||
{ | ||
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal"> | ||
<div> | ||
<p> | ||
@foreach (var provider in Model.ExternalLogins) | ||
{ | ||
<button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @(provider.DisplayName ??provider.Name) account">@(provider.DisplayName ?? provider.Name)</button> | ||
} | ||
</p> | ||
</div> | ||
</form> | ||
} | ||
} | ||
</section> | ||
</div> | ||
</div> | ||
|
||
@section Scripts { | ||
<partial name="_ValidationScriptsPartial" /> | ||
} |
Oops, something went wrong.