Add social auth and two factor
- Merge Authentication into Core - Add social login support - Add two factor support - Rework options model for startup
This commit is contained in:
parent
5a2de1aaac
commit
3f4f846cbb
28
Identity.sln
28
Identity.sln
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.21722.1
|
VisualStudioVersion = 14.0.22013.1
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -11,12 +11,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity",
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.SqlServer", "src\Microsoft.AspNet.Identity.SqlServer\Microsoft.AspNet.Identity.SqlServer.kproj", "{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.SqlServer", "src\Microsoft.AspNet.Identity.SqlServer\Microsoft.AspNet.Identity.SqlServer.kproj", "{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.Authentication", "src\Microsoft.AspNet.Identity.Authentication\Microsoft.AspNet.Identity.Authentication.kproj", "{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}"
|
|
||||||
EndProject
|
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.Test", "test\Microsoft.AspNet.Identity.Test\Microsoft.AspNet.Identity.Test.kproj", "{2CF3927B-19E4-4866-9BAA-2C131580E7C3}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.Test", "test\Microsoft.AspNet.Identity.Test\Microsoft.AspNet.Identity.Test.kproj", "{2CF3927B-19E4-4866-9BAA-2C131580E7C3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.Authentication.Test", "test\Microsoft.AspNet.Identity.Authentication.Test\Microsoft.AspNet.Identity.Authentication.Test.kproj", "{823453CC-5846-4D49-B343-15BC0074CA60}"
|
|
||||||
EndProject
|
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.InMemory.Test", "test\Microsoft.AspNet.Identity.InMemory.Test\Microsoft.AspNet.Identity.InMemory.Test.kproj", "{65161409-C4C4-4D63-A73B-231FCFF4D503}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.InMemory.Test", "test\Microsoft.AspNet.Identity.InMemory.Test\Microsoft.AspNet.Identity.InMemory.Test.kproj", "{65161409-C4C4-4D63-A73B-231FCFF4D503}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.SqlServer.Test", "test\Microsoft.AspNet.Identity.SqlServer.Test\Microsoft.AspNet.Identity.SqlServer.Test.kproj", "{B4C067C1-F934-493C-9DBC-19E8CA305613}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.SqlServer.Test", "test\Microsoft.AspNet.Identity.SqlServer.Test\Microsoft.AspNet.Identity.SqlServer.Test.kproj", "{B4C067C1-F934-493C-9DBC-19E8CA305613}"
|
||||||
|
|
@ -62,16 +58,6 @@ Global
|
||||||
{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}.Release|x86.ActiveCfg = Release|Any CPU
|
{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
|
@ -82,16 +68,6 @@ Global
|
||||||
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x86.ActiveCfg = Release|Any CPU
|
{2CF3927B-19E4-4866-9BAA-2C131580E7C3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
{65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
|
@ -139,9 +115,7 @@ Global
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{1729302E-A58E-4652-B639-5B6B68DA2748} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
{1729302E-A58E-4652-B639-5B6B68DA2748} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
||||||
{AD42BAFB-1993-4FAF-A280-3711A9F33E2F} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
{AD42BAFB-1993-4FAF-A280-3711A9F33E2F} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
||||||
{7B4CFF5A-1948-45EC-B170-6EB7C039B2F9} = {0F647068-6602-4E24-B1DC-8ED91481A50A}
|
|
||||||
{2CF3927B-19E4-4866-9BAA-2C131580E7C3} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
{2CF3927B-19E4-4866-9BAA-2C131580E7C3} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||||
{823453CC-5846-4D49-B343-15BC0074CA60} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
|
||||||
{65161409-C4C4-4D63-A73B-231FCFF4D503} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
{65161409-C4C4-4D63-A73B-231FCFF4D503} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||||
{B4C067C1-F934-493C-9DBC-19E8CA305613} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
{B4C067C1-F934-493C-9DBC-19E8CA305613} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||||
{813B36FE-BBA5-4449-B157-6EBBA5ED02CA} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
{813B36FE-BBA5-4449-B157-6EBBA5ED02CA} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,30 @@
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using IdentitySample.Models;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IdentitySample.Models
|
namespace IdentitySample.Models
|
||||||
{
|
{
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class AccountController : Controller
|
public class AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
||||||
|
: Controller
|
||||||
{
|
{
|
||||||
public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
public UserManager<ApplicationUser> UserManager { get; } = userManager;
|
||||||
{
|
|
||||||
UserManager = userManager;
|
|
||||||
SignInManager = signInManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserManager<ApplicationUser> UserManager { get; private set; }
|
public SignInManager<ApplicationUser> SignInManager { get; } = signInManager;
|
||||||
|
|
||||||
public SignInManager<ApplicationUser> SignInManager { get; private set; }
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Account/Login
|
// GET: /Account/Login
|
||||||
|
[HttpGet]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IActionResult Login(string returnUrl = null)
|
public IActionResult Login(string returnUrl = null)
|
||||||
{
|
{
|
||||||
ViewBag.ReturnUrl = returnUrl;
|
ViewBag.ReturnUrl = returnUrl;
|
||||||
|
ViewBag.LoginProviders = Context.GetExternalAuthenticationTypes().ToList();
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,6 +45,8 @@ namespace IdentitySample.Models
|
||||||
case SignInStatus.LockedOut:
|
case SignInStatus.LockedOut:
|
||||||
ModelState.AddModelError("", "User is locked out, try again later.");
|
ModelState.AddModelError("", "User is locked out, try again later.");
|
||||||
return View(model);
|
return View(model);
|
||||||
|
case SignInStatus.RequiresVerification:
|
||||||
|
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
|
||||||
case SignInStatus.Failure:
|
case SignInStatus.Failure:
|
||||||
default:
|
default:
|
||||||
ModelState.AddModelError("", "Invalid username or password.");
|
ModelState.AddModelError("", "Invalid username or password.");
|
||||||
|
|
@ -58,6 +60,7 @@ namespace IdentitySample.Models
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Account/Register
|
// GET: /Account/Register
|
||||||
|
[HttpGet]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IActionResult Register()
|
public IActionResult Register()
|
||||||
{
|
{
|
||||||
|
|
@ -73,54 +76,17 @@ namespace IdentitySample.Models
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
var user = new ApplicationUser { UserName = model.UserName };
|
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
|
||||||
var result = await UserManager.CreateAsync(user, model.Password);
|
var result = await UserManager.CreateAsync(user, model.Password);
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
|
||||||
return RedirectToAction("Index", "Home");
|
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
|
||||||
}
|
await UserManager.SendEmailAsync(user, "Confirm your account", "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
|
||||||
else
|
ViewBag.Link = callbackUrl;
|
||||||
{
|
return View("DisplayEmail");
|
||||||
AddErrors(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got this far, something failed, redisplay form
|
|
||||||
return View(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// GET: /Account/Manage
|
|
||||||
public IActionResult Manage(ManageMessageId? message = null)
|
|
||||||
{
|
|
||||||
ViewBag.StatusMessage =
|
|
||||||
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
|
|
||||||
: message == ManageMessageId.Error ? "An error has occurred."
|
|
||||||
: "";
|
|
||||||
ViewBag.ReturnUrl = Url.Action("Manage");
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// POST: /Account/Manage
|
|
||||||
[HttpPost]
|
|
||||||
[ValidateAntiForgeryToken]
|
|
||||||
public async Task<IActionResult> Manage(ManageUserViewModel model)
|
|
||||||
{
|
|
||||||
ViewBag.ReturnUrl = Url.Action("Manage");
|
|
||||||
if (ModelState.IsValid)
|
|
||||||
{
|
|
||||||
var user = await GetCurrentUserAsync();
|
|
||||||
var result = await UserManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
|
|
||||||
if (result.Succeeded)
|
|
||||||
{
|
|
||||||
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddErrors(result);
|
|
||||||
}
|
}
|
||||||
|
AddErrors(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we got this far, something failed, redisplay form
|
// If we got this far, something failed, redisplay form
|
||||||
|
|
@ -137,6 +103,277 @@ namespace IdentitySample.Models
|
||||||
return RedirectToAction("Index", "Home");
|
return RedirectToAction("Index", "Home");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/ExternalLogin
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public IActionResult ExternalLogin(string provider, string returnUrl = null)
|
||||||
|
{
|
||||||
|
// Request a redirect to the external login provider
|
||||||
|
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
|
||||||
|
var properties = Context.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
|
||||||
|
return new ChallengeResult(provider, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ExternalLoginCallback
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
|
||||||
|
{
|
||||||
|
var info = await Context.GetExternalLoginInfo();
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
return RedirectToAction("Login");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case SignInStatus.Success:
|
||||||
|
return RedirectToLocal(returnUrl);
|
||||||
|
case SignInStatus.LockedOut:
|
||||||
|
return View("Lockout");
|
||||||
|
case SignInStatus.RequiresVerification:
|
||||||
|
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
|
||||||
|
case SignInStatus.Failure:
|
||||||
|
default:
|
||||||
|
// If the user does not have an account, then prompt the user to create an account
|
||||||
|
ViewBag.ReturnUrl = returnUrl;
|
||||||
|
ViewBag.LoginProvider = info.LoginProvider;
|
||||||
|
// REVIEW: handle case where email not in claims?
|
||||||
|
var email = info.ExternalIdentity.FindFirstValue(ClaimTypes.Email);
|
||||||
|
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/ExternalLoginConfirmation
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null)
|
||||||
|
{
|
||||||
|
if (User.Identity.IsAuthenticated)
|
||||||
|
{
|
||||||
|
return RedirectToAction("Index", "Manage");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
// Get the information about the user from the external login provider
|
||||||
|
var info = await Context.GetExternalLoginInfo();
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
return View("ExternalLoginFailure");
|
||||||
|
}
|
||||||
|
var user = new ApplicationUser { UserName = model.Email, Email = model.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);
|
||||||
|
return RedirectToLocal(returnUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddErrors(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewBag.ReturnUrl = returnUrl;
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// REVIEW: We should make this a POST
|
||||||
|
//
|
||||||
|
// GET: /Account/ConfirmEmail
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> ConfirmEmail(string userId, string code)
|
||||||
|
{
|
||||||
|
if (userId == null || code == null)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
var user = await UserManager.FindByIdAsync(userId);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
var result = await UserManager.ConfirmEmailAsync(user, code);
|
||||||
|
return View(result.Succeeded ? "ConfirmEmail" : "Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ForgotPassword
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult ForgotPassword()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/ForgotPassword
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
var user = await UserManager.FindByNameAsync(model.Email);
|
||||||
|
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user)))
|
||||||
|
{
|
||||||
|
// Don't reveal that the user does not exist or is not confirmed
|
||||||
|
return View("ForgotPasswordConfirmation");
|
||||||
|
}
|
||||||
|
|
||||||
|
var code = await UserManager.GeneratePasswordResetTokenAsync(user);
|
||||||
|
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
|
||||||
|
await UserManager.SendEmailAsync(user, "Reset Password", "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
|
||||||
|
ViewBag.Link = callbackUrl;
|
||||||
|
return View("ForgotPasswordConfirmation");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got this far, something failed, redisplay form
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ForgotPasswordConfirmation
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult ForgotPasswordConfirmation()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
var user = await UserManager.FindByNameAsync(model.Email);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
// Don't reveal that the user does not exist
|
||||||
|
return RedirectToAction("ResetPasswordConfirmation", "Account");
|
||||||
|
}
|
||||||
|
var result = await UserManager.ResetPasswordAsync(user, model.Code, model.Password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return RedirectToAction("ResetPasswordConfirmation", "Account");
|
||||||
|
}
|
||||||
|
AddErrors(result);
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/ResetPasswordConfirmation
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult ResetPasswordConfirmation()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/SendCode
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<ActionResult> SendCode(string returnUrl = null, bool rememberMe = false)
|
||||||
|
{
|
||||||
|
var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(user);
|
||||||
|
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
|
||||||
|
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/SendCode
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> SendCode(SendCodeViewModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the token and send it
|
||||||
|
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GET: /Account/VerifyCode
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null)
|
||||||
|
{
|
||||||
|
var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
// Remove before production
|
||||||
|
ViewBag.Status = "For DEMO purposes the current " + provider + " code is: " + await UserManager.GenerateTwoFactorTokenAsync(user, provider);
|
||||||
|
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POST: /Account/VerifyCode
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> VerifyCode(VerifyCodeViewModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser);
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case SignInStatus.Success:
|
||||||
|
return RedirectToLocal(model.ReturnUrl);
|
||||||
|
case SignInStatus.LockedOut:
|
||||||
|
return View("Lockout");
|
||||||
|
default:
|
||||||
|
ModelState.AddModelError("", "Invalid code.");
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region Helpers
|
#region Helpers
|
||||||
|
|
||||||
private void AddErrors(IdentityResult result)
|
private void AddErrors(IdentityResult result)
|
||||||
|
|
@ -152,12 +389,6 @@ namespace IdentitySample.Models
|
||||||
return await UserManager.FindByIdAsync(Context.User.Identity.GetUserId());
|
return await UserManager.FindByIdAsync(Context.User.Identity.GetUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ManageMessageId
|
|
||||||
{
|
|
||||||
ChangePasswordSuccess,
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
|
|
||||||
private IActionResult RedirectToLocal(string returnUrl)
|
private IActionResult RedirectToLocal(string returnUrl)
|
||||||
{
|
{
|
||||||
if (Url.IsLocalUrl(returnUrl))
|
if (Url.IsLocalUrl(returnUrl))
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ namespace IdentitySample.Models
|
||||||
{
|
{
|
||||||
public class HomeController : Controller
|
public class HomeController : Controller
|
||||||
{
|
{
|
||||||
|
[HttpGet]
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize]
|
[HttpGet]
|
||||||
public IActionResult About()
|
public IActionResult About()
|
||||||
{
|
{
|
||||||
ViewBag.Message = "Your app description page.";
|
ViewBag.Message = "Your app description page.";
|
||||||
|
|
@ -17,6 +18,7 @@ namespace IdentitySample.Models
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
public IActionResult Contact()
|
public IActionResult Contact()
|
||||||
{
|
{
|
||||||
ViewBag.Message = "Your contact page.";
|
ViewBag.Message = "Your contact page.";
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,24 @@
|
||||||
|
using IdentitySample.Models;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Identity;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using IdentitySample.Models;
|
using System.Linq;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IdentitySample
|
namespace IdentitySample
|
||||||
{
|
{
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class ManageController : Controller
|
public class ManageController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
||||||
|
: Controller
|
||||||
{
|
{
|
||||||
public ManageController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
public UserManager<ApplicationUser> UserManager { get; } = userManager;
|
||||||
{
|
|
||||||
UserManager = userManager;
|
|
||||||
SignInManager = signInManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserManager<ApplicationUser> UserManager { get; private set; }
|
public SignInManager<ApplicationUser> SignInManager { get; } = signInManager;
|
||||||
|
|
||||||
public SignInManager<ApplicationUser> SignInManager { get; private set; }
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Account/Index
|
// GET: /Account/Index
|
||||||
|
[HttpGet]
|
||||||
public async Task<IActionResult> Index(ManageMessageId? message = null)
|
public async Task<IActionResult> Index(ManageMessageId? message = null)
|
||||||
{
|
{
|
||||||
ViewBag.StatusMessage =
|
ViewBag.StatusMessage =
|
||||||
|
|
@ -46,6 +44,7 @@ namespace IdentitySample
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Account/RemoveLogin
|
// GET: /Account/RemoveLogin
|
||||||
|
[HttpGet]
|
||||||
public async Task<IActionResult> RemoveLogin()
|
public async Task<IActionResult> RemoveLogin()
|
||||||
{
|
{
|
||||||
var user = await GetCurrentUserAsync();
|
var user = await GetCurrentUserAsync();
|
||||||
|
|
@ -92,7 +91,6 @@ namespace IdentitySample
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
// Generate the token and send it
|
// Generate the token and send it
|
||||||
#if ASPNET50
|
|
||||||
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), model.Number);
|
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), model.Number);
|
||||||
if (UserManager.SmsService != null)
|
if (UserManager.SmsService != null)
|
||||||
{
|
{
|
||||||
|
|
@ -103,19 +101,19 @@ namespace IdentitySample
|
||||||
};
|
};
|
||||||
await UserManager.SmsService.SendAsync(message);
|
await UserManager.SmsService.SendAsync(message);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
|
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// POST: /Manage/RememberBrowser
|
// POST: /Manage/RememberBrowser
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> RememberBrowser()
|
public async Task<IActionResult> RememberBrowser()
|
||||||
{
|
{
|
||||||
var user = await GetCurrentUserAsync();
|
var user = await GetCurrentUserAsync();
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await SignInManager.RememberTwoFactorClient(user);
|
await SignInManager.RememberTwoFactorClientAsync(user);
|
||||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||||
}
|
}
|
||||||
return RedirectToAction("Index", "Manage");
|
return RedirectToAction("Index", "Manage");
|
||||||
|
|
@ -124,6 +122,7 @@ namespace IdentitySample
|
||||||
//
|
//
|
||||||
// POST: /Manage/ForgetBrowser
|
// POST: /Manage/ForgetBrowser
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> ForgetBrowser()
|
public async Task<IActionResult> ForgetBrowser()
|
||||||
{
|
{
|
||||||
await SignInManager.ForgetTwoFactorClientAsync();
|
await SignInManager.ForgetTwoFactorClientAsync();
|
||||||
|
|
@ -133,6 +132,7 @@ namespace IdentitySample
|
||||||
//
|
//
|
||||||
// POST: /Manage/EnableTFA
|
// POST: /Manage/EnableTFA
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> EnableTFA()
|
public async Task<IActionResult> EnableTFA()
|
||||||
{
|
{
|
||||||
var user = await GetCurrentUserAsync();
|
var user = await GetCurrentUserAsync();
|
||||||
|
|
@ -148,6 +148,7 @@ namespace IdentitySample
|
||||||
//
|
//
|
||||||
// POST: /Manage/DisableTFA
|
// POST: /Manage/DisableTFA
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> DisableTFA()
|
public async Task<IActionResult> DisableTFA()
|
||||||
{
|
{
|
||||||
var user = await GetCurrentUserAsync();
|
var user = await GetCurrentUserAsync();
|
||||||
|
|
@ -161,14 +162,13 @@ namespace IdentitySample
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Account/VerifyPhoneNumber
|
// GET: /Account/VerifyPhoneNumber
|
||||||
|
[HttpGet]
|
||||||
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber)
|
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber)
|
||||||
{
|
{
|
||||||
// This code allows you exercise the flow without actually sending codes
|
// This code allows you exercise the flow without actually sending codes
|
||||||
// For production use please register a SMS provider in IdentityConfig and generate a code here.
|
// For production use please register a SMS provider in IdentityConfig and generate a code here.
|
||||||
#if ASPNET50
|
|
||||||
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber);
|
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber);
|
||||||
ViewBag.Status = "For DEMO purposes only, the current code is " + code;
|
ViewBag.Status = "For DEMO purposes only, the current code is " + code;
|
||||||
#endif
|
|
||||||
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
|
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,6 +199,7 @@ namespace IdentitySample
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Account/RemovePhoneNumber
|
// GET: /Account/RemovePhoneNumber
|
||||||
|
[HttpGet]
|
||||||
public async Task<IActionResult> RemovePhoneNumber()
|
public async Task<IActionResult> RemovePhoneNumber()
|
||||||
{
|
{
|
||||||
var user = await GetCurrentUserAsync();
|
var user = await GetCurrentUserAsync();
|
||||||
|
|
@ -216,6 +217,7 @@ namespace IdentitySample
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Manage/ChangePassword
|
// GET: /Manage/ChangePassword
|
||||||
|
[HttpGet]
|
||||||
public IActionResult ChangePassword()
|
public IActionResult ChangePassword()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
|
|
@ -248,6 +250,7 @@ namespace IdentitySample
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Manage/SetPassword
|
// GET: /Manage/SetPassword
|
||||||
|
[HttpGet]
|
||||||
public IActionResult SetPassword()
|
public IActionResult SetPassword()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
|
|
@ -279,55 +282,63 @@ namespace IdentitySample
|
||||||
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
|
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GET: /Account/Manage
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null)
|
||||||
|
{
|
||||||
|
ViewBag.StatusMessage =
|
||||||
|
message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
|
||||||
|
: message == ManageMessageId.AddLoginSuccess ? "The external login was added."
|
||||||
|
: message == ManageMessageId.Error ? "An error has occurred."
|
||||||
|
: "";
|
||||||
|
var user = await GetCurrentUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
var userLogins = await UserManager.GetLoginsAsync(user);
|
||||||
|
var otherLogins = Context.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
|
||||||
|
ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
|
||||||
|
return View(new ManageLoginsViewModel
|
||||||
|
{
|
||||||
|
CurrentLogins = userLogins,
|
||||||
|
OtherLogins = otherLogins
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// GET: /Account/Manage
|
// POST: /Manage/LinkLogin
|
||||||
//public async Task<IActionResult> ManageLogins(ManageMessageId? message)
|
[HttpPost]
|
||||||
//{
|
[ValidateAntiForgeryToken]
|
||||||
// ViewBag.StatusMessage =
|
public IActionResult LinkLogin(string provider)
|
||||||
// message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
|
{
|
||||||
// : message == ManageMessageId.Error ? "An error has occurred."
|
// Request a redirect to the external login provider to link a login for the current user
|
||||||
// : "";
|
var redirectUrl = Url.Action("LinkLoginCallback", "Manage");
|
||||||
// var user = await GetCurrentUserAsync();
|
var properties = Context.ConfigureExternalAuthenticationProperties(provider, redirectUrl, User.Identity.GetUserId());
|
||||||
// if (user == null)
|
return new ChallengeResult(provider, properties);
|
||||||
// {
|
}
|
||||||
// return View("Error");
|
|
||||||
// }
|
|
||||||
// var userLogins = await UserManager.GetLoginsAsync(user);
|
|
||||||
//var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
|
|
||||||
//ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
|
|
||||||
//return View(new ManageLoginsViewModel
|
|
||||||
//{
|
|
||||||
// CurrentLogins = userLogins,
|
|
||||||
// OtherLogins = otherLogins
|
|
||||||
//});
|
|
||||||
//}
|
|
||||||
|
|
||||||
////
|
//
|
||||||
//// POST: /Manage/LinkLogin
|
// GET: /Manage/LinkLoginCallback
|
||||||
//[HttpPost]
|
[HttpGet]
|
||||||
//[ValidateAntiForgeryToken]
|
public async Task<ActionResult> LinkLoginCallback()
|
||||||
//public IActionResult LinkLogin(string provider)
|
{
|
||||||
//{
|
var user = await GetCurrentUserAsync();
|
||||||
// // Request a redirect to the external login provider to link a login for the current user
|
if (user == null)
|
||||||
// return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
|
{
|
||||||
//}
|
return View("Error");
|
||||||
|
}
|
||||||
////
|
var info = await Context.GetExternalLoginInfo(User.Identity.GetUserId());
|
||||||
//// GET: /Manage/LinkLoginCallback
|
if (info == null)
|
||||||
//public async Task<ActionResult> LinkLoginCallback()
|
{
|
||||||
//{
|
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
|
||||||
// var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
|
}
|
||||||
// if (loginInfo == null)
|
var result = await UserManager.AddLoginAsync(user, info);
|
||||||
// {
|
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
|
||||||
// return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
|
return RedirectToAction("ManageLogins", new { Message = message });
|
||||||
// }
|
}
|
||||||
// var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
|
|
||||||
// return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
|
|
||||||
//}
|
|
||||||
|
|
||||||
#region Helpers
|
#region Helpers
|
||||||
// Used for XSRF protection when adding external logins
|
|
||||||
//private const string XsrfKey = "XsrfId";
|
|
||||||
|
|
||||||
private void AddErrors(IdentityResult result)
|
private void AddErrors(IdentityResult result)
|
||||||
{
|
{
|
||||||
|
|
@ -350,6 +361,7 @@ namespace IdentitySample
|
||||||
public enum ManageMessageId
|
public enum ManageMessageId
|
||||||
{
|
{
|
||||||
AddPhoneSuccess,
|
AddPhoneSuccess,
|
||||||
|
AddLoginSuccess,
|
||||||
ChangePasswordSuccess,
|
ChangePasswordSuccess,
|
||||||
SetTwoFactorSuccess,
|
SetTwoFactorSuccess,
|
||||||
SetPasswordSuccess,
|
SetPasswordSuccess,
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,66 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace IdentitySample.Models
|
namespace IdentitySample.Models
|
||||||
{
|
{
|
||||||
public class ExternalLoginConfirmationViewModel
|
public class ExternalLoginConfirmationViewModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[Display(Name = "User name")]
|
[Display(Name = "Email")]
|
||||||
public string UserName { get; set; }
|
public string Email { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ManageUserViewModel
|
public class SendCodeViewModel
|
||||||
|
{
|
||||||
|
public string SelectedProvider { get; set; }
|
||||||
|
public ICollection<SelectListItem> Providers { get; set; }
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VerifyCodeViewModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[DataType(DataType.Password)]
|
public string Provider { get; set; }
|
||||||
[Display(Name = "Current password")]
|
|
||||||
public string OldPassword { get; set; }
|
[Required]
|
||||||
|
[Display(Name = "Code")]
|
||||||
|
public string Code { get; set; }
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Remember this browser?")]
|
||||||
|
public bool RememberBrowser { get; set; }
|
||||||
|
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ResetPasswordViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
[DataType(DataType.Password)]
|
[DataType(DataType.Password)]
|
||||||
[Display(Name = "New password")]
|
[Display(Name = "Password")]
|
||||||
public string NewPassword { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
[DataType(DataType.Password)]
|
[DataType(DataType.Password)]
|
||||||
[Display(Name = "Confirm new password")]
|
[Display(Name = "Confirm password")]
|
||||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||||
public string ConfirmPassword { get; set; }
|
public string ConfirmPassword { get; set; }
|
||||||
|
|
||||||
|
public string Code { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ForgotPasswordViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[Display(Name = "Email")]
|
||||||
|
public string Email { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LoginViewModel
|
public class LoginViewModel
|
||||||
|
|
@ -46,8 +81,8 @@ namespace IdentitySample.Models
|
||||||
public class RegisterViewModel
|
public class RegisterViewModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[Display(Name = "User name")]
|
[Display(Name = "Email")]
|
||||||
public string UserName { get; set; }
|
public string Email { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
@ -14,11 +15,11 @@ namespace IdentitySample.Models
|
||||||
public bool BrowserRemembered { get; set; }
|
public bool BrowserRemembered { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
//public class ManageLoginsViewModel
|
public class ManageLoginsViewModel
|
||||||
//{
|
{
|
||||||
// public IList<UserLoginInfo> CurrentLogins { get; set; }
|
public IList<UserLoginInfo> CurrentLogins { get; set; }
|
||||||
// public IList<AuthenticationDescription> OtherLogins { get; set; }
|
public IList<AuthenticationDescription> OtherLogins { get; set; }
|
||||||
//}
|
}
|
||||||
|
|
||||||
public class FactorViewModel
|
public class FactorViewModel
|
||||||
{
|
{
|
||||||
|
|
@ -84,5 +85,4 @@ namespace IdentitySample.Models
|
||||||
public string SelectedProvider { get; set; }
|
public string SelectedProvider { get; set; }
|
||||||
public ICollection<SelectListItem> Providers { get; set; }
|
public ICollection<SelectListItem> Providers { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
using Microsoft.Framework.ConfigurationModel;
|
|
||||||
using Microsoft.Framework.DependencyInjection;
|
|
||||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
|
||||||
using Microsoft.AspNet.Hosting;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MusicStore
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This demonstrates how the application can be launched in a K console application.
|
|
||||||
/// k run command in the application folder will invoke this.
|
|
||||||
/// </summary>
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
private readonly IServiceProvider _hostServiceProvider;
|
|
||||||
|
|
||||||
public Program(IServiceProvider hostServiceProvider)
|
|
||||||
{
|
|
||||||
_hostServiceProvider = hostServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<int> Main(string[] args)
|
|
||||||
{
|
|
||||||
//Add command line configuration source to read command line parameters.
|
|
||||||
var config = new Configuration();
|
|
||||||
config.AddCommandLine(args);
|
|
||||||
|
|
||||||
var serviceCollection = new ServiceCollection();
|
|
||||||
serviceCollection.Add(HostingServices.GetDefaultServices(config));
|
|
||||||
var services = serviceCollection.BuildServiceProvider(_hostServiceProvider);
|
|
||||||
|
|
||||||
var context = new HostingContext()
|
|
||||||
{
|
|
||||||
Services = services,
|
|
||||||
Configuration = config,
|
|
||||||
ServerName = "Microsoft.AspNet.Server.WebListener",
|
|
||||||
ApplicationName = "MusicStore"
|
|
||||||
};
|
|
||||||
|
|
||||||
var engine = services.GetService<IHostingEngine>();
|
|
||||||
if (engine == null)
|
|
||||||
{
|
|
||||||
throw new Exception("TODO: IHostingEngine service not available exception");
|
|
||||||
}
|
|
||||||
|
|
||||||
using (engine.Start(context))
|
|
||||||
{
|
|
||||||
Console.WriteLine("Started the server..");
|
|
||||||
Console.WriteLine("Press any key to stop the server");
|
|
||||||
Console.ReadLine();
|
|
||||||
}
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +1,94 @@
|
||||||
|
using System;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Diagnostics;
|
using Microsoft.AspNet.Diagnostics;
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Identity;
|
||||||
using Microsoft.AspNet.Identity.Authentication;
|
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.AspNet.Security.Cookies;
|
using Microsoft.AspNet.Security.Facebook;
|
||||||
|
using Microsoft.AspNet.Security.Google;
|
||||||
|
using Microsoft.AspNet.Security.Twitter;
|
||||||
using Microsoft.Data.Entity;
|
using Microsoft.Data.Entity;
|
||||||
using Microsoft.Framework.ConfigurationModel;
|
using Microsoft.Framework.ConfigurationModel;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
using IdentitySample.Models;
|
using IdentitySample.Models;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace IdentitySamples
|
namespace IdentitySamples
|
||||||
{
|
{
|
||||||
public class Startup
|
public static class UseExt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Middleware constructors need to take IOptionsAccessor<TOptions>
|
||||||
|
|
||||||
|
* Move options setup into a different method?
|
||||||
|
|
||||||
|
* Cookie options need to be different, named service/option instances? i.e. Singleton Named Options
|
||||||
|
|
||||||
|
SetupNamedOption("ApplicationCookie", options => { })
|
||||||
|
UseCookieAuthentication("ApplicationCookie")
|
||||||
|
SetupNamedOption("ExternalCookie", options => { })
|
||||||
|
UseCookieAuthentication("ApplicationCookie")
|
||||||
|
|
||||||
|
// Overloads which use default/no name
|
||||||
|
SetupOption(options => { })
|
||||||
|
UseGoogleAuthentication()
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseGoogleAuthentication(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<GoogleAuthenticationMiddleware>();
|
||||||
|
//return builder.UseGoogleAuthentication(b =>
|
||||||
|
// b.ApplicationServices.GetService<IOptionsAccessor<GoogleAuthenticationOptions>>().Options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseGoogleAuthentication(this IApplicationBuilder builder, Func<IApplicationBuilder, GoogleAuthenticationOptions> func)
|
||||||
|
{
|
||||||
|
return builder.UseGoogleAuthentication(func(builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseFacebookAuthentication(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
// This should go inside of the middleware delegate
|
||||||
|
return builder.UseFacebookAuthentication(b =>
|
||||||
|
b.ApplicationServices.GetService<IOptionsAccessor<FacebookAuthenticationOptions>>().Options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseFacebookAuthentication(this IApplicationBuilder builder, Func<IApplicationBuilder, FacebookAuthenticationOptions> func)
|
||||||
|
{
|
||||||
|
return builder.UseFacebookAuthentication(func(builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseTwitterAuthentication(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseTwitterAuthentication(b =>
|
||||||
|
b.ApplicationServices.GetService<IOptionsAccessor<TwitterAuthenticationOptions>>().Options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseTwitterAuthentication(this IApplicationBuilder builder, Func<IApplicationBuilder, TwitterAuthenticationOptions> func)
|
||||||
|
{
|
||||||
|
return builder.UseTwitterAuthentication(func(builder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Startup()
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1' is found in both the registered sources,
|
||||||
|
* then the later source will win. By this way a Local config can be overridden by a different setting while deployed remotely.
|
||||||
|
*/
|
||||||
|
Configuration = new Configuration()
|
||||||
|
.AddJsonFile("LocalConfig.json")
|
||||||
|
.AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values.
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; private set; }
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
/* Adding IConfiguration as a service in the IoC to avoid instantiating Configuration again.
|
|
||||||
* Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1' is found in both the registered sources,
|
|
||||||
* then the later source will win. By this way a Local config can be overridden by a different setting while deployed remotely.
|
|
||||||
*/
|
|
||||||
var configuration = new Configuration();
|
|
||||||
configuration.AddJsonFile("LocalConfig.json");
|
|
||||||
configuration.AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values.
|
|
||||||
|
|
||||||
app.UseServices(services =>
|
app.UseServices(services =>
|
||||||
{
|
{
|
||||||
// Add EF services to the services container
|
// Add EF services to the services container
|
||||||
|
|
@ -34,21 +98,52 @@ namespace IdentitySamples
|
||||||
// Configure DbContext
|
// Configure DbContext
|
||||||
services.SetupOptions<IdentityDbContextOptions>(options =>
|
services.SetupOptions<IdentityDbContextOptions>(options =>
|
||||||
{
|
{
|
||||||
options.DefaultAdminUserName = configuration.Get("DefaultAdminUsername");
|
options.DefaultAdminUserName = Configuration.Get("DefaultAdminUsername");
|
||||||
options.DefaultAdminPassword = configuration.Get("DefaultAdminPassword");
|
options.DefaultAdminPassword = Configuration.Get("DefaultAdminPassword");
|
||||||
options.UseSqlServer(configuration.Get("Data:IdentityConnection:ConnectionString"));
|
options.UseSqlServer(Configuration.Get("Data:IdentityConnection:ConnectionString"));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add Identity services to the services container
|
// Add Identity services to the services container
|
||||||
services.AddIdentitySqlServer<ApplicationDbContext, ApplicationUser>()
|
services.AddDefaultIdentity<ApplicationDbContext, ApplicationUser, IdentityRole>(Configuration);
|
||||||
.AddAuthentication()
|
|
||||||
.SetupOptions(options =>
|
// move this into add identity along with the
|
||||||
{
|
//service.SetupOptions<ExternalAuthenticationOptions>(options => options.SignInAsAuthenticationType = "External")
|
||||||
options.Password.RequireDigit = false;
|
|
||||||
options.Password.RequireLowercase = false;
|
services.SetupOptions<IdentityOptions>(options =>
|
||||||
options.Password.RequireUppercase = false;
|
{
|
||||||
options.Password.RequireNonLetterOrDigit = false;
|
options.Password.RequireDigit = false;
|
||||||
});
|
options.Password.RequireLowercase = false;
|
||||||
|
options.Password.RequireUppercase = false;
|
||||||
|
options.Password.RequireNonLetterOrDigit = false;
|
||||||
|
options.SecurityStampValidationInterval = TimeSpan.Zero;
|
||||||
|
});
|
||||||
|
services.SetupOptions<GoogleAuthenticationOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ClientId = "514485782433-fr3ml6sq0imvhi8a7qir0nb46oumtgn9.apps.googleusercontent.com";
|
||||||
|
options.ClientSecret = "V2nDD9SkFbvLTqAUBWBBxYAL";
|
||||||
|
});
|
||||||
|
services.AddInstance(new GoogleAuthenticationOptions
|
||||||
|
{
|
||||||
|
ClientId = "514485782433-fr3ml6sq0imvhi8a7qir0nb46oumtgn9.apps.googleusercontent.com",
|
||||||
|
ClientSecret = "V2nDD9SkFbvLTqAUBWBBxYAL"
|
||||||
|
});
|
||||||
|
services.SetupOptions<FacebookAuthenticationOptions>(options =>
|
||||||
|
{
|
||||||
|
options.AppId = "901611409868059";
|
||||||
|
options.AppSecret = "4aa3c530297b1dcebc8860334b39668b";
|
||||||
|
});
|
||||||
|
|
||||||
|
services.SetupOptions<FacebookAuthenticationOptions>(options =>
|
||||||
|
{
|
||||||
|
options.AppId = "901611409868059";
|
||||||
|
options.AppSecret = "4aa3c530297b1dcebc8860334b39668b";
|
||||||
|
});
|
||||||
|
|
||||||
|
services.SetupOptions<TwitterAuthenticationOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ConsumerKey = "BSdJJ0CrDuvEhpkchnukXZBUv";
|
||||||
|
options.ConsumerSecret = "xKUNuKhsRdHD03eLn67xhPAyE1wFFEndFo1X2UJaK2m1jdAxf4";
|
||||||
|
});
|
||||||
|
|
||||||
// Add MVC services to the services container
|
// Add MVC services to the services container
|
||||||
services.AddMvc();
|
services.AddMvc();
|
||||||
|
|
@ -62,19 +157,13 @@ namespace IdentitySamples
|
||||||
// Add static files to the request pipeline
|
// Add static files to the request pipeline
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
|
// Setup identity cookie middleware
|
||||||
// Add cookie-based authentication to the request pipeline
|
// Add cookie-based authentication to the request pipeline
|
||||||
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
app.UseIdentity();
|
||||||
{
|
|
||||||
AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType,
|
|
||||||
LoginPath = new PathString("/Account/Login"),
|
|
||||||
Notifications = new CookieAuthenticationNotifications
|
|
||||||
{
|
|
||||||
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUser>(
|
|
||||||
validateInterval: TimeSpan.FromMinutes(0))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.UseTwoFactorSignInCookies();
|
app.UseGoogleAuthentication();
|
||||||
|
app.UseFacebookAuthentication();
|
||||||
|
app.UseTwitterAuthentication();
|
||||||
|
|
||||||
// Add MVC to the request pipeline
|
// Add MVC to the request pipeline
|
||||||
app.UseMvc(routes =>
|
app.UseMvc(routes =>
|
||||||
|
|
@ -85,8 +174,15 @@ namespace IdentitySamples
|
||||||
defaults: new { controller = "Home", action = "Index" });
|
defaults: new { controller = "Home", action = "Index" });
|
||||||
});
|
});
|
||||||
|
|
||||||
//Populates the MusicStore sample data
|
//Populates the Admin user and role
|
||||||
SampleData.InitializeIdentityDatabaseAsync(app.ApplicationServices).Wait();
|
SampleData.InitializeIdentityDatabaseAsync(app.ApplicationServices).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move services here
|
||||||
|
public IServiceProvider ConfigureServices(ServiceCollection services)
|
||||||
|
{
|
||||||
|
return services.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Confirm Email";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Thank you for confirming your email. Please @Html.ActionLink("Click here to Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "DEMO purpose Email Link";
|
||||||
|
}
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
<p class="text-info">
|
||||||
|
Please check your email and confirm your email address.
|
||||||
|
</p>
|
||||||
|
<p class="text-danger">
|
||||||
|
For DEMO only: You can click this link to confirm the email: <a href="@ViewBag.Link">link</a>
|
||||||
|
Please change this code to register an email service in IdentityConfig to send an email.
|
||||||
|
</p>
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
@model IdentitySample.Models.ExternalLoginConfirmationViewModel
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Register";
|
||||||
|
}
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
<h3>Associate your @ViewBag.LoginProvider account.</h3>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("ExternalLoginConfirmation", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
|
||||||
|
<h4>Association Form</h4>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
|
||||||
|
<p class="text-info">
|
||||||
|
You've successfully authenticated with <strong>@ViewBag.LoginProvider</strong>.
|
||||||
|
Please enter a user name for this site below and click the Register button to finish
|
||||||
|
logging in.
|
||||||
|
</p>
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
|
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Register" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Login Failure";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
<h3 class="text-error">Unsuccessful login with service.</h3>
|
||||||
|
</hgroup>
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
@model IdentitySample.Models.ForgotPasswordViewModel
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Forgot your password?";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<h4>Enter your email.</h4>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Email Link" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Forgot Password Confirmation";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup class="title">
|
||||||
|
<h1>@ViewBag.Title.</h1>
|
||||||
|
</hgroup>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Please check your email to reset your password.
|
||||||
|
</p>
|
||||||
|
<p class="text-danger">
|
||||||
|
For DEMO only: You can click this link to reset password: <a href="@ViewBag.Link">link</a>
|
||||||
|
Please change this code to register an email service in IdentityConfig to send an email.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
@model IdentitySample.Models.LoginViewModel
|
@model IdentitySample.Models.LoginViewModel
|
||||||
|
@using System.Collections.Generic
|
||||||
|
@using Microsoft.AspNet.Http
|
||||||
|
@using Microsoft.AspNet.Http.Security
|
||||||
|
|
||||||
@{
|
@{
|
||||||
//TODO: Until we have a way to specify the layout page at application level.
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
|
@ -44,8 +47,39 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
@Html.ActionLink("Register", "Register") if you don't have a local account.
|
@Html.ActionLink("Register a new user?", "Register")
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
@Html.ActionLink("Forget your password?", "ForgotPassword")
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<section id="socialLoginForm">
|
||||||
|
<h4>Use another service to log in.</h4>
|
||||||
|
<hr />
|
||||||
|
@{
|
||||||
|
if (ViewBag.LoginProviders.Count == 0) {
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
There are no external authentication services configured. See <a href="http://go.microsoft.com/fwlink/?LinkId=313242">this article</a>
|
||||||
|
for details on setting up this ASP.NET application to support logging in via external services.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = ViewBag.ReturnUrl })) {
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<div id="socialLoginList">
|
||||||
|
<p>
|
||||||
|
@foreach (AuthenticationDescription p in ViewBag.LoginProviders) {
|
||||||
|
<button type="submit" class="btn btn-default" id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType" title="Log in using your @p.Caption account">@p.AuthenticationType</button>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
@{
|
|
||||||
//TODO: Until we have a way to specify the layout page at application level.
|
|
||||||
Layout = "/Views/Shared/_Layout.cshtml";
|
|
||||||
ViewBag.Title = "Manage Account";
|
|
||||||
}
|
|
||||||
|
|
||||||
<h2>@ViewBag.Title.</h2>
|
|
||||||
<p class="text-success">@ViewBag.StatusMessage</p>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
@await Html.PartialAsync("_ChangePasswordPartial")
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@section Scripts {
|
|
||||||
@*TODO : Until script helpers are available, adding script references manually*@
|
|
||||||
@*@Scripts.Render("~/bundles/jqueryval")*@
|
|
||||||
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
|
||||||
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
|
||||||
}
|
|
||||||
|
|
@ -14,9 +14,9 @@
|
||||||
<hr />
|
<hr />
|
||||||
@Html.ValidationSummary()
|
@Html.ValidationSummary()
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
@model IdentitySample.Models.ResetPasswordViewModel
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Reset password";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<h4>Reset your password.</h4>
|
||||||
|
<hr />
|
||||||
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
|
@Html.HiddenFor(model => model.Code)
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Reset" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Reset password confirmation";
|
||||||
|
}
|
||||||
|
|
||||||
|
<hgroup class="title">
|
||||||
|
<h1>@ViewBag.Title.</h1>
|
||||||
|
</hgroup>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Your password has been reset. Please @Html.ActionLink("click here to log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
@model IdentitySample.Models.SendCodeViewModel
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Send Verification Code";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("SendCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) {
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
@Html.Hidden("rememberMe", @Model.RememberMe)
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
Two Factor Authentication Provider:
|
||||||
|
@Html.DropDownListFor(model => model.SelectedProvider, Model.Providers)
|
||||||
|
<input type="submit" value="Submit" class="btn btn-default" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
@model IdentitySample.Models.VerifyCodeViewModel
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Enter Verification Code";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>@ViewBag.Title.</h2>
|
||||||
|
|
||||||
|
@using (Html.BeginForm("VerifyCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) {
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
|
@Html.Hidden("provider", @Model.Provider)
|
||||||
|
@Html.Hidden("rememberMe", @Model.RememberMe)
|
||||||
|
<h4>@ViewBag.Status</h4>
|
||||||
|
<hr />
|
||||||
|
<div class="form-group">
|
||||||
|
@Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" })
|
||||||
|
<div class="col-md-10">
|
||||||
|
@Html.TextBoxFor(m => m.Code, new { @class = "form-control" })
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<div class="checkbox">
|
||||||
|
@Html.CheckBoxFor(m => m.RememberBrowser)
|
||||||
|
@Html.LabelFor(m => m.RememberBrowser)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-offset-2 col-md-10">
|
||||||
|
<input type="submit" class="btn btn-default" value="Submit" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
|
}
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
@using System.Security.Principal
|
|
||||||
@model IdentitySample.Models.ManageUserViewModel
|
|
||||||
|
|
||||||
<p>You're logged in as <strong>@User.Identity.GetUserName()</strong>.</p>
|
|
||||||
|
|
||||||
@using (Html.BeginForm("Manage", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
|
|
||||||
{
|
|
||||||
@Html.AntiForgeryToken()
|
|
||||||
<h4>Change Password Form</h4>
|
|
||||||
<hr />
|
|
||||||
@Html.ValidationSummary()
|
|
||||||
<div class="form-group">
|
|
||||||
@Html.LabelFor(m => m.OldPassword, new { @class = "col-md-2 control-label" })
|
|
||||||
<div class="col-md-10">
|
|
||||||
@Html.PasswordFor(m => m.OldPassword, new { @class = "form-control" })
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
@Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" })
|
|
||||||
<div class="col-md-10">
|
|
||||||
@Html.PasswordFor(m => m.NewPassword, new { @class = "form-control" })
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
|
|
||||||
<div class="col-md-10">
|
|
||||||
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-md-offset-2 col-md-10">
|
|
||||||
<input type="submit" value="Change password" class="btn btn-default" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
@ -10,14 +10,8 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<p>
|
<p>
|
||||||
@if (Model.HasPassword)
|
@(Model.HasPassword ? Html.ActionLink("Change your password", "ChangePassword")
|
||||||
{
|
: Html.ActionLink("Set your password", "SetPassword"))
|
||||||
@Html.ActionLink("Change your password", "ChangePassword")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@Html.ActionLink("Pick a password", "SetPassword")
|
|
||||||
}
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Phone Number: @(Model.PhoneNumber ?? "None") [
|
Phone Number: @(Model.PhoneNumber ?? "None") [
|
||||||
|
|
@ -39,39 +33,47 @@
|
||||||
</p>
|
</p>
|
||||||
@if (Model.TwoFactor)
|
@if (Model.TwoFactor)
|
||||||
{
|
{
|
||||||
<form method="post" action="/Manage/DisableTFA">
|
using (Html.BeginForm("DisableTFA", "Manage", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
|
||||||
<p>
|
{
|
||||||
Two factor is currently enabled:
|
@Html.AntiForgeryToken()
|
||||||
<input type="submit" value="Disable" class="btn btn-default"/>
|
<p>
|
||||||
</p>
|
Two factor is currently enabled:
|
||||||
</form>
|
<input type="submit" value="Disable" class="btn btn-default"/>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<form method="post" action="/Manage/EnableTFA">
|
using (Html.BeginForm("EnableTFA", "Manage", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
<p>
|
<p>
|
||||||
Two factor is currently disabled:
|
Two factor is currently disabled:
|
||||||
<input type="submit" value="Enable" class="btn btn-default"/>
|
<input type="submit" value="Enable" class="btn btn-default"/>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
}
|
||||||
}
|
}
|
||||||
@if (Model.BrowserRemembered)
|
@if (Model.BrowserRemembered)
|
||||||
{
|
{
|
||||||
<form method="post" action="/Manage/ForgetBrowser">
|
using (Html.BeginForm("ForgetBrowser", "Manage", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
<p>
|
<p>
|
||||||
Browser is curently remembered for two factor:
|
Browser is curently remembered for two factor:
|
||||||
<input type="submit" value="Forget Browser" class="btn btn-default" />
|
<input type="submit" value="Forget Browser" class="btn btn-default" />
|
||||||
</p>
|
</p>
|
||||||
</form>
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<form method="post" action="/Manage/RememberBrowser">
|
using (Html.BeginForm("RememberBrowser", "Manage", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
<p>
|
<p>
|
||||||
Browser is curently not remembered for two factor:
|
Browser is curently not remembered for two factor:
|
||||||
<input type="submit" value="Remember Browser" class="btn btn-default"/>
|
<input type="submit" value="Remember Browser" class="btn btn-default"/>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
@model IdentitySample.Models.ManageLoginsViewModel
|
@model IdentitySample.Models.ManageLoginsViewModel
|
||||||
@using Microsoft.Owin.Security
|
@using Microsoft.AspNet.Http.Security
|
||||||
@{
|
@{
|
||||||
//TODO: Until we have a way to specify the layout page at application level.
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
Layout = "/Views/Shared/_Layout.cshtml";
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
@model IdentitySample.Models.SetPasswordViewModel
|
@model IdentitySample.Models.SetPasswordViewModel
|
||||||
|
@{
|
||||||
|
//TODO: Until we have a way to specify the layout page at application level.
|
||||||
|
Layout = "/Views/Shared/_Layout.cshtml";
|
||||||
|
ViewBag.Title = "Set Password";
|
||||||
|
}
|
||||||
|
|
||||||
<p class="text-info">
|
<p class="text-info">
|
||||||
You do not have a local username/password for this site. Add a local
|
You do not have a local username/password for this site. Add a local
|
||||||
|
|
@ -9,7 +14,7 @@
|
||||||
{
|
{
|
||||||
@Html.AntiForgeryToken()
|
@Html.AntiForgeryToken()
|
||||||
|
|
||||||
<h4>Create Local Login</h4>
|
<h4>Set your password</h4>
|
||||||
<hr />
|
<hr />
|
||||||
@Html.ValidationSummary("", new { @class = "text-danger" })
|
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -31,5 +36,8 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
@Scripts.Render("~/bundles/jqueryval")
|
@*TODO : Until script helpers are available, adding script references manually*@
|
||||||
|
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||||
|
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||||
}
|
}
|
||||||
|
|
@ -5,21 +5,28 @@
|
||||||
"description": "Identity sample MVC application on K",
|
"description": "Identity sample MVC application on K",
|
||||||
"version": "1.0.0-*",
|
"version": "1.0.0-*",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Http": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.Core": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.ModelBinding": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.Razor": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Routing": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
|
"Microsoft.AspNet.Diagnostics": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Identity.SqlServer": "3.0.0-*",
|
"Microsoft.AspNet.Identity.SqlServer": "3.0.0-*",
|
||||||
"Microsoft.AspNet.Identity.Authentication": "3.0.0-*",
|
|
||||||
"Microsoft.AspNet.Security.Cookies": "1.0.0-*",
|
"Microsoft.AspNet.Security.Cookies": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security.Facebook": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security.Google": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security.Twitter": "1.0.0-*",
|
||||||
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
|
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
|
||||||
"EntityFramework.SqlServer": "7.0.0-*",
|
"EntityFramework.SqlServer": "7.0.0-*",
|
||||||
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
|
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
|
||||||
"Microsoft.Framework.OptionsModel": "1.0.0-*"
|
"Microsoft.Framework.OptionsModel": "1.0.0-*"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5002",
|
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:41532",
|
||||||
"run": "run server.urls=http://localhost:5003"
|
"run": "run server.urls=http://localhost:41532"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"aspnet50": {
|
"aspnet50": {
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.AspNet.Identity.Authentication;
|
|
||||||
using Microsoft.AspNet.Security.Cookies;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Builder
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Startup extensions
|
|
||||||
/// </summary>
|
|
||||||
public static class BuilderExtensions
|
|
||||||
{
|
|
||||||
public static IApplicationBuilder UseTwoFactorSignInCookies(this IApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
// TODO: expose some way for them to customize these cookie lifetimes?
|
|
||||||
builder.UseCookieAuthentication(new CookieAuthenticationOptions
|
|
||||||
{
|
|
||||||
AuthenticationType = HttpAuthenticationManager.TwoFactorRememberedAuthenticationType,
|
|
||||||
AuthenticationMode = Security.AuthenticationMode.Passive
|
|
||||||
});
|
|
||||||
builder.UseCookieAuthentication(new CookieAuthenticationOptions
|
|
||||||
{
|
|
||||||
AuthenticationType = HttpAuthenticationManager.TwoFactorUserIdAuthenticationType,
|
|
||||||
AuthenticationMode = Security.AuthenticationMode.Passive
|
|
||||||
});
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Http.Security;
|
|
||||||
using Microsoft.Framework.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.Authentication
|
|
||||||
{
|
|
||||||
public class HttpAuthenticationManager : IAuthenticationManager
|
|
||||||
{
|
|
||||||
public static readonly string TwoFactorUserIdAuthenticationType = "Microsoft.AspNet.Identity.TwoFactor.UserId";
|
|
||||||
public static readonly string TwoFactorRememberedAuthenticationType = "Microsoft.AspNet.Identity.TwoFactor.Remembered";
|
|
||||||
|
|
||||||
public HttpAuthenticationManager(IContextAccessor<HttpContext> contextAccessor)
|
|
||||||
{
|
|
||||||
Context = contextAccessor.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpContext Context { get; private set; }
|
|
||||||
|
|
||||||
public void ForgetClient()
|
|
||||||
{
|
|
||||||
Context.Response.SignOut(TwoFactorRememberedAuthenticationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsClientRememeberedAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
var result =
|
|
||||||
await Context.AuthenticateAsync(TwoFactorRememberedAuthenticationType);
|
|
||||||
return (result != null && result.Identity != null && result.Identity.Name == userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RememberClient(string userId)
|
|
||||||
{
|
|
||||||
var rememberBrowserIdentity = new ClaimsIdentity(TwoFactorRememberedAuthenticationType);
|
|
||||||
rememberBrowserIdentity.AddClaim(new Claim(ClaimTypes.Name, userId));
|
|
||||||
Context.Response.SignIn(rememberBrowserIdentity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> RetrieveUserId()
|
|
||||||
{
|
|
||||||
var result = await Context.AuthenticateAsync(TwoFactorUserIdAuthenticationType);
|
|
||||||
if (result != null && result.Identity != null)
|
|
||||||
{
|
|
||||||
return result.Identity.Name;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SignIn(ClaimsIdentity identity, bool isPersistent)
|
|
||||||
{
|
|
||||||
Context.Response.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
|
|
||||||
}
|
|
||||||
public void SignOut(string authenticationType)
|
|
||||||
{
|
|
||||||
Context.Response.SignOut(authenticationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StoreUserId(string userId)
|
|
||||||
{
|
|
||||||
var userIdentity = new ClaimsIdentity(TwoFactorUserIdAuthenticationType);
|
|
||||||
userIdentity.AddClaim(new Claim(ClaimTypes.Name, userId));
|
|
||||||
Context.Response.SignIn(userIdentity);
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using Microsoft.AspNet.Identity;
|
|
||||||
using Microsoft.AspNet.Identity.Authentication;
|
|
||||||
|
|
||||||
namespace Microsoft.Framework.DependencyInjection
|
|
||||||
{
|
|
||||||
public static class IdentityBuilderExtensions
|
|
||||||
{
|
|
||||||
public static IdentityBuilder<TUser, TRole> AddAuthentication<TUser, TRole>(this IdentityBuilder<TUser, TRole> builder)
|
|
||||||
where TUser : class
|
|
||||||
where TRole : class
|
|
||||||
{
|
|
||||||
builder.Services.AddScoped<IAuthenticationManager, HttpAuthenticationManager>();
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
|
|
||||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<ProjectGuid>7b4cff5a-1948-45ec-b170-6eb7c039b2f9</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNet.Security.Cookies;
|
|
||||||
using Microsoft.Framework.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.Authentication
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Static helper class used to configure a CookieAuthenticationProvider to validate a cookie against a user's security
|
|
||||||
/// stamp
|
|
||||||
/// </summary>
|
|
||||||
public static class SecurityStampValidator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Can be used as the ValidateIdentity method for a CookieAuthenticationProvider which will check a user's security
|
|
||||||
/// stamp after validateInterval
|
|
||||||
/// Rejects the identity if the stamp changes, and otherwise will call regenerateIdentity to sign in a new
|
|
||||||
/// ClaimsIdentity
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TUser"></typeparam>
|
|
||||||
/// <param name="validateInterval"></param>
|
|
||||||
/// <param name="regenerateIdentity"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Func<CookieValidateIdentityContext, Task> OnValidateIdentity<TUser>(
|
|
||||||
TimeSpan validateInterval)
|
|
||||||
where TUser : class
|
|
||||||
{
|
|
||||||
return OnValidateIdentity<TUser>(validateInterval, id => id.GetUserId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Can be used as the ValidateIdentity method for a CookieAuthenticationProvider which will check a user's security
|
|
||||||
/// stamp after validateInterval
|
|
||||||
/// Rejects the identity if the stamp changes, and otherwise will call regenerateIdentity to sign in a new
|
|
||||||
/// ClaimsIdentity
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TUser"></typeparam>
|
|
||||||
/// <typeparam name="TKey"></typeparam>
|
|
||||||
/// <param name="validateInterval"></param>
|
|
||||||
/// <param name="regenerateIdentityCallback"></param>
|
|
||||||
/// <param name="getUserIdCallback"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Func<CookieValidateIdentityContext, Task> OnValidateIdentity<TUser>(
|
|
||||||
TimeSpan validateInterval,
|
|
||||||
Func<ClaimsIdentity, string> getUserIdCallback)
|
|
||||||
where TUser : class
|
|
||||||
{
|
|
||||||
return async context =>
|
|
||||||
{
|
|
||||||
var currentUtc = DateTimeOffset.UtcNow;
|
|
||||||
if (context.Options != null && context.Options.SystemClock != null)
|
|
||||||
{
|
|
||||||
currentUtc = context.Options.SystemClock.UtcNow;
|
|
||||||
}
|
|
||||||
var issuedUtc = context.Properties.IssuedUtc;
|
|
||||||
|
|
||||||
// Only validate if enough time has elapsed
|
|
||||||
var validate = (issuedUtc == null);
|
|
||||||
if (issuedUtc != null)
|
|
||||||
{
|
|
||||||
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
|
|
||||||
validate = timeElapsed > validateInterval;
|
|
||||||
}
|
|
||||||
if (validate)
|
|
||||||
{
|
|
||||||
var manager = context.HttpContext.RequestServices.GetService<SignInManager<TUser>>();
|
|
||||||
var userId = getUserIdCallback(context.Identity);
|
|
||||||
var user = await manager.ValidateSecurityStampAsync(context.Identity, userId);
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
bool isPersistent = false;
|
|
||||||
if (context.Properties != null)
|
|
||||||
{
|
|
||||||
isPersistent = context.Properties.IsPersistent;
|
|
||||||
}
|
|
||||||
await manager.SignInAsync(user, isPersistent);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.RejectIdentity();
|
|
||||||
manager.SignOut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"version": "3.0.0-*",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.AspNet.Http" : "1.0.0-*",
|
|
||||||
"Microsoft.AspNet.Identity" : "",
|
|
||||||
"Microsoft.AspNet.Security.Cookies" : "1.0.0-*",
|
|
||||||
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
|
|
||||||
"System.Security.Claims" : "1.0.0-*"
|
|
||||||
},
|
|
||||||
"frameworks": {
|
|
||||||
"aspnet50": {},
|
|
||||||
"aspnetcore50": {
|
|
||||||
"dependencies": {
|
|
||||||
"System.Collections": "4.0.10.0",
|
|
||||||
"System.ComponentModel": "4.0.0.0",
|
|
||||||
"System.Diagnostics.Debug": "4.0.10.0",
|
|
||||||
"System.Diagnostics.Tools": "4.0.0.0",
|
|
||||||
"System.Globalization": "4.0.10.0",
|
|
||||||
"System.Linq": "4.0.0.0",
|
|
||||||
"System.Linq.Expressions": "4.0.0.0",
|
|
||||||
"System.Reflection": "4.0.10.0",
|
|
||||||
"System.Resources.ResourceManager": "4.0.0.0",
|
|
||||||
"System.Runtime": "4.0.20.0",
|
|
||||||
"System.Runtime.Extensions": "4.0.10.0",
|
|
||||||
"System.Security.Principal": "4.0.0.0",
|
|
||||||
"System.Text.Encoding": "4.0.10.0",
|
|
||||||
"System.Threading.Tasks": "4.0.10.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -11,8 +11,6 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
IdentityDbContext<IdentityUser, IdentityRole, string>
|
IdentityDbContext<IdentityUser, IdentityRole, string>
|
||||||
{
|
{
|
||||||
public IdentityDbContext() { }
|
public IdentityDbContext() { }
|
||||||
public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
|
|
||||||
public IdentityDbContext(DbContextOptions options) : base(options) { }
|
|
||||||
public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
|
public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,8 +19,6 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
where TUser : IdentityUser
|
where TUser : IdentityUser
|
||||||
{
|
{
|
||||||
public IdentityDbContext() { }
|
public IdentityDbContext() { }
|
||||||
public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
|
|
||||||
public IdentityDbContext(DbContextOptions options) : base(options) { }
|
|
||||||
public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
|
public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,8 +35,6 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
public DbSet<IdentityRoleClaim<TKey>> RoleClaims { get; set; }
|
public DbSet<IdentityRoleClaim<TKey>> RoleClaims { get; set; }
|
||||||
|
|
||||||
public IdentityDbContext() { }
|
public IdentityDbContext() { }
|
||||||
public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
|
|
||||||
public IdentityDbContext(DbContextOptions options) : base(options) { }
|
|
||||||
public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
|
public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
|
|
@ -48,26 +42,26 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
builder.Entity<TUser>(b =>
|
builder.Entity<TUser>(b =>
|
||||||
{
|
{
|
||||||
b.Key(u => u.Id);
|
b.Key(u => u.Id);
|
||||||
b.Property(u => u.UserName);
|
|
||||||
b.ToTable("AspNetUsers");
|
b.ToTable("AspNetUsers");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<TRole>(b =>
|
builder.Entity<TRole>(b =>
|
||||||
{
|
{
|
||||||
b.Key(r => r.Id);
|
b.Key(r => r.Id);
|
||||||
b.Property(r => r.Name);
|
|
||||||
b.ToTable("AspNetRoles");
|
b.ToTable("AspNetRoles");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<IdentityUserClaim<TKey>>(b =>
|
builder.Entity<IdentityUserClaim<TKey>>(b =>
|
||||||
{
|
{
|
||||||
b.Key(uc => uc.Id);
|
b.Key(uc => uc.Id);
|
||||||
|
b.ManyToOne<TUser>().ForeignKey(uc => uc.UserId);
|
||||||
b.ToTable("AspNetUserClaims");
|
b.ToTable("AspNetUserClaims");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<IdentityRoleClaim<TKey>>(b =>
|
builder.Entity<IdentityRoleClaim<TKey>>(b =>
|
||||||
{
|
{
|
||||||
b.Key(uc => uc.Id);
|
b.Key(rc => rc.Id);
|
||||||
|
b.ManyToOne<TRole>().ForeignKey(rc => rc.RoleId);
|
||||||
b.ToTable("AspNetRoleClaims");
|
b.ToTable("AspNetRoleClaims");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -76,8 +70,8 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
var userClaimType = builder.Model.GetEntityType(typeof(IdentityUserClaim<TKey>));
|
var userClaimType = builder.Model.GetEntityType(typeof(IdentityUserClaim<TKey>));
|
||||||
var roleClaimType = builder.Model.GetEntityType(typeof(IdentityRoleClaim<TKey>));
|
var roleClaimType = builder.Model.GetEntityType(typeof(IdentityRoleClaim<TKey>));
|
||||||
var userRoleType = builder.Model.GetEntityType(typeof(IdentityUserRole<TKey>));
|
var userRoleType = builder.Model.GetEntityType(typeof(IdentityUserRole<TKey>));
|
||||||
var ucfk = userClaimType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userClaimType.GetProperty("UserId") });
|
//var ucfk = userClaimType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userClaimType.GetProperty("UserId") });
|
||||||
userType.AddNavigation(new Navigation(ucfk, "Claims", false));
|
//userType.AddNavigation(new Navigation(ucfk, "Claims", false));
|
||||||
//userClaimType.AddNavigation(new Navigation(ucfk, "User", true));
|
//userClaimType.AddNavigation(new Navigation(ucfk, "User", true));
|
||||||
//var urfk = userRoleType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userRoleType.GetProperty("UserId") });
|
//var urfk = userRoleType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userRoleType.GetProperty("UserId") });
|
||||||
//userType.AddNavigation(new Navigation(urfk, "Roles", false));
|
//userType.AddNavigation(new Navigation(urfk, "Roles", false));
|
||||||
|
|
@ -99,10 +93,10 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
|
|
||||||
builder.Entity<IdentityUserLogin<TKey>>(b =>
|
builder.Entity<IdentityUserLogin<TKey>>(b =>
|
||||||
{
|
{
|
||||||
b.Key(l => new { l.LoginProvider, l.ProviderKey, l.UserId });
|
b.Key(l => new { l.LoginProvider, l.ProviderKey });
|
||||||
|
b.ManyToOne<TUser>().ForeignKey(uc => uc.UserId);
|
||||||
b.ToTable("AspNetUserLogins");
|
b.ToTable("AspNetUserLogins");
|
||||||
});
|
});
|
||||||
//.ForeignKeys(fk => fk.ForeignKey<TUser>(f => f.UserId))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Identity.SqlServer;
|
||||||
|
using Microsoft.Data.Entity;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.Framework.DependencyInjection
|
||||||
|
{
|
||||||
|
public static class IdentityEntityFrameworkBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IdentityBuilder<IdentityUser, IdentityRole> AddEntityFramework(this IdentityBuilder<IdentityUser, IdentityRole> builder)
|
||||||
|
{
|
||||||
|
return builder.AddEntityFramework<IdentityDbContext, IdentityUser, IdentityRole>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityBuilder<IdentityUser, IdentityRole> AddEntityFramework<TContext>(this IdentityBuilder<IdentityUser, IdentityRole> builder)
|
||||||
|
where TContext : DbContext
|
||||||
|
{
|
||||||
|
return builder.AddEntityFramework<TContext, IdentityUser, IdentityRole>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityBuilder<TUser, IdentityRole> AddEntityFramework<TContext, TUser>(this IdentityBuilder<TUser, IdentityRole> builder)
|
||||||
|
where TUser : IdentityUser, new()
|
||||||
|
where TContext : DbContext
|
||||||
|
{
|
||||||
|
return builder.AddEntityFramework<TContext, TUser, IdentityRole>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityBuilder<TUser, TRole> AddEntityFramework<TContext, TUser, TRole>(this IdentityBuilder<TUser, TRole> builder)
|
||||||
|
where TUser : IdentityUser, new()
|
||||||
|
where TRole : IdentityRole, new()
|
||||||
|
where TContext : DbContext
|
||||||
|
{
|
||||||
|
builder.Services.AddScoped<IUserStore<TUser>, UserStore<TUser, TRole, TContext>>();
|
||||||
|
builder.Services.AddScoped<IRoleStore<TRole>, RoleStore<TRole, TContext>>();
|
||||||
|
builder.Services.AddScoped<TContext>();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityBuilder<TUser, TRole> AddEntityFramework<TContext, TUser, TRole, TKey>(this IdentityBuilder<TUser, TRole> builder)
|
||||||
|
where TUser : IdentityUser<TKey>, new()
|
||||||
|
where TRole : IdentityRole<TKey>, new()
|
||||||
|
where TContext : DbContext
|
||||||
|
where TKey : IEquatable<TKey>
|
||||||
|
{
|
||||||
|
builder.Services.AddScoped<IUserStore<TUser>, UserStore<TUser, TRole, TContext, TKey>>();
|
||||||
|
builder.Services.AddScoped<IRoleStore<TRole>, RoleStore<TRole, TContext, TKey>>();
|
||||||
|
builder.Services.AddScoped<TContext>();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,31 +4,54 @@
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Identity;
|
||||||
using Microsoft.AspNet.Identity.SqlServer;
|
using Microsoft.AspNet.Identity.SqlServer;
|
||||||
using Microsoft.Data.Entity;
|
using Microsoft.Data.Entity;
|
||||||
|
using Microsoft.Framework.ConfigurationModel;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.Framework.DependencyInjection
|
namespace Microsoft.Framework.DependencyInjection
|
||||||
{
|
{
|
||||||
public static class IdentityEntityFrameworkServiceCollectionExtensions
|
public static class IdentityEntityFrameworkServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentitySqlServer(this ServiceCollection services)
|
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentitySqlServer(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
return services.AddIdentitySqlServer<IdentityDbContext, IdentityUser, IdentityRole>();
|
return services.AddIdentitySqlServer<IdentityDbContext, IdentityUser, IdentityRole>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentitySqlServer<TContext>(this ServiceCollection services)
|
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentitySqlServer<TContext>(this IServiceCollection services)
|
||||||
where TContext : DbContext
|
where TContext : DbContext
|
||||||
{
|
{
|
||||||
return services.AddIdentitySqlServer<TContext, IdentityUser, IdentityRole>();
|
return services.AddIdentitySqlServer<TContext, IdentityUser, IdentityRole>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder<TUser, IdentityRole> AddIdentitySqlServer<TContext, TUser>(this ServiceCollection services)
|
public static IdentityBuilder<TUser, TRole> AddDefaultIdentity<TContext, TUser, TRole>(this IServiceCollection services, IConfiguration config)
|
||||||
|
where TUser : IdentityUser, new()
|
||||||
|
where TRole : IdentityRole, new()
|
||||||
|
where TContext : DbContext
|
||||||
|
{
|
||||||
|
return services.AddDefaultIdentity<TUser, TRole>(config)
|
||||||
|
.AddEntityFramework<TContext, TUser, TRole>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IdentityBuilder<TUser, IdentityRole> AddIdentitySqlServer<TContext, TUser>(this IServiceCollection services)
|
||||||
where TUser : IdentityUser, new()
|
where TUser : IdentityUser, new()
|
||||||
where TContext : DbContext
|
where TContext : DbContext
|
||||||
{
|
{
|
||||||
return services.AddIdentitySqlServer<TContext, TUser, IdentityRole>();
|
return services.AddIdentitySqlServer<TContext, TUser, IdentityRole>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder<TUser, TRole> AddIdentitySqlServer<TContext, TUser, TRole>(this ServiceCollection services)
|
public static IdentityBuilder<TUser, TRole> AddSqlServer<TContext, TUser, TRole>(this IServiceCollection services)
|
||||||
|
where TUser : IdentityUser, new()
|
||||||
|
where TRole : IdentityRole, new()
|
||||||
|
where TContext : DbContext
|
||||||
|
{
|
||||||
|
var builder = services.AddIdentity<TUser, TRole>();
|
||||||
|
services.AddScoped<IUserStore<TUser>, UserStore<TUser, TRole, TContext>>();
|
||||||
|
services.AddScoped<IRoleStore<TRole>, RoleStore<TRole, TContext>>();
|
||||||
|
services.AddScoped<TContext>();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityBuilder<TUser, TRole> AddIdentitySqlServer<TContext, TUser, TRole>(this IServiceCollection services)
|
||||||
where TUser : IdentityUser, new()
|
where TUser : IdentityUser, new()
|
||||||
where TRole : IdentityRole, new()
|
where TRole : IdentityRole, new()
|
||||||
where TContext : DbContext
|
where TContext : DbContext
|
||||||
|
|
|
||||||
|
|
@ -12,43 +12,39 @@ using Microsoft.Data.Entity;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.SqlServer
|
namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
{
|
{
|
||||||
public class RoleStore<TRole> : RoleStore<TRole, DbContext, string> where TRole : IdentityRole
|
public class RoleStore<TRole>(DbContext context) : RoleStore<TRole, DbContext, string>(context)
|
||||||
{
|
where TRole : IdentityRole
|
||||||
public RoleStore(DbContext context) : base(context) { }
|
{ }
|
||||||
}
|
|
||||||
|
|
||||||
public class RoleStore<TRole, TContext> : RoleStore<TRole, TContext, string>
|
public class RoleStore<TRole, TContext>(TContext context) : RoleStore<TRole, TContext, string>(context)
|
||||||
where TRole : IdentityRole
|
where TRole : IdentityRole
|
||||||
where TContext : DbContext
|
where TContext : DbContext
|
||||||
{
|
{ }
|
||||||
public RoleStore(TContext context) : base(context) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RoleStore<TRole, TContext, TKey> :
|
public class RoleStore<TRole, TContext, TKey>(TContext context) :
|
||||||
IQueryableRoleStore<TRole>,
|
IQueryableRoleStore<TRole>,
|
||||||
IRoleClaimStore<TRole>
|
IRoleClaimStore<TRole>
|
||||||
where TRole : IdentityRole<TKey>
|
where TRole : IdentityRole<TKey>
|
||||||
where TKey : IEquatable<TKey>
|
where TKey : IEquatable<TKey>
|
||||||
where TContext : DbContext
|
where TContext : DbContext
|
||||||
{
|
{
|
||||||
private bool _disposed;
|
// Primary constructor
|
||||||
|
|
||||||
public RoleStore(TContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("context");
|
throw new ArgumentNullException("context");
|
||||||
}
|
}
|
||||||
Context = context;
|
|
||||||
AutoSaveChanges = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TContext Context { get; private set; }
|
private bool _disposed;
|
||||||
|
|
||||||
|
|
||||||
|
public TContext Context { get; } = context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
|
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutoSaveChanges { get; set; }
|
public bool AutoSaveChanges { get; set; } = true;
|
||||||
|
|
||||||
private async Task SaveChanges(CancellationToken cancellationToken)
|
private async Task SaveChanges(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,25 +13,20 @@ using Microsoft.Data.Entity;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.SqlServer
|
namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
{
|
{
|
||||||
public class UserStore : UserStore<IdentityUser>
|
public class UserStore(DbContext context) : UserStore<IdentityUser>(context)
|
||||||
{
|
{ }
|
||||||
public UserStore(DbContext context) : base(context) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UserStore<TUser> : UserStore<TUser, IdentityRole, DbContext> where TUser : IdentityUser, new()
|
public class UserStore<TUser>(DbContext context) : UserStore<TUser, IdentityRole, DbContext>(context)
|
||||||
{
|
where TUser : IdentityUser, new()
|
||||||
public UserStore(DbContext context) : base(context) { }
|
{ }
|
||||||
}
|
|
||||||
|
|
||||||
public class UserStore<TUser, TRole, TContext> : UserStore<TUser, TRole, TContext, string>
|
public class UserStore<TUser, TRole, TContext>(TContext context) : UserStore<TUser, TRole, TContext, string>(context)
|
||||||
where TUser : IdentityUser, new()
|
where TUser : IdentityUser, new()
|
||||||
where TRole : IdentityRole, new()
|
where TRole : IdentityRole, new()
|
||||||
where TContext : DbContext
|
where TContext : DbContext
|
||||||
{
|
{ }
|
||||||
public UserStore(TContext context) : base(context) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UserStore<TUser, TRole, TContext, TKey> :
|
public class UserStore<TUser, TRole, TContext, TKey>(TContext context) :
|
||||||
IUserLoginStore<TUser>,
|
IUserLoginStore<TUser>,
|
||||||
IUserRoleStore<TUser>,
|
IUserRoleStore<TUser>,
|
||||||
IUserClaimStore<TUser>,
|
IUserClaimStore<TUser>,
|
||||||
|
|
@ -47,24 +42,22 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
where TContext : DbContext
|
where TContext : DbContext
|
||||||
where TKey : IEquatable<TKey>
|
where TKey : IEquatable<TKey>
|
||||||
{
|
{
|
||||||
private bool _disposed;
|
// Primary constructor
|
||||||
|
|
||||||
public UserStore(TContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("context");
|
throw new ArgumentNullException("context");
|
||||||
}
|
}
|
||||||
Context = context;
|
|
||||||
AutoSaveChanges = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TContext Context { get; private set; }
|
private bool _disposed;
|
||||||
|
|
||||||
|
public TContext Context { get; } = context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
|
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutoSaveChanges { get; set; }
|
public bool AutoSaveChanges { get; set; } = true;
|
||||||
|
|
||||||
private Task SaveChanges(CancellationToken cancellationToken)
|
private Task SaveChanges(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|
@ -507,7 +500,7 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
public virtual async Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
@ -516,9 +509,11 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
// todo: ensure logins loaded
|
// todo: ensure logins loaded
|
||||||
IList<UserLoginInfo> result = user.Logins
|
//IList<UserLoginInfo> result = user.Logins
|
||||||
.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToList();
|
// .Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToList();
|
||||||
return Task.FromResult(result);
|
var userId = user.Id;
|
||||||
|
return await UserLogins.Where(l => l.UserId.Equals(userId))
|
||||||
|
.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async virtual Task<TUser> FindByLoginAsync(string loginProvider, string providerKey,
|
public async virtual Task<TUser> FindByLoginAsync(string loginProvider, string providerKey,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Builder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Startup extensions
|
||||||
|
/// </summary>
|
||||||
|
public static class BuilderExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseIdentity(this IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
if (app == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("app");
|
||||||
|
}
|
||||||
|
var options = app.ApplicationServices.GetService<IOptionsAccessor<IdentityOptions>>().Options;
|
||||||
|
app.UseCookieAuthentication(options.ApplicationCookie);
|
||||||
|
app.SetDefaultSignInAsAuthenticationType(options.DefaultSignInAsAuthenticationType);
|
||||||
|
app.UseCookieAuthentication(options.ExternalCookie);
|
||||||
|
app.UseCookieAuthentication(options.TwoFactorRememberMeCookie);
|
||||||
|
app.UseCookieAuthentication(options.TwoFactorUserIdCookie);
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,41 +7,32 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
public class ClaimsIdentityOptions
|
public class ClaimsIdentityOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// ClaimType used for the security stamp by default
|
|
||||||
/// </summary>
|
|
||||||
public static readonly string DefaultSecurityStampClaimType = "AspNet.Identity.SecurityStamp";
|
public static readonly string DefaultSecurityStampClaimType = "AspNet.Identity.SecurityStamp";
|
||||||
public static readonly string DefaultAuthenticationType = typeof(ClaimsIdentityOptions).Namespace + ".Application";
|
public static readonly string DefaultAuthenticationType = typeof(ClaimsIdentityOptions).Namespace + ".Application";
|
||||||
|
public static readonly string DefaultExternalLoginAuthenticationType = typeof(ClaimsIdentityOptions).Namespace + ".ExternalLogin";
|
||||||
|
public static readonly string DefaultTwoFactorRememberMeAuthenticationType = typeof(ClaimsIdentityOptions).Namespace + ".TwoFactorRememberMe";
|
||||||
|
public static readonly string DefaultTwoFactorUserIdAuthenticationType = typeof(ClaimsIdentityOptions).Namespace + ".TwoFactorUserId";
|
||||||
|
|
||||||
public ClaimsIdentityOptions()
|
public string AuthenticationType { get; set; } = DefaultAuthenticationType;
|
||||||
{
|
|
||||||
AuthenticationType = DefaultAuthenticationType;
|
|
||||||
RoleClaimType = ClaimTypes.Role;
|
|
||||||
SecurityStampClaimType = DefaultSecurityStampClaimType;
|
|
||||||
UserIdClaimType = ClaimTypes.NameIdentifier;
|
|
||||||
UserNameClaimType = ClaimTypes.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string AuthenticationType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Claim type used for role claims
|
/// Claim type used for role claims
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string RoleClaimType { get; set; }
|
public string RoleClaimType { get; set; } = ClaimTypes.Role;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Claim type used for the user name
|
/// Claim type used for the user name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string UserNameClaimType { get; set; }
|
public string UserNameClaimType { get; set; } = ClaimTypes.Name;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Claim type used for the user id
|
/// Claim type used for the user id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string UserIdClaimType { get; set; }
|
public string UserIdClaimType { get; set; } = ClaimTypes.NameIdentifier;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Claim type used for the user security stamp
|
/// Claim type used for the user security stamp
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SecurityStampClaimType { get; set; }
|
public string SecurityStampClaimType { get; set; } = DefaultSecurityStampClaimType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
using Microsoft.AspNet.Security.DataProtection;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Identity
|
||||||
|
{
|
||||||
|
public class DataProtectionTokenProviderOptions
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "DataProtection";
|
||||||
|
public TimeSpan TokenLifespan { get; set; } = TimeSpan.FromDays(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Token provider that uses an IDataProtector to generate encrypted tokens based off of the security stamp
|
||||||
|
/// </summary>
|
||||||
|
public class DataProtectorTokenProvider<TUser>(DataProtectionTokenProviderOptions options, IDataProtector protector) : IUserTokenProvider<TUser> where TUser : class
|
||||||
|
{
|
||||||
|
{
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
if (protector == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(protector));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataProtectionTokenProviderOptions Options { get; } = options;
|
||||||
|
public IDataProtector Protector { get; } = protector;
|
||||||
|
|
||||||
|
public string Name { get { return Options.Name; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a protected string for a user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="purpose"></param>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("user");
|
||||||
|
}
|
||||||
|
var ms = new MemoryStream();
|
||||||
|
var userId = await manager.GetUserIdAsync(user, cancellationToken);
|
||||||
|
using (var writer = ms.CreateWriter())
|
||||||
|
{
|
||||||
|
writer.Write(DateTimeOffset.UtcNow);
|
||||||
|
writer.Write(userId);
|
||||||
|
writer.Write(purpose ?? "");
|
||||||
|
string stamp = null;
|
||||||
|
if (manager.SupportsUserSecurityStamp)
|
||||||
|
{
|
||||||
|
stamp = await manager.GetSecurityStampAsync(user);
|
||||||
|
}
|
||||||
|
writer.Write(stamp ?? "");
|
||||||
|
}
|
||||||
|
var protectedBytes = Protector.Protect(ms.ToArray());
|
||||||
|
return Convert.ToBase64String(protectedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return false if the token is not valid
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="purpose"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var unprotectedData = Protector.Unprotect(Convert.FromBase64String(token));
|
||||||
|
var ms = new MemoryStream(unprotectedData);
|
||||||
|
using (var reader = ms.CreateReader())
|
||||||
|
{
|
||||||
|
var creationTime = reader.ReadDateTimeOffset();
|
||||||
|
var expirationTime = creationTime + Options.TokenLifespan;
|
||||||
|
if (expirationTime < DateTimeOffset.UtcNow)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userId = reader.ReadString();
|
||||||
|
var actualUserId = await manager.GetUserIdAsync(user, cancellationToken);
|
||||||
|
if (userId != actualUserId)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var purp = reader.ReadString();
|
||||||
|
if (!string.Equals(purp, purpose))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var stamp = reader.ReadString();
|
||||||
|
if (reader.PeekChar() != -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manager.SupportsUserSecurityStamp)
|
||||||
|
{
|
||||||
|
return stamp == await manager.GetSecurityStampAsync(user);
|
||||||
|
}
|
||||||
|
return stamp == "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ReSharper disable once EmptyGeneralCatchClause
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Do not leak exception
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns false because tokens are two long to be used for two factor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This provider no-ops by default when asked to notify a user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task NotifyAsync(string token, UserManager<TUser> manager, TUser user,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on Levi's authentication sample
|
||||||
|
internal static class StreamExtensions
|
||||||
|
{
|
||||||
|
internal static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true);
|
||||||
|
|
||||||
|
public static BinaryReader CreateReader(this Stream stream)
|
||||||
|
{
|
||||||
|
return new BinaryReader(stream, DefaultEncoding, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BinaryWriter CreateWriter(this Stream stream)
|
||||||
|
{
|
||||||
|
return new BinaryWriter(stream, DefaultEncoding, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTimeOffset ReadDateTimeOffset(this BinaryReader reader)
|
||||||
|
{
|
||||||
|
return new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(this BinaryWriter writer, DateTimeOffset value)
|
||||||
|
{
|
||||||
|
writer.Write(value.UtcTicks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,33 +5,30 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity
|
namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
/// <summary>
|
public class EmailTokenProviderOptions
|
||||||
/// TokenProvider that generates tokens from the user's security stamp and notifies a user via their email
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TUser"></typeparam>
|
|
||||||
public class EmailTokenProvider<TUser> : TotpSecurityStampBasedTokenProvider<TUser>
|
|
||||||
where TUser : class
|
|
||||||
{
|
{
|
||||||
private string _body;
|
public string Name { get; set; } = Resources.DefaultEmailTokenProviderName;
|
||||||
private string _subject;
|
|
||||||
|
|
||||||
/// <summary>
|
public string Subject { get; set; } = "Security Code";
|
||||||
/// Email subject used when a token notification is received
|
|
||||||
/// </summary>
|
|
||||||
public string Subject
|
|
||||||
{
|
|
||||||
get { return _subject ?? string.Empty; }
|
|
||||||
set { _subject = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Format string which will be used for the email body, it will be passed the token for the first parameter
|
/// Format string which will be used for the email body, it will be passed the token for the first parameter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string BodyFormat
|
public string BodyFormat { get; set; } = "Your security code is: {0}";
|
||||||
{
|
}
|
||||||
get { return _body ?? "{0}"; }
|
|
||||||
set { _body = value; }
|
/// <summary>
|
||||||
}
|
/// TokenProvider that generates tokens from the user's security stamp and notifies a user via their email
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TUser"></typeparam>
|
||||||
|
public class EmailTokenProvider<TUser>(EmailTokenProviderOptions options) : TotpSecurityStampBasedTokenProvider<TUser>
|
||||||
|
where TUser : class
|
||||||
|
{
|
||||||
|
public EmailTokenProvider() : this(new EmailTokenProviderOptions()) { }
|
||||||
|
|
||||||
|
public EmailTokenProviderOptions Options { get; } = options;
|
||||||
|
|
||||||
|
public override string Name { get { return Options.Name; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the user has an email set
|
/// True if the user has an email set
|
||||||
|
|
@ -39,7 +36,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="manager"></param>
|
/// <param name="manager"></param>
|
||||||
/// <param name="user"></param>
|
/// <param name="user"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<bool> IsValidProviderForUserAsync(UserManager<TUser> manager, TUser user,
|
public override async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var email = await manager.GetEmailAsync(user, cancellationToken);
|
var email = await manager.GetEmailAsync(user, cancellationToken);
|
||||||
|
|
@ -74,7 +71,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("manager");
|
throw new ArgumentNullException("manager");
|
||||||
}
|
}
|
||||||
return manager.SendEmailAsync(user, Subject, String.Format(CultureInfo.CurrentCulture, BodyFormat, token), cancellationToken);
|
return manager.SendEmailAsync(user, Options.Subject, String.Format(CultureInfo.CurrentCulture, Options.BodyFormat, token), cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Identity
|
||||||
|
{
|
||||||
|
public class ExternalLoginInfo(ClaimsIdentity externalIdentity, string loginProvider, string providerKey,
|
||||||
|
string displayName) : UserLoginInfo(loginProvider, providerKey, displayName)
|
||||||
|
{
|
||||||
|
public ClaimsIdentity ExternalIdentity { get; set; } = externalIdentity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using System.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Http
|
||||||
|
{
|
||||||
|
public static class HttpContextExtensions
|
||||||
|
{
|
||||||
|
private const string LoginProviderKey = "LoginProvider";
|
||||||
|
private const string XsrfKey = "XsrfId";
|
||||||
|
|
||||||
|
public static IEnumerable<AuthenticationDescription> GetExternalAuthenticationTypes(this HttpContext context)
|
||||||
|
{
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(context));
|
||||||
|
}
|
||||||
|
return context.GetAuthenticationTypes().Where(d => !string.IsNullOrEmpty(d.Caption));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<ExternalLoginInfo> GetExternalLoginInfo(this HttpContext context, string expectedXsrf = null)
|
||||||
|
{
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(context));
|
||||||
|
}
|
||||||
|
// REVIEW: should we consider taking the external authentication type as an argument?
|
||||||
|
var auth = await context.AuthenticateAsync(ClaimsIdentityOptions.DefaultExternalLoginAuthenticationType);
|
||||||
|
if (auth == null || auth.Identity == null || auth.Properties.Dictionary == null || !auth.Properties.Dictionary.ContainsKey(LoginProviderKey))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedXsrf != null)
|
||||||
|
{
|
||||||
|
if (!auth.Properties.Dictionary.ContainsKey(XsrfKey))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var userId = auth.Properties.Dictionary[XsrfKey] as string;
|
||||||
|
if (userId != expectedXsrf)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var providerKey = auth.Identity.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var provider = auth.Properties.Dictionary[LoginProviderKey] as string;
|
||||||
|
if (providerKey == null || provider == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ExternalLoginInfo(auth.Identity, provider, providerKey, auth.Description.Caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthenticationProperties ConfigureExternalAuthenticationProperties(this HttpContext context, string provider, string redirectUrl, string userId = null)
|
||||||
|
{
|
||||||
|
var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
|
||||||
|
properties.Dictionary[LoginProviderKey] = provider;
|
||||||
|
if (userId != null)
|
||||||
|
{
|
||||||
|
properties.Dictionary[XsrfKey] = userId;
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity
|
|
||||||
{
|
|
||||||
public interface IAuthenticationManager
|
|
||||||
{
|
|
||||||
void SignIn(ClaimsIdentity identity, bool isPersistent);
|
|
||||||
void SignOut(string authenticationType);
|
|
||||||
|
|
||||||
// remember browser for two factor
|
|
||||||
void ForgetClient();
|
|
||||||
void RememberClient(string userId);
|
|
||||||
Task<bool> IsClientRememeberedAsync(string userId,
|
|
||||||
CancellationToken cancellationToken = default(CancellationToken));
|
|
||||||
|
|
||||||
// half cookie
|
|
||||||
Task StoreUserId(string userId);
|
|
||||||
Task<string> RetrieveUserId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,10 +12,10 @@ namespace Microsoft.AspNet.Identity
|
||||||
public interface IPasswordValidator<TUser> where TUser : class
|
public interface IPasswordValidator<TUser> where TUser : class
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validate the item
|
/// Validate the password for the user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IdentityResult> ValidateAsync(string password, UserManager<TUser> manager,
|
Task<IdentityResult> ValidateAsync(TUser user, string password, UserManager<TUser> manager,
|
||||||
CancellationToken cancellationToken = default(CancellationToken));
|
CancellationToken cancellationToken = default(CancellationToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Identity
|
||||||
|
{
|
||||||
|
public interface ISecurityStampValidator
|
||||||
|
{
|
||||||
|
Task Validate(CookieValidateIdentityContext context, ClaimsIdentity identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -12,6 +11,11 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IUserTokenProvider<TUser> where TUser : class
|
public interface IUserTokenProvider<TUser> where TUser : class
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the token provider
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate a token for a user
|
/// Generate a token for a user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -47,13 +51,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
CancellationToken cancellationToken = default(CancellationToken));
|
CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if provider can be used for this user, i.e. could require a user to have an email
|
/// Returns true if provider can be used for this user to generate two factor tokens, i.e. could require a user to have an email
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="manager"></param>
|
/// <param name="manager"></param>
|
||||||
/// <param name="user"></param>
|
/// <param name="user"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<bool> IsValidProviderForUserAsync(UserManager<TUser> manager, TUser user,
|
Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user,
|
||||||
CancellationToken cancellationToken = default(CancellationToken));
|
CancellationToken cancellationToken = default(CancellationToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,39 +9,44 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
public class IdentityBuilder<TUser, TRole> where TUser : class where TRole : class
|
public class IdentityBuilder<TUser, TRole> where TUser : class where TRole : class
|
||||||
{
|
{
|
||||||
public ServiceCollection Services { get; private set; }
|
public IServiceCollection Services { get; private set; }
|
||||||
|
|
||||||
public IdentityBuilder(ServiceCollection services)
|
public IdentityBuilder(IServiceCollection services)
|
||||||
{
|
{
|
||||||
Services = services;
|
Services = services;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename to Add
|
// Rename to Add
|
||||||
|
|
||||||
public IdentityBuilder<TUser, TRole> AddInstance<T>(Func<T> func)
|
public IdentityBuilder<TUser, TRole> AddInstance<T>(T obj)
|
||||||
{
|
{
|
||||||
Services.AddInstance(func());
|
Services.AddInstance(obj);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityBuilder<TUser, TRole> AddUserStore(Func<IUserStore<TUser>> func)
|
public IdentityBuilder<TUser, TRole> AddUserStore(IUserStore<TUser> store)
|
||||||
{
|
{
|
||||||
return AddInstance(func);
|
return AddInstance(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityBuilder<TUser, TRole> AddRoleStore(Func<IRoleStore<TRole>> func)
|
public IdentityBuilder<TUser, TRole> AddRoleStore(IRoleStore<TRole> store)
|
||||||
{
|
{
|
||||||
return AddInstance(func);
|
return AddInstance(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityBuilder<TUser, TRole> AddPasswordValidator(Func<IPasswordValidator<TUser>> func)
|
public IdentityBuilder<TUser, TRole> AddPasswordValidator(IPasswordValidator<TUser> validator)
|
||||||
{
|
{
|
||||||
return AddInstance(func);
|
return AddInstance(validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityBuilder<TUser, TRole> AddUserValidator(Func<IUserValidator<TUser>> func)
|
public IdentityBuilder<TUser, TRole> AddUserValidator(IUserValidator<TUser> validator)
|
||||||
{
|
{
|
||||||
return AddInstance(func);
|
return AddInstance(validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdentityBuilder<TUser, TRole> AddTokenProvider(IUserTokenProvider<TUser> tokenProvider)
|
||||||
|
{
|
||||||
|
return AddInstance(tokenProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityBuilder<TUser, TRole> SetupOptions(Action<IdentityOptions> action, int order)
|
public IdentityBuilder<TUser, TRole> SetupOptions(Action<IdentityOptions> action, int order)
|
||||||
|
|
@ -66,11 +71,5 @@ namespace Microsoft.AspNet.Identity
|
||||||
Services.AddScoped<TManager>();
|
Services.AddScoped<TManager>();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
//public IdentityBuilder<TUser, TRole> UseTwoFactorProviders(Func<IDictionary<string,
|
|
||||||
//IUserTokenProvider<TUser>>> func)
|
|
||||||
//{
|
|
||||||
// return Use(func);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Security;
|
||||||
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity
|
namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -8,24 +13,58 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class IdentityOptions
|
public class IdentityOptions
|
||||||
{
|
{
|
||||||
public IdentityOptions()
|
public ClaimsIdentityOptions ClaimsIdentity { get; set; } = new ClaimsIdentityOptions();
|
||||||
|
|
||||||
|
public UserOptions User { get; set; } = new UserOptions();
|
||||||
|
|
||||||
|
public PasswordOptions Password { get; set; } = new PasswordOptions();
|
||||||
|
|
||||||
|
public LockoutOptions Lockout { get; set; } = new LockoutOptions();
|
||||||
|
|
||||||
|
public SignInOptions SignIn { get; set; } = new SignInOptions();
|
||||||
|
|
||||||
|
public TimeSpan SecurityStampValidationInterval { get; set; } = TimeSpan.FromMinutes(30);
|
||||||
|
|
||||||
|
public string EmailConfirmationTokenProvider { get; set; } = Resources.DefaultTokenProvider;
|
||||||
|
|
||||||
|
public string PasswordResetTokenProvider { get; set; } = Resources.DefaultTokenProvider;
|
||||||
|
|
||||||
|
//public string ApplicationCookieAuthenticationType { get; set; }
|
||||||
|
//public string ExternalCookieAuthenticationType { get; set; }
|
||||||
|
//public string TwoFactorCookieAuthenticationType { get; set; }
|
||||||
|
//public string TwoFactorFactorCookieAuthenticationType { get; set; }
|
||||||
|
|
||||||
|
public CookieAuthenticationOptions ApplicationCookie { get; set; } = new CookieAuthenticationOptions
|
||||||
{
|
{
|
||||||
ClaimsIdentity = new ClaimsIdentityOptions();
|
AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType,
|
||||||
User = new UserOptions();
|
LoginPath = new PathString("/Account/Login"),
|
||||||
Password = new PasswordOptions();
|
Notifications = new CookieAuthenticationNotifications
|
||||||
Lockout = new LockoutOptions();
|
{
|
||||||
SignIn = new SignInOptions();
|
OnValidateIdentity = SecurityStampValidator.ValidateIdentityAsync
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public ClaimsIdentityOptions ClaimsIdentity { get; set; }
|
// Move to setups for named per cookie option
|
||||||
|
|
||||||
public UserOptions User { get; set; }
|
public string DefaultSignInAsAuthenticationType { get; set; } = ClaimsIdentityOptions.DefaultExternalLoginAuthenticationType;
|
||||||
|
|
||||||
public PasswordOptions Password { get; set; }
|
public CookieAuthenticationOptions ExternalCookie { get; set; } = new CookieAuthenticationOptions
|
||||||
|
{
|
||||||
|
AuthenticationType = ClaimsIdentityOptions.DefaultExternalLoginAuthenticationType,
|
||||||
|
AuthenticationMode = AuthenticationMode.Passive
|
||||||
|
};
|
||||||
|
|
||||||
public LockoutOptions Lockout { get; set; }
|
public CookieAuthenticationOptions TwoFactorRememberMeCookie { get; set; } = new CookieAuthenticationOptions
|
||||||
|
{
|
||||||
|
AuthenticationType = ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType,
|
||||||
|
AuthenticationMode = AuthenticationMode.Passive
|
||||||
|
};
|
||||||
|
|
||||||
public SignInOptions SignIn { get; set; }
|
public CookieAuthenticationOptions TwoFactorUserIdCookie { get; set; } = new CookieAuthenticationOptions
|
||||||
|
{
|
||||||
|
AuthenticationType = ClaimsIdentityOptions.DefaultTwoFactorUserIdAuthenticationType,
|
||||||
|
AuthenticationMode = AuthenticationMode.Passive
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,23 +33,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// Represents a Role entity
|
/// Represents a Role entity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TKey"></typeparam>
|
/// <typeparam name="TKey"></typeparam>
|
||||||
public class IdentityRole<TKey> where TKey : IEquatable<TKey>
|
public class IdentityRole<TKey>() where TKey : IEquatable<TKey>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
public IdentityRole()
|
|
||||||
{
|
|
||||||
Users = new List<IdentityUserRole<TKey>>();
|
|
||||||
Claims = new List<IdentityRoleClaim<TKey>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="roleName"></param>
|
/// <param name="roleName"></param>
|
||||||
public IdentityRole(string roleName)
|
public IdentityRole(string roleName) : this()
|
||||||
: this()
|
|
||||||
{
|
{
|
||||||
Name = roleName;
|
Name = roleName;
|
||||||
}
|
}
|
||||||
|
|
@ -57,12 +47,12 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Navigation property for users in the role
|
/// Navigation property for users in the role
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual ICollection<IdentityUserRole<TKey>> Users { get; private set; }
|
public virtual ICollection<IdentityUserRole<TKey>> Users { get; private set; } = new List<IdentityUserRole<TKey>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Navigation property for claims in the role
|
/// Navigation property for claims in the role
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual ICollection<IdentityRoleClaim<TKey>> Claims { get; private set; }
|
public virtual ICollection<IdentityRoleClaim<TKey>> Claims { get; private set; } = new List<IdentityRoleClaim<TKey>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Role id
|
/// Role id
|
||||||
|
|
@ -74,4 +64,4 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual string Name { get; set; }
|
public virtual string Name { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,52 +4,64 @@
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Identity;
|
||||||
using Microsoft.Framework.ConfigurationModel;
|
using Microsoft.Framework.ConfigurationModel;
|
||||||
|
using Microsoft.AspNet.Security.DataProtection;
|
||||||
|
|
||||||
namespace Microsoft.Framework.DependencyInjection
|
namespace Microsoft.Framework.DependencyInjection
|
||||||
{
|
{
|
||||||
public static class IdentityServiceCollectionExtensions
|
public static class IdentityServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentity(this ServiceCollection services,
|
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentity(this IServiceCollection services,
|
||||||
IConfiguration identityConfig)
|
IConfiguration identityConfig)
|
||||||
{
|
{
|
||||||
services.SetupOptions<IdentityOptions>(identityConfig);
|
services.SetupOptions<IdentityOptions>(identityConfig);
|
||||||
return services.AddIdentity<IdentityUser, IdentityRole>();
|
return services.AddIdentity<IdentityUser, IdentityRole>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentity(this ServiceCollection services)
|
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentity(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
return services.AddIdentity<IdentityUser, IdentityRole>();
|
return services.AddIdentity<IdentityUser, IdentityRole>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder<TUser, TRole> AddIdentity<TUser, TRole>(this ServiceCollection services,
|
public static IdentityBuilder<TUser, TRole> AddIdentity<TUser, TRole>(this IServiceCollection services,
|
||||||
IConfiguration identityConfig)
|
IConfiguration identityConfig = null)
|
||||||
where TUser : class
|
where TUser : class
|
||||||
where TRole : class
|
where TRole : class
|
||||||
{
|
{
|
||||||
services.SetupOptions<IdentityOptions>(identityConfig);
|
if (identityConfig != null)
|
||||||
return services.AddIdentity<TUser, TRole>();
|
{
|
||||||
}
|
services.SetupOptions<IdentityOptions>(identityConfig);
|
||||||
|
}
|
||||||
public static IdentityBuilder<TUser, TRole> AddIdentity<TUser, TRole>(this ServiceCollection services)
|
services.Add(IdentityServices.GetDefaultServices<TUser, TRole>(identityConfig));
|
||||||
where TUser : class
|
|
||||||
where TRole : class
|
|
||||||
{
|
|
||||||
services.Add(IdentityServices.GetDefaultUserServices<TUser>());
|
|
||||||
services.Add(IdentityServices.GetDefaultRoleServices<TRole>());
|
|
||||||
services.AddScoped<UserManager<TUser>>();
|
services.AddScoped<UserManager<TUser>>();
|
||||||
services.AddScoped<SignInManager<TUser>>();
|
services.AddScoped<SignInManager<TUser>>();
|
||||||
|
services.AddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
|
||||||
services.AddScoped<RoleManager<TRole>>();
|
services.AddScoped<RoleManager<TRole>>();
|
||||||
services.AddScoped<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser, TRole>>();
|
services.AddScoped<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser, TRole>>();
|
||||||
return new IdentityBuilder<TUser, TRole>(services);
|
return new IdentityBuilder<TUser, TRole>(services);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder<TUser, IdentityRole> AddIdentity<TUser>(this ServiceCollection services)
|
public static IdentityBuilder<TUser, TRole> AddDefaultIdentity<TUser, TRole>(this IServiceCollection services, IConfiguration config = null)
|
||||||
|
where TUser : class
|
||||||
|
where TRole : class
|
||||||
|
{
|
||||||
|
return services.AddIdentity<TUser, TRole>(config)
|
||||||
|
.AddTokenProvider(new DataProtectorTokenProvider<TUser>(
|
||||||
|
new DataProtectionTokenProviderOptions
|
||||||
|
{
|
||||||
|
Name = Resources.DefaultTokenProvider,
|
||||||
|
},
|
||||||
|
DataProtectionProvider.CreateFromDpapi().CreateProtector("ASP.NET Identity")))
|
||||||
|
.AddTokenProvider(new PhoneNumberTokenProvider<TUser>())
|
||||||
|
.AddTokenProvider(new EmailTokenProvider<TUser>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IdentityBuilder<TUser, IdentityRole> AddIdentity<TUser>(this IServiceCollection services)
|
||||||
where TUser : class
|
where TUser : class
|
||||||
{
|
{
|
||||||
return services.AddIdentity<TUser, IdentityRole>();
|
return services.AddIdentity<TUser, IdentityRole>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder<TUser, IdentityRole> AddIdentity<TUser>(this ServiceCollection services,
|
public static IdentityBuilder<TUser, IdentityRole> AddIdentity<TUser>(this IServiceCollection services,
|
||||||
IConfiguration identityConfig)
|
IConfiguration identityConfig)
|
||||||
where TUser : class
|
where TUser : class
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,34 +13,23 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class IdentityServices
|
public class IdentityServices
|
||||||
{
|
{
|
||||||
public static IEnumerable<IServiceDescriptor> GetDefaultUserServices<TUser>() where TUser : class
|
public static IEnumerable<IServiceDescriptor> GetDefaultServices<TUser, TRole>(IConfiguration config = null)
|
||||||
|
where TUser : class where TRole : class
|
||||||
{
|
{
|
||||||
return GetDefaultUserServices<TUser>(new Configuration());
|
ServiceDescriber describe;
|
||||||
}
|
if (config == null)
|
||||||
|
{
|
||||||
public static IEnumerable<IServiceDescriptor> GetDefaultUserServices<TUser>(IConfiguration configuration)
|
describe = new ServiceDescriber();
|
||||||
where TUser : class
|
}
|
||||||
{
|
else
|
||||||
var describe = new ServiceDescriber(configuration);
|
{
|
||||||
|
describe = new ServiceDescriber(config);
|
||||||
|
}
|
||||||
yield return describe.Transient<IUserValidator<TUser>, UserValidator<TUser>>();
|
yield return describe.Transient<IUserValidator<TUser>, UserValidator<TUser>>();
|
||||||
yield return describe.Transient<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
|
yield return describe.Transient<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
|
||||||
yield return describe.Transient<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
|
yield return describe.Transient<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
|
||||||
yield return describe.Transient<IUserNameNormalizer, UpperInvariantUserNameNormalizer>();
|
yield return describe.Transient<IUserNameNormalizer, UpperInvariantUserNameNormalizer>();
|
||||||
|
yield return describe.Transient<IRoleValidator<TRole>, RoleValidator<TRole>>();
|
||||||
// TODO: rationalize email/sms/usertoken services
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<IServiceDescriptor> GetDefaultRoleServices<TRole>() where TRole : class
|
|
||||||
{
|
|
||||||
return GetDefaultRoleServices<TRole>(new Configuration());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<IServiceDescriptor> GetDefaultRoleServices<TRole>(IConfiguration configuration)
|
|
||||||
where TRole : class
|
|
||||||
{
|
|
||||||
var describe = new ServiceDescriber(configuration);
|
|
||||||
yield return describe.Instance<IRoleValidator<TRole>>(new RoleValidator<TRole>());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -19,16 +19,8 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IdentityUser<TKey>
|
public class IdentityUser<TKey>() where TKey : IEquatable<TKey>
|
||||||
where TKey : IEquatable<TKey>
|
|
||||||
{
|
{
|
||||||
public IdentityUser()
|
|
||||||
{
|
|
||||||
Claims = new List<IdentityUserClaim<TKey>>();
|
|
||||||
Roles = new List<IdentityUserRole<TKey>>();
|
|
||||||
Logins = new List<IdentityUserLogin<TKey>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IdentityUser(string userName) : this()
|
public IdentityUser(string userName) : this()
|
||||||
{
|
{
|
||||||
UserName = userName;
|
UserName = userName;
|
||||||
|
|
@ -91,16 +83,16 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Roles for the user
|
/// Roles for the user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual ICollection<IdentityUserRole<TKey>> Roles { get; private set; }
|
public virtual ICollection<IdentityUserRole<TKey>> Roles { get; } = new List<IdentityUserRole<TKey>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Claims for the user
|
/// Claims for the user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual ICollection<IdentityUserClaim<TKey>> Claims { get; private set; }
|
public virtual ICollection<IdentityUserClaim<TKey>> Claims { get; } = new List<IdentityUserClaim<TKey>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Associated logins for the user
|
/// Associated logins for the user
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual ICollection<IdentityUserLogin<TKey>> Logins { get; private set; }
|
public virtual ICollection<IdentityUserLogin<TKey>> Logins { get; } = new List<IdentityUserLogin<TKey>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,26 +7,19 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
public class LockoutOptions
|
public class LockoutOptions
|
||||||
{
|
{
|
||||||
public LockoutOptions()
|
|
||||||
{
|
|
||||||
DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
|
|
||||||
EnabledByDefault = false;
|
|
||||||
MaxFailedAccessAttempts = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, will enable user lockout when users are created
|
/// If true, will enable user lockout when users are created
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnabledByDefault { get; set; }
|
public bool EnabledByDefault { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of access attempts allowed for a user before lockout (if enabled)
|
/// Number of access attempts allowed for a user before lockout (if enabled)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxFailedAccessAttempts { get; set; }
|
public int MaxFailedAccessAttempts { get; set; } = 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default amount of time an user is locked out for after MaxFailedAccessAttempsBeforeLockout is reached
|
/// Default amount of time an user is locked out for after MaxFailedAccessAttempsBeforeLockout is reached
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan DefaultLockoutTimeSpan { get; set; }
|
public TimeSpan DefaultLockoutTimeSpan { get; set; } = TimeSpan.FromMinutes(5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,38 +5,29 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
public class PasswordOptions
|
public class PasswordOptions
|
||||||
{
|
{
|
||||||
public PasswordOptions()
|
|
||||||
{
|
|
||||||
RequireDigit = true;
|
|
||||||
RequireLowercase = true;
|
|
||||||
RequireNonLetterOrDigit = true;
|
|
||||||
RequireUppercase = true;
|
|
||||||
RequiredLength = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum required length
|
/// Minimum required length
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int RequiredLength { get; set; }
|
public int RequiredLength { get; set; } = 6;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Require a non letter or digit character
|
/// Require a non letter or digit character
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequireNonLetterOrDigit { get; set; }
|
public bool RequireNonLetterOrDigit { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Require a lower case letter ('a' - 'z')
|
/// Require a lower case letter ('a' - 'z')
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequireLowercase { get; set; }
|
public bool RequireLowercase { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Require an upper case letter ('A' - 'Z')
|
/// Require an upper case letter ('A' - 'Z')
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequireUppercase { get; set; }
|
public bool RequireUppercase { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Require a digit ('0' - '9')
|
/// Require a digit ('0' - '9')
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequireDigit { get; set; }
|
public bool RequireDigit { get; set; } = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="manager"></param>
|
/// <param name="manager"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual Task<IdentityResult> ValidateAsync(string password, UserManager<TUser> manager,
|
public virtual Task<IdentityResult> ValidateAsync(TUser user, string password, UserManager<TUser> manager,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
if (password == null)
|
if (password == null)
|
||||||
|
|
|
||||||
|
|
@ -5,23 +5,28 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity
|
namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
/// <summary>
|
public class PhoneNumberTokenProviderOptions
|
||||||
/// TokenProvider that generates tokens from the user's security stamp and notifies a user via their phone number
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TUser"></typeparam>
|
|
||||||
public class PhoneNumberTokenProvider<TUser> : TotpSecurityStampBasedTokenProvider<TUser>
|
|
||||||
where TUser : class
|
|
||||||
{
|
{
|
||||||
private string _body;
|
public string Name { get; set; } = Resources.DefaultPhoneNumberTokenProviderName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Message contents which should contain a format string which the token will be the only argument
|
/// Message contents which should contain a format string which the token will be the only argument
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string MessageFormat
|
public string MessageFormat { get; set; } = "Your security code is: {0}";
|
||||||
{
|
}
|
||||||
get { return _body ?? "{0}"; }
|
|
||||||
set { _body = value; }
|
/// <summary>
|
||||||
}
|
/// TokenProvider that generates tokens from the user's security stamp and notifies a user via their phone number
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TUser"></typeparam>
|
||||||
|
public class PhoneNumberTokenProvider<TUser>(PhoneNumberTokenProviderOptions options) : TotpSecurityStampBasedTokenProvider<TUser>
|
||||||
|
where TUser : class
|
||||||
|
{
|
||||||
|
public PhoneNumberTokenProvider() : this(new PhoneNumberTokenProviderOptions()) { }
|
||||||
|
|
||||||
|
public PhoneNumberTokenProviderOptions Options { get; } = options;
|
||||||
|
|
||||||
|
public override string Name { get { return Options.Name; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the user has a phone number set
|
/// Returns true if the user has a phone number set
|
||||||
|
|
@ -29,7 +34,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="manager"></param>
|
/// <param name="manager"></param>
|
||||||
/// <param name="user"></param>
|
/// <param name="user"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override async Task<bool> IsValidProviderForUserAsync(UserManager<TUser> manager, TUser user,
|
public override async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
if (manager == null)
|
if (manager == null)
|
||||||
|
|
@ -72,7 +77,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("manager");
|
throw new ArgumentNullException("manager");
|
||||||
}
|
}
|
||||||
return manager.SendSmsAsync(user, String.Format(CultureInfo.CurrentCulture, MessageFormat, token), cancellationToken);
|
return manager.SendSmsAsync(user, String.Format(CultureInfo.CurrentCulture, Options.MessageFormat, token), cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.AspNet.Identity.Test")]
|
||||||
|
|
@ -10,6 +10,54 @@ namespace Microsoft.AspNet.Identity
|
||||||
private static readonly ResourceManager _resourceManager
|
private static readonly ResourceManager _resourceManager
|
||||||
= new ResourceManager("Microsoft.AspNet.Identity.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
= new ResourceManager("Microsoft.AspNet.Identity.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Your security code is: {0}
|
||||||
|
/// </summary>
|
||||||
|
internal static string DefaultEmailTokenProviderBodyFormat
|
||||||
|
{
|
||||||
|
get { return GetString("DefaultEmailTokenProviderBodyFormat"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Your security code is: {0}
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatDefaultEmailTokenProviderBodyFormat(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("DefaultEmailTokenProviderBodyFormat"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Email
|
||||||
|
/// </summary>
|
||||||
|
internal static string DefaultEmailTokenProviderName
|
||||||
|
{
|
||||||
|
get { return GetString("DefaultEmailTokenProviderName"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Email
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatDefaultEmailTokenProviderName()
|
||||||
|
{
|
||||||
|
return GetString("DefaultEmailTokenProviderName");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Security Code
|
||||||
|
/// </summary>
|
||||||
|
internal static string DefaultEmailTokenProviderSubject
|
||||||
|
{
|
||||||
|
get { return GetString("DefaultEmailTokenProviderSubject"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Security Code
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatDefaultEmailTokenProviderSubject()
|
||||||
|
{
|
||||||
|
return GetString("DefaultEmailTokenProviderSubject");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An unknown failure has occured.
|
/// An unknown failure has occured.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -26,6 +74,54 @@ namespace Microsoft.AspNet.Identity
|
||||||
return GetString("DefaultError");
|
return GetString("DefaultError");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Your security code is: {0}
|
||||||
|
/// </summary>
|
||||||
|
internal static string DefaultPhoneNumberTokenProviderMessageFormat
|
||||||
|
{
|
||||||
|
get { return GetString("DefaultPhoneNumberTokenProviderMessageFormat"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Your security code is: {0}
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatDefaultPhoneNumberTokenProviderMessageFormat(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("DefaultPhoneNumberTokenProviderMessageFormat"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Phone
|
||||||
|
/// </summary>
|
||||||
|
internal static string DefaultPhoneNumberTokenProviderName
|
||||||
|
{
|
||||||
|
get { return GetString("DefaultPhoneNumberTokenProviderName"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Phone
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatDefaultPhoneNumberTokenProviderName()
|
||||||
|
{
|
||||||
|
return GetString("DefaultPhoneNumberTokenProviderName");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DefaultTokenProvider
|
||||||
|
/// </summary>
|
||||||
|
internal static string DefaultTokenProvider
|
||||||
|
{
|
||||||
|
get { return GetString("DefaultTokenProvider"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DefaultTokenProvider
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatDefaultTokenProvider()
|
||||||
|
{
|
||||||
|
return GetString("DefaultTokenProvider");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Email '{0}' is already taken.
|
/// Email '{0}' is already taken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -139,7 +235,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// No IUserTokenProvider is registered.
|
/// No IUserTokenProvider named '{0}' is registered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string NoTokenProvider
|
internal static string NoTokenProvider
|
||||||
{
|
{
|
||||||
|
|
@ -147,27 +243,11 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// No IUserTokenProvider is registered.
|
/// No IUserTokenProvider named '{0}' is registered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatNoTokenProvider()
|
internal static string FormatNoTokenProvider(object p0)
|
||||||
{
|
{
|
||||||
return GetString("NoTokenProvider");
|
return string.Format(CultureInfo.CurrentCulture, GetString("NoTokenProvider"), p0);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// No IUserTwoFactorProvider for '{0}' is registered.
|
|
||||||
/// </summary>
|
|
||||||
internal static string NoTwoFactorProvider
|
|
||||||
{
|
|
||||||
get { return GetString("NoTwoFactorProvider"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// No IUserTwoFactorProvider for '{0}' is registered.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatNoTwoFactorProvider(object p0)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("NoTwoFactorProvider"), p0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -117,10 +117,34 @@
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<data name="DefaultEmailTokenProviderBodyFormat" xml:space="preserve">
|
||||||
|
<value>Your security code is: {0}</value>
|
||||||
|
<comment>Default body format for the email</comment>
|
||||||
|
</data>
|
||||||
|
<data name="DefaultEmailTokenProviderName" xml:space="preserve">
|
||||||
|
<value>Email</value>
|
||||||
|
<comment>Default name for the email token provider</comment>
|
||||||
|
</data>
|
||||||
|
<data name="DefaultEmailTokenProviderSubject" xml:space="preserve">
|
||||||
|
<value>Security Code</value>
|
||||||
|
<comment>Default subject for the email</comment>
|
||||||
|
</data>
|
||||||
<data name="DefaultError" xml:space="preserve">
|
<data name="DefaultError" xml:space="preserve">
|
||||||
<value>An unknown failure has occured.</value>
|
<value>An unknown failure has occured.</value>
|
||||||
<comment>Default identity result error message</comment>
|
<comment>Default identity result error message</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="DefaultPhoneNumberTokenProviderMessageFormat" xml:space="preserve">
|
||||||
|
<value>Your security code is: {0}</value>
|
||||||
|
<comment>Default message format for the phone number token provider</comment>
|
||||||
|
</data>
|
||||||
|
<data name="DefaultPhoneNumberTokenProviderName" xml:space="preserve">
|
||||||
|
<value>Phone</value>
|
||||||
|
<comment>Default name for the phone number token provider</comment>
|
||||||
|
</data>
|
||||||
|
<data name="DefaultTokenProvider" xml:space="preserve">
|
||||||
|
<value>DefaultTokenProvider</value>
|
||||||
|
<comment>Name of the default token provider</comment>
|
||||||
|
</data>
|
||||||
<data name="DuplicateEmail" xml:space="preserve">
|
<data name="DuplicateEmail" xml:space="preserve">
|
||||||
<value>Email '{0}' is already taken.</value>
|
<value>Email '{0}' is already taken.</value>
|
||||||
<comment>error for duplicate emails</comment>
|
<comment>error for duplicate emails</comment>
|
||||||
|
|
@ -150,13 +174,9 @@
|
||||||
<comment>error when lockout is not enabled</comment>
|
<comment>error when lockout is not enabled</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoTokenProvider" xml:space="preserve">
|
<data name="NoTokenProvider" xml:space="preserve">
|
||||||
<value>No IUserTokenProvider is registered.</value>
|
<value>No IUserTokenProvider named '{0}' is registered.</value>
|
||||||
<comment>Error when there is no IUserTokenProvider</comment>
|
<comment>Error when there is no IUserTokenProvider</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoTwoFactorProvider" xml:space="preserve">
|
|
||||||
<value>No IUserTwoFactorProvider for '{0}' is registered.</value>
|
|
||||||
<comment>Error when there is no provider found</comment>
|
|
||||||
</data>
|
|
||||||
<data name="PasswordMismatch" xml:space="preserve">
|
<data name="PasswordMismatch" xml:space="preserve">
|
||||||
<value>Incorrect password.</value>
|
<value>Incorrect password.</value>
|
||||||
<comment>Error when a password doesn't match</comment>
|
<comment>Error when a password doesn't match</comment>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
private static int ComputeTotp(HashAlgorithm hashAlgorithm, ulong timestepNumber, string modifier)
|
private static int ComputeTotp(HashAlgorithm hashAlgorithm, ulong timestepNumber, string modifier)
|
||||||
{
|
{
|
||||||
// # of 0's = length of pin
|
// # of 0's = length of pin
|
||||||
const int mod = 1000000;
|
const int Mod = 1000000;
|
||||||
|
|
||||||
// See https://tools.ietf.org/html/rfc4226
|
// See https://tools.ietf.org/html/rfc4226
|
||||||
// We can add an optional modifier
|
// We can add an optional modifier
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
| (hash[offset + 2] & 0xff) << 8
|
| (hash[offset + 2] & 0xff) << 8
|
||||||
| (hash[offset + 3] & 0xff);
|
| (hash[offset + 3] & 0xff);
|
||||||
|
|
||||||
return binaryCode % mod;
|
return binaryCode % Mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] ApplyModifier(byte[] input, string modifier)
|
private static byte[] ApplyModifier(byte[] input, string modifier)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Identity
|
||||||
|
{
|
||||||
|
public class SecurityStampValidator<TUser> : ISecurityStampValidator where TUser : class
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rejects the identity if the stamp changes, and otherwise will sign in a new
|
||||||
|
/// ClaimsIdentity
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual async Task Validate(CookieValidateIdentityContext context, ClaimsIdentity identity)
|
||||||
|
{
|
||||||
|
var manager = context.HttpContext.RequestServices.GetService<SignInManager<TUser>>();
|
||||||
|
var userId = identity.GetUserId();
|
||||||
|
var user = await manager.ValidateSecurityStampAsync(identity, userId);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
var isPersistent = false;
|
||||||
|
if (context.Properties != null)
|
||||||
|
{
|
||||||
|
isPersistent = context.Properties.IsPersistent;
|
||||||
|
}
|
||||||
|
await manager.SignInAsync(user, isPersistent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.RejectIdentity();
|
||||||
|
manager.SignOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static helper class used to configure a CookieAuthenticationNotifications to validate a cookie against a user's security
|
||||||
|
/// stamp
|
||||||
|
/// </summary>
|
||||||
|
public static class SecurityStampValidator
|
||||||
|
{
|
||||||
|
public static Task ValidateIdentityAsync(CookieValidateIdentityContext context)
|
||||||
|
{
|
||||||
|
var currentUtc = DateTimeOffset.UtcNow;
|
||||||
|
if (context.Options != null && context.Options.SystemClock != null)
|
||||||
|
{
|
||||||
|
currentUtc = context.Options.SystemClock.UtcNow;
|
||||||
|
}
|
||||||
|
var issuedUtc = context.Properties.IssuedUtc;
|
||||||
|
|
||||||
|
// Only validate if enough time has elapsed
|
||||||
|
var validate = (issuedUtc == null);
|
||||||
|
if (issuedUtc != null)
|
||||||
|
{
|
||||||
|
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
|
||||||
|
var identityOptions = context.HttpContext.RequestServices.GetService<IOptionsAccessor<IdentityOptions>>().Options;
|
||||||
|
validate = timeElapsed > identityOptions.SecurityStampValidationInterval;
|
||||||
|
}
|
||||||
|
if (validate)
|
||||||
|
{
|
||||||
|
var validator = context.HttpContext.RequestServices.GetService<ISecurityStampValidator>();
|
||||||
|
return validator.Validate(context, context.Identity);
|
||||||
|
}
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,9 @@ using System.Security.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity
|
namespace Microsoft.AspNet.Identity
|
||||||
|
|
@ -16,33 +19,33 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <typeparam name="TUser"></typeparam>
|
/// <typeparam name="TUser"></typeparam>
|
||||||
public class SignInManager<TUser> where TUser : class
|
public class SignInManager<TUser> where TUser : class
|
||||||
{
|
{
|
||||||
public SignInManager(UserManager<TUser> userManager, IAuthenticationManager authenticationManager,
|
public SignInManager(UserManager<TUser> userManager, IContextAccessor<HttpContext> contextAccessor,
|
||||||
IClaimsIdentityFactory<TUser> claimsFactory, IOptionsAccessor<IdentityOptions> optionsAccessor)
|
IClaimsIdentityFactory<TUser> claimsFactory, IOptionsAccessor<IdentityOptions> optionsAccessor)
|
||||||
{
|
{
|
||||||
if (userManager == null)
|
if (userManager == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("userManager");
|
throw new ArgumentNullException(nameof(userManager));
|
||||||
}
|
}
|
||||||
if (authenticationManager == null)
|
if (contextAccessor == null || contextAccessor.Value == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("authenticationManager");
|
throw new ArgumentNullException(nameof(contextAccessor));
|
||||||
}
|
}
|
||||||
if (claimsFactory == null)
|
if (claimsFactory == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("claimsFactory");
|
throw new ArgumentNullException(nameof(claimsFactory));
|
||||||
}
|
}
|
||||||
if (optionsAccessor == null || optionsAccessor.Options == null)
|
if (optionsAccessor == null || optionsAccessor.Options == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("optionsAccessor");
|
throw new ArgumentNullException(nameof(optionsAccessor));
|
||||||
}
|
}
|
||||||
UserManager = userManager;
|
UserManager = userManager;
|
||||||
AuthenticationManager = authenticationManager;
|
Context = contextAccessor.Value;
|
||||||
ClaimsFactory = claimsFactory;
|
ClaimsFactory = claimsFactory;
|
||||||
Options = optionsAccessor.Options;
|
Options = optionsAccessor.Options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserManager<TUser> UserManager { get; private set; }
|
public UserManager<TUser> UserManager { get; private set; }
|
||||||
public IAuthenticationManager AuthenticationManager { get; private set; }
|
public HttpContext Context { get; private set; }
|
||||||
public IClaimsIdentityFactory<TUser> ClaimsFactory { get; private set; }
|
public IClaimsIdentityFactory<TUser> ClaimsFactory { get; private set; }
|
||||||
public IdentityOptions Options { get; private set; }
|
public IdentityOptions Options { get; private set; }
|
||||||
|
|
||||||
|
|
@ -50,7 +53,6 @@ namespace Microsoft.AspNet.Identity
|
||||||
public virtual async Task<ClaimsIdentity> CreateUserIdentityAsync(TUser user,
|
public virtual async Task<ClaimsIdentity> CreateUserIdentityAsync(TUser user,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
// REVIEW: should sign in manager take options instead of using the user manager instance?
|
|
||||||
return await ClaimsFactory.CreateAsync(user, Options.ClaimsIdentity);
|
return await ClaimsFactory.CreateAsync(user, Options.ClaimsIdentity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,18 +70,22 @@ namespace Microsoft.AspNet.Identity
|
||||||
// return true;
|
// return true;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
public virtual async Task SignInAsync(TUser user, bool isPersistent,
|
public virtual async Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var userIdentity = await CreateUserIdentityAsync(user);
|
var userIdentity = await CreateUserIdentityAsync(user);
|
||||||
AuthenticationManager.SignIn(userIdentity, isPersistent);
|
if (authenticationMethod != null)
|
||||||
|
{
|
||||||
|
userIdentity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, authenticationMethod));
|
||||||
|
}
|
||||||
|
Context.Response.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, userIdentity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Should this be async?
|
// TODO: Should this be async?
|
||||||
public virtual void SignOut()
|
public virtual void SignOut()
|
||||||
{
|
{
|
||||||
// REVIEW: need a new home for this option config?
|
// REVIEW: need a new home for this option config?
|
||||||
AuthenticationManager.SignOut(Options.ClaimsIdentity.AuthenticationType);
|
Context.Response.SignOut(Options.ClaimsIdentity.AuthenticationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> IsLockedOut(TUser user, CancellationToken token)
|
private async Task<bool> IsLockedOut(TUser user, CancellationToken token)
|
||||||
|
|
@ -138,16 +144,31 @@ namespace Microsoft.AspNet.Identity
|
||||||
return SignInStatus.Failure;
|
return SignInStatus.Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ClaimsIdentity CreateIdentity(TwoFactorAuthenticationInfo info)
|
||||||
|
{
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var identity = new ClaimsIdentity(ClaimsIdentityOptions.DefaultTwoFactorUserIdAuthenticationType);
|
||||||
|
identity.AddClaim(new Claim(ClaimTypes.Name, info.UserId));
|
||||||
|
if (info.LoginProvider != null)
|
||||||
|
{
|
||||||
|
identity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, info.LoginProvider));
|
||||||
|
}
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual async Task<bool> SendTwoFactorCodeAsync(string provider,
|
public virtual async Task<bool> SendTwoFactorCodeAsync(string provider,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var userId = await AuthenticationManager.RetrieveUserId();
|
var twoFactorInfo = await RetrieveTwoFactorInfoAsync(cancellationToken);
|
||||||
if (userId == null)
|
if (twoFactorInfo == null || twoFactorInfo.UserId == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await UserManager.FindByIdAsync(userId, cancellationToken);
|
var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId, cancellationToken);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -162,31 +183,35 @@ namespace Microsoft.AspNet.Identity
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
||||||
return await AuthenticationManager.IsClientRememeberedAsync(userId, cancellationToken);
|
var result =
|
||||||
|
await Context.AuthenticateAsync(ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType);
|
||||||
|
return (result != null && result.Identity != null && result.Identity.Name == userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task RememberTwoFactorClient(TUser user,
|
public virtual async Task RememberTwoFactorClientAsync(TUser user,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
||||||
AuthenticationManager.RememberClient(userId);
|
var rememberBrowserIdentity = new ClaimsIdentity(ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType);
|
||||||
|
rememberBrowserIdentity.AddClaim(new Claim(ClaimTypes.Name, userId));
|
||||||
|
Context.Response.SignIn(new AuthenticationProperties { IsPersistent = true }, rememberBrowserIdentity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Task ForgetTwoFactorClientAsync()
|
public virtual Task ForgetTwoFactorClientAsync()
|
||||||
{
|
{
|
||||||
AuthenticationManager.ForgetClient();
|
Context.Response.SignOut(ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType);
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent,
|
public virtual async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
bool rememberClient, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var userId = await AuthenticationManager.RetrieveUserId();
|
var twoFactorInfo = await RetrieveTwoFactorInfoAsync(cancellationToken);
|
||||||
if (userId == null)
|
if (twoFactorInfo == null || twoFactorInfo.UserId == null)
|
||||||
{
|
{
|
||||||
return SignInStatus.Failure;
|
return SignInStatus.Failure;
|
||||||
}
|
}
|
||||||
var user = await UserManager.FindByIdAsync(userId, cancellationToken);
|
var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId, cancellationToken);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return SignInStatus.Failure;
|
return SignInStatus.Failure;
|
||||||
|
|
@ -199,7 +224,11 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
// When token is verified correctly, clear the access failed count used for lockout
|
// When token is verified correctly, clear the access failed count used for lockout
|
||||||
await UserManager.ResetAccessFailedCountAsync(user, cancellationToken);
|
await UserManager.ResetAccessFailedCountAsync(user, cancellationToken);
|
||||||
await SignInAsync(user, isPersistent);
|
await SignInAsync(user, isPersistent, twoFactorInfo.LoginProvider, cancellationToken);
|
||||||
|
if (rememberClient)
|
||||||
|
{
|
||||||
|
await RememberTwoFactorClientAsync(user, cancellationToken);
|
||||||
|
}
|
||||||
return SignInStatus.Success;
|
return SignInStatus.Success;
|
||||||
}
|
}
|
||||||
// If the token is incorrect, record the failure which also may cause the user to be locked out
|
// If the token is incorrect, record the failure which also may cause the user to be locked out
|
||||||
|
|
@ -207,6 +236,23 @@ namespace Microsoft.AspNet.Identity
|
||||||
return SignInStatus.Failure;
|
return SignInStatus.Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the user who has started the two factor authentication process
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual async Task<TUser> GetTwoFactorAuthenticationUserAsync(
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var info = await RetrieveTwoFactorInfoAsync(cancellationToken);
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await UserManager.FindByIdAsync(info.UserId, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<SignInStatus> ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent,
|
public async Task<SignInStatus> ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
|
|
@ -219,24 +265,57 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
return SignInStatus.LockedOut;
|
return SignInStatus.LockedOut;
|
||||||
}
|
}
|
||||||
return await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken);
|
return await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken, loginProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SignInStatus> SignInOrTwoFactorAsync(TUser user, bool isPersistent,
|
private async Task<SignInStatus> SignInOrTwoFactorAsync(TUser user, bool isPersistent,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken, string loginProvider = null)
|
||||||
{
|
{
|
||||||
if (UserManager.SupportsUserTwoFactor && await UserManager.GetTwoFactorEnabledAsync(user))
|
if (UserManager.SupportsUserTwoFactor &&
|
||||||
|
await UserManager.GetTwoFactorEnabledAsync(user, cancellationToken) &&
|
||||||
|
(await UserManager.GetValidTwoFactorProvidersAsync(user, cancellationToken)).Count > 0)
|
||||||
{
|
{
|
||||||
if (!await IsTwoFactorClientRememberedAsync(user, cancellationToken))
|
if (!await IsTwoFactorClientRememberedAsync(user, cancellationToken))
|
||||||
{
|
{
|
||||||
// Store the userId for use after two factor check
|
// Store the userId for use after two factor check
|
||||||
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
||||||
await AuthenticationManager.StoreUserId(userId);
|
Context.Response.SignIn(StoreTwoFactorInfo(userId, loginProvider));
|
||||||
return SignInStatus.RequiresVerification;
|
return SignInStatus.RequiresVerification;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await SignInAsync(user, isPersistent, cancellationToken);
|
await SignInAsync(user, isPersistent, loginProvider, cancellationToken);
|
||||||
return SignInStatus.Success;
|
return SignInStatus.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<TwoFactorAuthenticationInfo> RetrieveTwoFactorInfoAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var result = await Context.AuthenticateAsync(ClaimsIdentityOptions.DefaultTwoFactorUserIdAuthenticationType);
|
||||||
|
if (result != null && result.Identity != null)
|
||||||
|
{
|
||||||
|
return new TwoFactorAuthenticationInfo
|
||||||
|
{
|
||||||
|
UserId = result.Identity.Name,
|
||||||
|
LoginProvider = result.Identity.FindFirstValue(ClaimTypes.AuthenticationMethod)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ClaimsIdentity StoreTwoFactorInfo(string userId, string loginProvider)
|
||||||
|
{
|
||||||
|
var identity = new ClaimsIdentity(ClaimsIdentityOptions.DefaultTwoFactorUserIdAuthenticationType);
|
||||||
|
identity.AddClaim(new Claim(ClaimTypes.Name, userId));
|
||||||
|
if (loginProvider != null)
|
||||||
|
{
|
||||||
|
identity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, loginProvider));
|
||||||
|
}
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class TwoFactorAuthenticationInfo
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
public string LoginProvider { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,8 +5,6 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
public class SignInOptions
|
public class SignInOptions
|
||||||
{
|
{
|
||||||
public SignInOptions() { }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If set, requires a confirmed email to sign in
|
/// If set, requires a confirmed email to sign in
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,11 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TUser"></typeparam>
|
/// <typeparam name="TUser"></typeparam>
|
||||||
/// <typeparam name="TKey"></typeparam>
|
/// <typeparam name="TKey"></typeparam>
|
||||||
public class TotpSecurityStampBasedTokenProvider<TUser> : IUserTokenProvider<TUser>
|
public abstract class TotpSecurityStampBasedTokenProvider<TUser> : IUserTokenProvider<TUser>
|
||||||
where TUser : class
|
where TUser : class
|
||||||
{
|
{
|
||||||
|
public abstract string Name { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This token provider does not notify the user by default
|
/// This token provider does not notify the user by default
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -26,23 +28,6 @@ namespace Microsoft.AspNet.Identity
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the provider can generate tokens for the user, by default this is equal to
|
|
||||||
/// manager.SupportsUserSecurityStamp
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="manager"></param>
|
|
||||||
/// <param name="user"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public virtual Task<bool> IsValidProviderForUserAsync(UserManager<TUser> manager, TUser user,
|
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
if (manager == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("manager");
|
|
||||||
}
|
|
||||||
return Task.FromResult(manager.SupportsUserSecurityStamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate a token for the user using their security stamp
|
/// Generate a token for the user using their security stamp
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -101,8 +86,10 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("manager");
|
throw new ArgumentNullException("manager");
|
||||||
}
|
}
|
||||||
string userId = await manager.GetUserIdAsync(user);
|
var userId = await manager.GetUserIdAsync(user);
|
||||||
return "Totp:" + purpose + ":" + userId;
|
return "Totp:" + purpose + ":" + userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,34 +6,21 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a linked login for a user (i.e. a local username/password or a facebook/google account
|
/// Represents a linked login for a user (i.e. a local username/password or a facebook/google account
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class UserLoginInfo
|
public class UserLoginInfo(string loginProvider, string providerKey, string displayName)
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="loginProvider"></param>
|
|
||||||
/// <param name="providerKey"></param>
|
|
||||||
/// <param name="displayName"></param>
|
|
||||||
public UserLoginInfo(string loginProvider, string providerKey, string displayName)
|
|
||||||
{
|
|
||||||
LoginProvider = loginProvider;
|
|
||||||
ProviderKey = providerKey;
|
|
||||||
ProviderDisplayName = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provider for the linked login, i.e. Local, Facebook, Google, etc.
|
/// Provider for the linked login, i.e. Local, Facebook, Google, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LoginProvider { get; set; }
|
public string LoginProvider { get; set; } = loginProvider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Key for the linked login at the provider
|
/// Key for the linked login at the provider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ProviderKey { get; set; }
|
public string ProviderKey { get; set; } = providerKey;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Display name for the provider
|
/// Display name for the provider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ProviderDisplayName { get; set; }
|
public string ProviderDisplayName { get; set; } = displayName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
using Microsoft.AspNet.Security.DataProtection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity
|
namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +20,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <typeparam name="TUser"></typeparam>
|
/// <typeparam name="TUser"></typeparam>
|
||||||
public class UserManager<TUser> : IDisposable where TUser : class
|
public class UserManager<TUser> : IDisposable where TUser : class
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, IUserTokenProvider<TUser>> _factors =
|
private readonly Dictionary<string, IUserTokenProvider<TUser>> _tokenProviders =
|
||||||
new Dictionary<string, IUserTokenProvider<TUser>>();
|
new Dictionary<string, IUserTokenProvider<TUser>>();
|
||||||
|
|
||||||
private TimeSpan _defaultLockout = TimeSpan.Zero;
|
private TimeSpan _defaultLockout = TimeSpan.Zero;
|
||||||
|
|
@ -38,19 +39,20 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="claimsIdentityFactory"></param>
|
/// <param name="claimsIdentityFactory"></param>
|
||||||
public UserManager(IUserStore<TUser> store, IOptionsAccessor<IdentityOptions> optionsAccessor,
|
public UserManager(IUserStore<TUser> store, IOptionsAccessor<IdentityOptions> optionsAccessor,
|
||||||
IPasswordHasher<TUser> passwordHasher, IUserValidator<TUser> userValidator,
|
IPasswordHasher<TUser> passwordHasher, IUserValidator<TUser> userValidator,
|
||||||
IPasswordValidator<TUser> passwordValidator, IUserNameNormalizer userNameNormalizer)
|
IPasswordValidator<TUser> passwordValidator, IUserNameNormalizer userNameNormalizer,
|
||||||
|
IEnumerable<IUserTokenProvider<TUser>> tokenProviders)
|
||||||
{
|
{
|
||||||
if (store == null)
|
if (store == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("store");
|
throw new ArgumentNullException(nameof(store));
|
||||||
}
|
}
|
||||||
if (optionsAccessor == null || optionsAccessor.Options == null)
|
if (optionsAccessor == null || optionsAccessor.Options == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("optionsAccessor");
|
throw new ArgumentNullException(nameof(optionsAccessor));
|
||||||
}
|
}
|
||||||
if (passwordHasher == null)
|
if (passwordHasher == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("passwordHasher");
|
throw new ArgumentNullException(nameof(passwordHasher));
|
||||||
}
|
}
|
||||||
Store = store;
|
Store = store;
|
||||||
Options = optionsAccessor.Options;
|
Options = optionsAccessor.Options;
|
||||||
|
|
@ -59,6 +61,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
PasswordValidator = passwordValidator;
|
PasswordValidator = passwordValidator;
|
||||||
UserNameNormalizer = userNameNormalizer;
|
UserNameNormalizer = userNameNormalizer;
|
||||||
// TODO: Email/Sms/Token services
|
// TODO: Email/Sms/Token services
|
||||||
|
|
||||||
|
if (tokenProviders != null) {
|
||||||
|
foreach (var tokenProvider in tokenProviders)
|
||||||
|
{
|
||||||
|
RegisterTokenProvider(tokenProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -112,11 +121,6 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IIdentityMessageService SmsService { get; set; }
|
public IIdentityMessageService SmsService { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used for generating ResetPassword and Confirmation Tokens
|
|
||||||
/// </summary>
|
|
||||||
public IUserTokenProvider<TUser> UserTokenProvider { get; set; }
|
|
||||||
|
|
||||||
public IdentityOptions Options
|
public IdentityOptions Options
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -271,14 +275,6 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Dictionary mapping user two factor providers
|
|
||||||
/// </summary>
|
|
||||||
public IDictionary<string, IUserTokenProvider<TUser>> TwoFactorProviders
|
|
||||||
{
|
|
||||||
get { return _factors; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dispose the store context
|
/// Dispose the store context
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -451,7 +447,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
public virtual async Task UpdateNormalizedUserName(TUser user,
|
public virtual async Task UpdateNormalizedUserName(TUser user,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
string userName = await GetUserNameAsync(user, cancellationToken);
|
var userName = await GetUserNameAsync(user, cancellationToken);
|
||||||
await Store.SetNormalizedUserNameAsync(user, NormalizeUserName(userName), cancellationToken);
|
await Store.SetNormalizedUserNameAsync(user, NormalizeUserName(userName), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -497,7 +493,6 @@ namespace Microsoft.AspNet.Identity
|
||||||
await UpdateNormalizedUserName(user, cancellationToken);
|
await UpdateNormalizedUserName(user, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the user's id
|
/// Get the user's id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -651,7 +646,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
if (PasswordValidator != null)
|
if (PasswordValidator != null)
|
||||||
{
|
{
|
||||||
var result = await PasswordValidator.ValidateAsync(newPassword, this, cancellationToken);
|
var result = await PasswordValidator.ValidateAsync(user, newPassword, this, cancellationToken);
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -736,7 +731,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
return await GenerateUserTokenAsync("ResetPassword", user, cancellationToken);
|
return await GenerateUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -756,7 +751,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
// Make sure the token is valid and the stamp matches
|
// Make sure the token is valid and the stamp matches
|
||||||
if (!await VerifyUserTokenAsync(user, "ResetPassword", token, cancellationToken))
|
if (!await VerifyUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", token, cancellationToken))
|
||||||
{
|
{
|
||||||
return IdentityResult.Failed(Resources.InvalidToken);
|
return IdentityResult.Failed(Resources.InvalidToken);
|
||||||
}
|
}
|
||||||
|
|
@ -974,7 +969,6 @@ namespace Microsoft.AspNet.Identity
|
||||||
return RemoveClaimsAsync(user, new Claim[] { claim }, cancellationToken);
|
return RemoveClaimsAsync(user, new Claim[] { claim }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove a user claim
|
/// Remove a user claim
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1271,7 +1265,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
return GenerateUserTokenAsync("Confirmation", user, cancellationToken);
|
return GenerateUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1290,7 +1284,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
if (!await VerifyUserTokenAsync(user, "Confirmation", token, cancellationToken))
|
if (!await VerifyUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", token, cancellationToken))
|
||||||
{
|
{
|
||||||
return IdentityResult.Failed(Resources.InvalidToken);
|
return IdentityResult.Failed(Resources.InvalidToken);
|
||||||
}
|
}
|
||||||
|
|
@ -1461,20 +1455,24 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual async Task<bool> VerifyUserTokenAsync(TUser user, string purpose, string token,
|
public virtual async Task<bool> VerifyUserTokenAsync(TUser user, string tokenProvider, string purpose, string token,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
if (UserTokenProvider == null)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException(Resources.NoTokenProvider);
|
|
||||||
}
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
|
if (tokenProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tokenProvider));
|
||||||
|
}
|
||||||
|
if (!_tokenProviders.ContainsKey(tokenProvider))
|
||||||
|
{
|
||||||
|
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider));
|
||||||
|
}
|
||||||
// Make sure the token is valid
|
// Make sure the token is valid
|
||||||
return await UserTokenProvider.ValidateAsync(purpose, token, this, user, cancellationToken);
|
return await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1484,38 +1482,38 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="user"></param>
|
/// <param name="user"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual async Task<string> GenerateUserTokenAsync(string purpose, TUser user,
|
public virtual async Task<string> GenerateUserTokenAsync(TUser user, string tokenProvider, string purpose,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
if (UserTokenProvider == null)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException(Resources.NoTokenProvider);
|
|
||||||
}
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
return await UserTokenProvider.GenerateAsync(purpose, this, user, cancellationToken);
|
if (tokenProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tokenProvider));
|
||||||
|
}
|
||||||
|
if (!_tokenProviders.ContainsKey(tokenProvider))
|
||||||
|
{
|
||||||
|
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider));
|
||||||
|
}
|
||||||
|
return await _tokenProviders[tokenProvider].GenerateAsync(purpose, this, user, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a user two factor provider
|
/// Register a user token provider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="twoFactorProvider"></param>
|
/// <param name="twoFactorProvider"></param>
|
||||||
/// <param name="provider"></param>
|
/// <param name="provider"></param>
|
||||||
public virtual void RegisterTwoFactorProvider(string twoFactorProvider, IUserTokenProvider<TUser> provider)
|
public virtual void RegisterTokenProvider(IUserTokenProvider<TUser> provider)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
if (twoFactorProvider == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("twoFactorProvider");
|
|
||||||
}
|
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("provider");
|
throw new ArgumentNullException("provider");
|
||||||
}
|
}
|
||||||
TwoFactorProviders[twoFactorProvider] = provider;
|
_tokenProviders[provider.Name] = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1533,9 +1531,9 @@ namespace Microsoft.AspNet.Identity
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
var results = new List<string>();
|
var results = new List<string>();
|
||||||
foreach (var f in TwoFactorProviders)
|
foreach (var f in _tokenProviders)
|
||||||
{
|
{
|
||||||
if (await f.Value.IsValidProviderForUserAsync(this, user, cancellationToken))
|
if (await f.Value.CanGenerateTwoFactorTokenAsync(this, user, cancellationToken))
|
||||||
{
|
{
|
||||||
results.Add(f.Key);
|
results.Add(f.Key);
|
||||||
}
|
}
|
||||||
|
|
@ -1551,7 +1549,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual async Task<bool> VerifyTwoFactorTokenAsync(TUser user, string twoFactorProvider, string token,
|
public virtual async Task<bool> VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
@ -1559,14 +1557,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
if (!_factors.ContainsKey(twoFactorProvider))
|
if (!_tokenProviders.ContainsKey(tokenProvider))
|
||||||
{
|
{
|
||||||
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
|
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
|
||||||
Resources.NoTwoFactorProvider, twoFactorProvider));
|
Resources.NoTokenProvider, tokenProvider));
|
||||||
}
|
}
|
||||||
// Make sure the token is valid
|
// Make sure the token is valid
|
||||||
var provider = _factors[twoFactorProvider];
|
return await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user, cancellationToken);
|
||||||
return await provider.ValidateAsync(twoFactorProvider, token, this, user, cancellationToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1576,7 +1573,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="twoFactorProvider"></param>
|
/// <param name="twoFactorProvider"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual async Task<string> GenerateTwoFactorTokenAsync(TUser user, string twoFactorProvider,
|
public virtual async Task<string> GenerateTwoFactorTokenAsync(TUser user, string tokenProvider,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
@ -1584,23 +1581,23 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
if (!_factors.ContainsKey(twoFactorProvider))
|
if (!_tokenProviders.ContainsKey(tokenProvider))
|
||||||
{
|
{
|
||||||
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
|
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
|
||||||
Resources.NoTwoFactorProvider, twoFactorProvider));
|
Resources.NoTokenProvider, tokenProvider));
|
||||||
}
|
}
|
||||||
return await _factors[twoFactorProvider].GenerateAsync(twoFactorProvider, this, user, cancellationToken);
|
return await _tokenProviders[tokenProvider].GenerateAsync("TwoFactor", this, user, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Notify a user with a token from a specific user factor provider
|
/// Notify a user with a token from a specific user factor provider
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user"></param>
|
/// <param name="user"></param>
|
||||||
/// <param name="twoFactorProvider"></param>
|
/// <param name="tokenProvider"></param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual async Task<IdentityResult> NotifyTwoFactorTokenAsync(TUser user, string twoFactorProvider,
|
public virtual async Task<IdentityResult> NotifyTwoFactorTokenAsync(TUser user, string tokenProvider,
|
||||||
string token, CancellationToken cancellationToken = default(CancellationToken))
|
string token, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
@ -1608,12 +1605,16 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
if (!_factors.ContainsKey(twoFactorProvider))
|
if (tokenProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tokenProvider));
|
||||||
|
}
|
||||||
|
if (!_tokenProviders.ContainsKey(tokenProvider))
|
||||||
{
|
{
|
||||||
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
|
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
|
||||||
Resources.NoTwoFactorProvider, twoFactorProvider));
|
Resources.NoTokenProvider, tokenProvider));
|
||||||
}
|
}
|
||||||
await _factors[twoFactorProvider].NotifyAsync(token, this, user, cancellationToken);
|
await _tokenProviders[tokenProvider].NotifyAsync(token, this, user, cancellationToken);
|
||||||
return IdentityResult.Success;
|
return IdentityResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"version": "3.0.0-*",
|
"version": "3.0.0-*",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"Microsoft.AspNet.Http" : "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security" : "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Security.Cookies" : "1.0.0-*",
|
||||||
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
|
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
|
||||||
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
|
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
|
||||||
"Microsoft.Framework.OptionsModel": "1.0.0-*",
|
"Microsoft.Framework.OptionsModel": "1.0.0-*",
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
|
|
||||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<ProjectGuid>823453cc-5846-4d49-b343-15bc0074ca60</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<ActiveTargetFramework>net45</ActiveTargetFramework>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.AspNet.FeatureModel" : "1.0.0-*",
|
|
||||||
"Microsoft.AspNet.Http" : "1.0.0-*",
|
|
||||||
"Microsoft.AspNet.HttpFeature" : "1.0.0-*",
|
|
||||||
"Microsoft.AspNet.Identity" : "",
|
|
||||||
"Microsoft.AspNet.Identity.Authentication" : "",
|
|
||||||
"Microsoft.AspNet.PipelineCore" : "1.0.0-*",
|
|
||||||
"Microsoft.AspNet.RequestContainer" : "1.0.0-*",
|
|
||||||
"Microsoft.AspNet.Security" : "1.0.0-*",
|
|
||||||
"Microsoft.AspNet.Security.Cookies" : "1.0.0-*",
|
|
||||||
"Microsoft.AspNet.Testing" : "1.0.0-*",
|
|
||||||
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
|
|
||||||
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
|
|
||||||
"Microsoft.Framework.OptionsModel" : "1.0.0-*",
|
|
||||||
"Microsoft.Framework.Logging" : "1.0.0-*",
|
|
||||||
"System.Security.Claims" : "1.0.0-*",
|
|
||||||
"Xunit.KRunner": "1.0.0-*"
|
|
||||||
},
|
|
||||||
"code": "**\\*.cs;..\\Shared\\*.cs",
|
|
||||||
"frameworks": {
|
|
||||||
"aspnet50": {
|
|
||||||
"dependencies": {
|
|
||||||
"Moq" : "4.2.1312.1622",
|
|
||||||
"System.Runtime": "",
|
|
||||||
"System.Collections": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"commands": {
|
|
||||||
"test": "Xunit.KRunner"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
|
||||||
app.UseServices(services =>
|
app.UseServices(services =>
|
||||||
{
|
{
|
||||||
services.AddInstance(contextAccessor.Object);
|
services.AddInstance(contextAccessor.Object);
|
||||||
services.AddIdentity<ApplicationUser, IdentityRole>().AddInMemory().AddAuthentication();
|
services.AddIdentity<ApplicationUser, IdentityRole>().AddInMemory();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNet.Http" : "1.0.0-*",
|
"Microsoft.AspNet.Http" : "1.0.0-*",
|
||||||
"Microsoft.AspNet.Identity" : "",
|
"Microsoft.AspNet.Identity" : "",
|
||||||
"Microsoft.AspNet.Identity.Authentication" : "",
|
|
||||||
"Microsoft.AspNet.PipelineCore" : "1.0.0-*",
|
"Microsoft.AspNet.PipelineCore" : "1.0.0-*",
|
||||||
"Microsoft.AspNet.RequestContainer" : "1.0.0-*",
|
"Microsoft.AspNet.RequestContainer" : "1.0.0-*",
|
||||||
"Microsoft.AspNet.Security" : "1.0.0-*",
|
"Microsoft.AspNet.Security" : "1.0.0-*",
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.InMemory.Test
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddEntityFramework().AddInMemoryStore();
|
services.AddEntityFramework().AddInMemoryStore();
|
||||||
var store = new RoleStore<IdentityRole>(new InMemoryContext());
|
var store = new RoleStore<IdentityRole>(new InMemoryContext());
|
||||||
services.AddIdentity().AddRoleStore(() => store);
|
services.AddIdentity().AddRoleStore(store);
|
||||||
var provider = services.BuildServiceProvider();
|
var provider = services.BuildServiceProvider();
|
||||||
var manager = provider.GetService<RoleManager<IdentityRole>>();
|
var manager = provider.GetService<RoleManager<IdentityRole>>();
|
||||||
Assert.NotNull(manager);
|
Assert.NotNull(manager);
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.InMemory.Test
|
||||||
|
|
||||||
public static UserManager<IdentityUser> CreateManager(InMemoryContext context)
|
public static UserManager<IdentityUser> CreateManager(InMemoryContext context)
|
||||||
{
|
{
|
||||||
return MockHelpers.CreateManager<IdentityUser>(() => new InMemoryUserStore<IdentityUser>(context));
|
return MockHelpers.CreateManager<IdentityUser>(new InMemoryUserStore<IdentityUser>(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserManager<IdentityUser> CreateManager()
|
public static UserManager<IdentityUser> CreateManager()
|
||||||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.InMemory.Test
|
||||||
public static RoleManager<IdentityRole> CreateRoleManager(InMemoryContext context)
|
public static RoleManager<IdentityRole> CreateRoleManager(InMemoryContext context)
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddIdentity().AddRoleStore(() => new RoleStore<IdentityRole>(context));
|
services.AddIdentity().AddRoleStore(new RoleStore<IdentityRole>(context));
|
||||||
return services.BuildServiceProvider().GetService<RoleManager<IdentityRole>>();
|
return services.BuildServiceProvider().GetService<RoleManager<IdentityRole>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
||||||
{
|
{
|
||||||
context = CreateTestContext();
|
context = CreateTestContext();
|
||||||
}
|
}
|
||||||
return MockHelpers.CreateManager(() => new UserStore<TUser, TRole, ApplicationDbContext, TKey>((ApplicationDbContext)context));
|
return MockHelpers.CreateManager(new UserStore<TUser, TRole, ApplicationDbContext, TKey>((ApplicationDbContext)context));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override RoleManager<TRole> CreateRoleManager(object context = null)
|
protected override RoleManager<TRole> CreateRoleManager(object context = null)
|
||||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
||||||
context = CreateTestContext();
|
context = CreateTestContext();
|
||||||
}
|
}
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddIdentity<TUser, TRole>().AddRoleStore(() => new RoleStore<TRole, ApplicationDbContext, TKey>((ApplicationDbContext)context));
|
services.AddIdentity<TUser, TRole>().AddRoleStore(new RoleStore<TRole, ApplicationDbContext, TKey>((ApplicationDbContext)context));
|
||||||
return services.BuildServiceProvider().GetService<RoleManager<TRole>>();
|
return services.BuildServiceProvider().GetService<RoleManager<TRole>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
||||||
{
|
{
|
||||||
context = CreateTestContext();
|
context = CreateTestContext();
|
||||||
}
|
}
|
||||||
return MockHelpers.CreateManager(() => new ApplicationUserStore((ApplicationDbContext)context));
|
return MockHelpers.CreateManager(new ApplicationUserStore((ApplicationDbContext)context));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override RoleManager<GuidRole> CreateRoleManager(object context)
|
protected override RoleManager<GuidRole> CreateRoleManager(object context)
|
||||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
||||||
context = CreateTestContext();
|
context = CreateTestContext();
|
||||||
}
|
}
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddIdentity<GuidUser, GuidRole>().AddRoleStore(() => new ApplicationRoleStore((ApplicationDbContext)context));
|
services.AddIdentity<GuidUser, GuidRole>().AddRoleStore(new ApplicationRoleStore((ApplicationDbContext)context));
|
||||||
return services.BuildServiceProvider().GetService<RoleManager<GuidRole>>();
|
return services.BuildServiceProvider().GetService<RoleManager<GuidRole>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
||||||
|
|
||||||
public static UserManager<IdentityUser> CreateManager(DbContext context)
|
public static UserManager<IdentityUser> CreateManager(DbContext context)
|
||||||
{
|
{
|
||||||
return MockHelpers.CreateManager(() => new UserStore<IdentityUser>(context));
|
return MockHelpers.CreateManager(new UserStore<IdentityUser>(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override UserManager<IdentityUser> CreateManager(object context = null)
|
protected override UserManager<IdentityUser> CreateManager(object context = null)
|
||||||
|
|
@ -187,7 +187,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
||||||
public static RoleManager<IdentityRole> CreateRoleManager(IdentityDbContext context)
|
public static RoleManager<IdentityRole> CreateRoleManager(IdentityDbContext context)
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddIdentity().AddRoleStore(() => new RoleStore<IdentityRole>(context));
|
services.AddIdentity().AddRoleStore(new RoleStore<IdentityRole>(context));
|
||||||
return services.BuildServiceProvider().GetService<RoleManager<IdentityRole>>();
|
return services.BuildServiceProvider().GetService<RoleManager<IdentityRole>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
var validator = new UserValidator<IdentityUser>();
|
var validator = new UserValidator<IdentityUser>();
|
||||||
services.AddIdentity<IdentityUser>().AddUserValidator(() => validator);
|
services.AddIdentity<IdentityUser>().AddUserValidator(validator);
|
||||||
Assert.Equal(validator, services.BuildServiceProvider().GetService<IUserValidator<IdentityUser>>());
|
Assert.Equal(validator, services.BuildServiceProvider().GetService<IUserValidator<IdentityUser>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
var validator = new PasswordValidator<IdentityUser>();
|
var validator = new PasswordValidator<IdentityUser>();
|
||||||
services.AddIdentity<IdentityUser>().AddPasswordValidator(() => validator);
|
services.AddIdentity<IdentityUser>().AddPasswordValidator(validator);
|
||||||
Assert.Equal(validator, services.BuildServiceProvider().GetService<IPasswordValidator<IdentityUser>>());
|
Assert.Equal(validator, services.BuildServiceProvider().GetService<IPasswordValidator<IdentityUser>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
private static void CanOverride<TService>(TService instance)
|
private static void CanOverride<TService>(TService instance)
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.AddIdentity<IdentityUser>().AddInstance(() => instance);
|
services.AddIdentity<IdentityUser>().AddInstance(instance);
|
||||||
Assert.Equal(instance, services.BuildServiceProvider().GetService<TService>());
|
Assert.Equal(instance, services.BuildServiceProvider().GetService<TService>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public class PasswordsNegativeLengthSetup : IOptionsSetup<IdentityOptions>
|
public class PasswordsNegativeLengthSetup : IOptionsSetup<IdentityOptions>
|
||||||
{
|
{
|
||||||
public int Order { get { return 0; } }
|
public int Order { get { return 0; } }
|
||||||
|
public string Name { get; set; }
|
||||||
public void Setup(IdentityOptions options)
|
public void Setup(IdentityOptions options)
|
||||||
{
|
{
|
||||||
options.Password.RequiredLength = -1;
|
options.Password.RequiredLength = -1;
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
// Assert
|
// Assert
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("password", () => validator.ValidateAsync(null, null));
|
await Assert.ThrowsAsync<ArgumentNullException>("password", () => validator.ValidateAsync(null, null, null));
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("manager", () => validator.ValidateAsync("foo", null));
|
await Assert.ThrowsAsync<ArgumentNullException>("manager", () => validator.ValidateAsync(null, "foo", null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
manager.Options.Password.RequireNonLetterOrDigit = false;
|
manager.Options.Password.RequireNonLetterOrDigit = false;
|
||||||
manager.Options.Password.RequireLowercase = false;
|
manager.Options.Password.RequireLowercase = false;
|
||||||
manager.Options.Password.RequireDigit = false;
|
manager.Options.Password.RequireDigit = false;
|
||||||
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input, manager), error);
|
IdentityResultAssert.IsFailure(await valid.ValidateAsync(null, input, manager), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
manager.Options.Password.RequireNonLetterOrDigit = false;
|
manager.Options.Password.RequireNonLetterOrDigit = false;
|
||||||
manager.Options.Password.RequireLowercase = false;
|
manager.Options.Password.RequireLowercase = false;
|
||||||
manager.Options.Password.RequireDigit = false;
|
manager.Options.Password.RequireDigit = false;
|
||||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
|
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(null, input, manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
manager.Options.Password.RequireLowercase = false;
|
manager.Options.Password.RequireLowercase = false;
|
||||||
manager.Options.Password.RequireDigit = false;
|
manager.Options.Password.RequireDigit = false;
|
||||||
manager.Options.Password.RequiredLength = 0;
|
manager.Options.Password.RequiredLength = 0;
|
||||||
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input, manager),
|
IdentityResultAssert.IsFailure(await valid.ValidateAsync(null, input, manager),
|
||||||
"Passwords must have at least one non letter and non digit character.");
|
"Passwords must have at least one non letter and non digit character.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
manager.Options.Password.RequireLowercase = false;
|
manager.Options.Password.RequireLowercase = false;
|
||||||
manager.Options.Password.RequireDigit = false;
|
manager.Options.Password.RequireDigit = false;
|
||||||
manager.Options.Password.RequiredLength = 0;
|
manager.Options.Password.RequiredLength = 0;
|
||||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
|
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(null, input, manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -135,11 +135,11 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
}
|
}
|
||||||
if (errors.Count == 0)
|
if (errors.Count == 0)
|
||||||
{
|
{
|
||||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
|
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(null, input, manager));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input, manager), string.Join(" ", errors));
|
IdentityResultAssert.IsFailure(await valid.ValidateAsync(null, input, manager), string.Join(" ", errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Security;
|
using Microsoft.AspNet.Http.Security;
|
||||||
using Microsoft.AspNet.Identity.Test;
|
|
||||||
using Microsoft.AspNet.Security;
|
using Microsoft.AspNet.Security;
|
||||||
using Microsoft.AspNet.Security.Cookies;
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
@ -16,7 +15,7 @@ using Microsoft.Framework.OptionsModel;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.Authentication.Test
|
namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
public class SecurityStampTest
|
public class SecurityStampTest
|
||||||
{
|
{
|
||||||
|
|
@ -28,7 +27,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||||
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow });
|
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow });
|
||||||
var context = new CookieValidateIdentityContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
|
var context = new CookieValidateIdentityContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
|
||||||
await Assert.ThrowsAsync<Exception>(() => SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context));
|
await Assert.ThrowsAsync<Exception>(() => SecurityStampValidator.ValidateIdentityAsync(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -37,19 +36,22 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
public async Task OnValidateIdentityTestSuccess(bool isPersistent)
|
public async Task OnValidateIdentityTestSuccess(bool isPersistent)
|
||||||
{
|
{
|
||||||
var user = new IdentityUser("test");
|
var user = new IdentityUser("test");
|
||||||
var httpContext = new Mock<HttpContext>();
|
|
||||||
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||||
var authManager = new Mock<IAuthenticationManager>();
|
|
||||||
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.Zero };
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
|
var httpContext = new Mock<HttpContext>();
|
||||||
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
|
contextAccessor.Setup(a => a.Value).Returns(httpContext.Object);
|
||||||
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
||||||
authManager.Object, claimsManager.Object, options.Object);
|
contextAccessor.Object, claimsManager.Object, options.Object);
|
||||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(user).Verifiable();
|
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(user).Verifiable();
|
||||||
signInManager.Setup(s => s.SignInAsync(user, isPersistent, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
|
signInManager.Setup(s => s.SignInAsync(user, isPersistent, null, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
|
services.AddInstance(options.Object);
|
||||||
services.AddInstance(signInManager.Object);
|
services.AddInstance(signInManager.Object);
|
||||||
|
services.AddInstance<ISecurityStampValidator>(new SecurityStampValidator<IdentityUser>());
|
||||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||||
|
|
@ -60,7 +62,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
Assert.NotNull(context.Options);
|
Assert.NotNull(context.Options);
|
||||||
Assert.NotNull(context.Identity);
|
Assert.NotNull(context.Identity);
|
||||||
await
|
await
|
||||||
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
SecurityStampValidator.ValidateIdentityAsync(context);
|
||||||
Assert.NotNull(context.Identity);
|
Assert.NotNull(context.Identity);
|
||||||
signInManager.VerifyAll();
|
signInManager.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
@ -69,18 +71,21 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails()
|
public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails()
|
||||||
{
|
{
|
||||||
var user = new IdentityUser("test");
|
var user = new IdentityUser("test");
|
||||||
var httpContext = new Mock<HttpContext>();
|
|
||||||
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||||
var authManager = new Mock<IAuthenticationManager>();
|
|
||||||
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.Zero };
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
|
var httpContext = new Mock<HttpContext>();
|
||||||
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
|
contextAccessor.Setup(a => a.Value).Returns(httpContext.Object);
|
||||||
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
||||||
authManager.Object, claimsManager.Object, options.Object);
|
contextAccessor.Object, claimsManager.Object, options.Object);
|
||||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(null).Verifiable();
|
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(null).Verifiable();
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
|
services.AddInstance(options.Object);
|
||||||
services.AddInstance(signInManager.Object);
|
services.AddInstance(signInManager.Object);
|
||||||
|
services.AddInstance<ISecurityStampValidator>(new SecurityStampValidator<IdentityUser>());
|
||||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||||
|
|
@ -91,7 +96,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
Assert.NotNull(context.Options);
|
Assert.NotNull(context.Options);
|
||||||
Assert.NotNull(context.Identity);
|
Assert.NotNull(context.Identity);
|
||||||
await
|
await
|
||||||
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
SecurityStampValidator.ValidateIdentityAsync(context);
|
||||||
Assert.Null(context.Identity);
|
Assert.Null(context.Identity);
|
||||||
signInManager.VerifyAll();
|
signInManager.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
@ -102,16 +107,19 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var user = new IdentityUser("test");
|
var user = new IdentityUser("test");
|
||||||
var httpContext = new Mock<HttpContext>();
|
var httpContext = new Mock<HttpContext>();
|
||||||
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||||
var authManager = new Mock<IAuthenticationManager>();
|
|
||||||
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.Zero };
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
|
contextAccessor.Setup(a => a.Value).Returns(httpContext.Object);
|
||||||
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
||||||
authManager.Object, claimsManager.Object, options.Object);
|
contextAccessor.Object, claimsManager.Object, options.Object);
|
||||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(null).Verifiable();
|
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(null).Verifiable();
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
|
services.AddInstance(options.Object);
|
||||||
services.AddInstance(signInManager.Object);
|
services.AddInstance(signInManager.Object);
|
||||||
|
services.AddInstance<ISecurityStampValidator>(new SecurityStampValidator<IdentityUser>());
|
||||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||||
|
|
@ -122,7 +130,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
Assert.NotNull(context.Options);
|
Assert.NotNull(context.Options);
|
||||||
Assert.NotNull(context.Identity);
|
Assert.NotNull(context.Identity);
|
||||||
await
|
await
|
||||||
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
SecurityStampValidator.ValidateIdentityAsync(context);
|
||||||
Assert.Null(context.Identity);
|
Assert.Null(context.Identity);
|
||||||
signInManager.VerifyAll();
|
signInManager.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
@ -133,17 +141,20 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var user = new IdentityUser("test");
|
var user = new IdentityUser("test");
|
||||||
var httpContext = new Mock<HttpContext>();
|
var httpContext = new Mock<HttpContext>();
|
||||||
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||||
var authManager = new Mock<IAuthenticationManager>();
|
|
||||||
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.FromDays(1) };
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
|
contextAccessor.Setup(a => a.Value).Returns(httpContext.Object);
|
||||||
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
||||||
authManager.Object, claimsManager.Object, options.Object);
|
contextAccessor.Object, claimsManager.Object, options.Object);
|
||||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).Throws(new Exception("Shouldn't be called"));
|
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).Throws(new Exception("Shouldn't be called"));
|
||||||
signInManager.Setup(s => s.SignInAsync(user, false, CancellationToken.None)).Throws(new Exception("Shouldn't be called"));
|
signInManager.Setup(s => s.SignInAsync(user, false, null, CancellationToken.None)).Throws(new Exception("Shouldn't be called"));
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
|
services.AddInstance(options.Object);
|
||||||
services.AddInstance(signInManager.Object);
|
services.AddInstance(signInManager.Object);
|
||||||
|
services.AddInstance<ISecurityStampValidator>(new SecurityStampValidator<IdentityUser>());
|
||||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||||
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||||
|
|
@ -154,7 +165,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
Assert.NotNull(context.Options);
|
Assert.NotNull(context.Options);
|
||||||
Assert.NotNull(context.Identity);
|
Assert.NotNull(context.Identity);
|
||||||
await
|
await
|
||||||
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.FromDays(1)).Invoke(context);
|
SecurityStampValidator.ValidateIdentityAsync(context);
|
||||||
Assert.NotNull(context.Identity);
|
Assert.NotNull(context.Identity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,23 +3,21 @@
|
||||||
|
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Security;
|
using Microsoft.AspNet.Http.Security;
|
||||||
using Microsoft.AspNet.Identity.Test;
|
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Moq;
|
using Moq;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.Authentication.Test
|
namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
public class ApplicationUser : IdentityUser { }
|
public class SignManagerInTest
|
||||||
|
|
||||||
public class HttpSignInTest
|
|
||||||
{
|
{
|
||||||
#if ASPNET50
|
|
||||||
//[Theory]
|
//[Theory]
|
||||||
//[InlineData(true)]
|
//[InlineData(true)]
|
||||||
//[InlineData(false)]
|
//[InlineData(false)]
|
||||||
|
|
@ -71,11 +69,14 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
{
|
{
|
||||||
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<IdentityUser>(null, null, null, null));
|
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<IdentityUser>(null, null, null, null));
|
||||||
var userManager = MockHelpers.MockUserManager<IdentityUser>().Object;
|
var userManager = MockHelpers.MockUserManager<IdentityUser>().Object;
|
||||||
Assert.Throws<ArgumentNullException>("authenticationManager", () => new SignInManager<IdentityUser>(userManager, null, null, null));
|
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<IdentityUser>(userManager, null, null, null));
|
||||||
var authManager = new Mock<IAuthenticationManager>().Object;
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
Assert.Throws<ArgumentNullException>("claimsFactory", () => new SignInManager<IdentityUser>(userManager, authManager, null, null));
|
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<IdentityUser>(userManager, contextAccessor.Object, null, null));
|
||||||
|
var context = new Mock<HttpContext>();
|
||||||
|
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||||
|
Assert.Throws<ArgumentNullException>("claimsFactory", () => new SignInManager<IdentityUser>(userManager, contextAccessor.Object, null, null));
|
||||||
var claimsFactory = new Mock<IClaimsIdentityFactory<IdentityUser>>().Object;
|
var claimsFactory = new Mock<IClaimsIdentityFactory<IdentityUser>>().Object;
|
||||||
Assert.Throws<ArgumentNullException>("optionsAccessor", () => new SignInManager<IdentityUser>(userManager, authManager, claimsFactory, null));
|
Assert.Throws<ArgumentNullException>("optionsAccessor", () => new SignInManager<IdentityUser>(userManager, contextAccessor.Object, claimsFactory, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Mock fails in K (this works fine in net45)
|
//TODO: Mock fails in K (this works fine in net45)
|
||||||
|
|
@ -125,7 +126,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
||||||
|
|
@ -159,7 +160,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
claimsFactory.Setup(m => m.CreateAsync(user, identityOptions.ClaimsIdentity, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable();
|
claimsFactory.Setup(m => m.CreateAsync(user, identityOptions.ClaimsIdentity, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
||||||
|
|
@ -194,7 +195,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
||||||
|
|
@ -220,6 +221,9 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
{
|
{
|
||||||
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable();
|
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable();
|
||||||
}
|
}
|
||||||
|
IList<string> providers = new List<string>();
|
||||||
|
providers.Add("PhoneNumber");
|
||||||
|
manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user, CancellationToken.None)).Returns(Task.FromResult(providers)).Verifiable();
|
||||||
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
|
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
|
||||||
manager.Setup(m => m.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable();
|
manager.Setup(m => m.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable();
|
||||||
manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable();
|
manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable();
|
||||||
|
|
@ -235,7 +239,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), new ClaimsIdentityFactory<TestUser, TestRole>(manager.Object, roleManager.Object), options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, new ClaimsIdentityFactory<TestUser, TestRole>(manager.Object, roleManager.Object), options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
||||||
|
|
@ -269,7 +273,9 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var context = new Mock<HttpContext>();
|
var context = new Mock<HttpContext>();
|
||||||
var response = new Mock<HttpResponse>();
|
var response = new Mock<HttpResponse>();
|
||||||
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
||||||
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent), It.IsAny<ClaimsIdentity>())).Verifiable();
|
response.Setup(r => r.SignIn(
|
||||||
|
It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent),
|
||||||
|
It.Is<ClaimsIdentity>(i => i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider))).Verifiable();
|
||||||
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||||
|
|
@ -278,7 +284,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
claimsFactory.Setup(m => m.CreateAsync(user, identityOptions.ClaimsIdentity, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable();
|
claimsFactory.Setup(m => m.CreateAsync(user, identityOptions.ClaimsIdentity, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent);
|
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent);
|
||||||
|
|
@ -293,11 +299,24 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(true, true)]
|
[InlineData(true, true, true, true)]
|
||||||
[InlineData(false, true)]
|
[InlineData(true, true, false, true)]
|
||||||
[InlineData(true, false)]
|
[InlineData(true, false, true, true)]
|
||||||
[InlineData(false, false)]
|
[InlineData(true, false, false, true)]
|
||||||
public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout)
|
[InlineData(false, true, true, true)]
|
||||||
|
[InlineData(false, true, false, true)]
|
||||||
|
[InlineData(false, false, true, true)]
|
||||||
|
[InlineData(false, false, false, true)]
|
||||||
|
[InlineData(true, true, true, false)]
|
||||||
|
[InlineData(true, true, false, false)]
|
||||||
|
[InlineData(true, false, true, false)]
|
||||||
|
[InlineData(true, false, false, false)]
|
||||||
|
[InlineData(false, true, true, false)]
|
||||||
|
[InlineData(false, true, false, false)]
|
||||||
|
[InlineData(false, false, true, false)]
|
||||||
|
[InlineData(false, false, false, false)]
|
||||||
|
|
||||||
|
public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout, bool externalLogin, bool rememberClient)
|
||||||
{
|
{
|
||||||
// Setup
|
// Setup
|
||||||
var user = new TestUser { UserName = "Foo" };
|
var user = new TestUser { UserName = "Foo" };
|
||||||
|
|
@ -315,23 +334,43 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
manager.Setup(m => m.GetUserNameAsync(user, CancellationToken.None)).ReturnsAsync(user.UserName).Verifiable();
|
manager.Setup(m => m.GetUserNameAsync(user, CancellationToken.None)).ReturnsAsync(user.UserName).Verifiable();
|
||||||
var context = new Mock<HttpContext>();
|
var context = new Mock<HttpContext>();
|
||||||
var response = new Mock<HttpResponse>();
|
var response = new Mock<HttpResponse>();
|
||||||
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent), It.IsAny<ClaimsIdentity>())).Verifiable();
|
|
||||||
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
|
||||||
var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorUserIdAuthenticationType);
|
|
||||||
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
|
|
||||||
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
|
|
||||||
context.Setup(c => c.AuthenticateAsync(HttpAuthenticationManager.TwoFactorUserIdAuthenticationType)).ReturnsAsync(authResult).Verifiable();
|
|
||||||
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
var twoFactorInfo = new SignInManager<TestUser>.TwoFactorAuthenticationInfo { UserId = user.Id };
|
||||||
|
var loginProvider = "loginprovider";
|
||||||
|
var id = SignInManager<TestUser>.StoreTwoFactorInfo(user.Id, externalLogin ? loginProvider : null);
|
||||||
|
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
|
||||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||||
var claimsFactory = new ClaimsIdentityFactory<TestUser, TestRole>(manager.Object, roleManager.Object);
|
var claimsFactory = new ClaimsIdentityFactory<TestUser, TestRole>(manager.Object, roleManager.Object);
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory, options.Object);
|
if (externalLogin)
|
||||||
|
{
|
||||||
|
response.Setup(r => r.SignIn(
|
||||||
|
It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent),
|
||||||
|
It.Is<ClaimsIdentity>(i => i.FindFirstValue(ClaimTypes.NameIdentifier) == user.Id
|
||||||
|
&& i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider))).Verifiable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Setup(r => r.SignIn(
|
||||||
|
It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent),
|
||||||
|
It.Is<ClaimsIdentity>(i => i.FindFirstValue(ClaimTypes.NameIdentifier) == user.Id))).Verifiable();
|
||||||
|
}
|
||||||
|
if (rememberClient)
|
||||||
|
{
|
||||||
|
response.Setup(r => r.SignIn(
|
||||||
|
It.Is<AuthenticationProperties>(v => v.IsPersistent == true),
|
||||||
|
It.Is<ClaimsIdentity>(i => i.FindFirstValue(ClaimTypes.Name) == user.Id
|
||||||
|
&& i.AuthenticationType == ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType))).Verifiable();
|
||||||
|
}
|
||||||
|
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
||||||
|
context.Setup(c => c.AuthenticateAsync(ClaimsIdentityOptions.DefaultTwoFactorUserIdAuthenticationType)).ReturnsAsync(authResult).Verifiable();
|
||||||
|
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||||
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent);
|
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent, rememberClient);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.Success, result);
|
Assert.Equal(SignInStatus.Success, result);
|
||||||
|
|
@ -342,28 +381,39 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RememberClientStoresUserId()
|
public async Task RememberClientStoresUserId()
|
||||||
{
|
{
|
||||||
// Setup
|
// Setup
|
||||||
var user = new TestUser { UserName = "Foo" };
|
var user = new TestUser { UserName = "Foo" };
|
||||||
|
var manager = MockHelpers.MockUserManager<TestUser>();
|
||||||
var context = new Mock<HttpContext>();
|
var context = new Mock<HttpContext>();
|
||||||
var response = new Mock<HttpResponse>();
|
var response = new Mock<HttpResponse>();
|
||||||
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
|
||||||
response.Setup(r => r.SignIn(It.Is<ClaimsIdentity>(i => i.AuthenticationType == HttpAuthenticationManager.TwoFactorRememberedAuthenticationType))).Verifiable();
|
|
||||||
var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType);
|
|
||||||
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
|
|
||||||
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
|
|
||||||
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||||
var signInService = new HttpAuthenticationManager(contextAccessor.Object);
|
var claimsFactory = new ClaimsIdentityFactory<TestUser, TestRole>(manager.Object, roleManager.Object);
|
||||||
|
var identityOptions = new IdentityOptions();
|
||||||
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
|
|
||||||
|
manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id).Verifiable();
|
||||||
|
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
||||||
|
response.Setup(r => r.SignIn(
|
||||||
|
It.Is<AuthenticationProperties>(v => v.IsPersistent == true),
|
||||||
|
It.Is<ClaimsIdentity>(i => i.FindFirstValue(ClaimTypes.Name) == user.Id
|
||||||
|
&& i.AuthenticationType == ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType))).Verifiable();
|
||||||
|
contextAccessor.Setup(a => a.Value).Returns(context.Object).Verifiable();
|
||||||
|
options.Setup(a => a.Options).Returns(identityOptions).Verifiable();
|
||||||
|
|
||||||
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
signInService.RememberClient(user.Id);
|
await helper.RememberTwoFactorClientAsync(user);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
contextAccessor.VerifyAll();
|
contextAccessor.VerifyAll();
|
||||||
|
options.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -375,6 +425,9 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var user = new TestUser { UserName = "Foo" };
|
var user = new TestUser { UserName = "Foo" };
|
||||||
var manager = MockHelpers.MockUserManager<TestUser>();
|
var manager = MockHelpers.MockUserManager<TestUser>();
|
||||||
manager.Setup(m => m.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable();
|
manager.Setup(m => m.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable();
|
||||||
|
IList<string> providers = new List<string>();
|
||||||
|
providers.Add("PhoneNumber");
|
||||||
|
manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user, CancellationToken.None)).Returns(Task.FromResult(providers)).Verifiable();
|
||||||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||||
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
|
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
|
||||||
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable();
|
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable();
|
||||||
|
|
@ -385,20 +438,19 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var response = new Mock<HttpResponse>();
|
var response = new Mock<HttpResponse>();
|
||||||
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
|
||||||
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent), It.Is<ClaimsIdentity>(i => i.AuthenticationType == ClaimsIdentityOptions.DefaultAuthenticationType))).Verifiable();
|
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent), It.Is<ClaimsIdentity>(i => i.AuthenticationType == ClaimsIdentityOptions.DefaultAuthenticationType))).Verifiable();
|
||||||
var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType);
|
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType);
|
||||||
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
|
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
|
||||||
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
|
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
|
||||||
context.Setup(c => c.AuthenticateAsync(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType)).ReturnsAsync(authResult).Verifiable();
|
context.Setup(c => c.AuthenticateAsync(ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType)).ReturnsAsync(authResult).Verifiable();
|
||||||
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||||
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||||
var signInService = new HttpAuthenticationManager(contextAccessor.Object);
|
|
||||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions();
|
||||||
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object);
|
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object);
|
||||||
claimsFactory.Setup(m => m.CreateAsync(user, identityOptions.ClaimsIdentity, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType)).Verifiable();
|
claimsFactory.Setup(m => m.CreateAsync(user, identityOptions.ClaimsIdentity, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType)).Verifiable();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, signInService, claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
||||||
|
|
@ -431,7 +483,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
identityOptions.ClaimsIdentity.AuthenticationType = authenticationType;
|
identityOptions.ClaimsIdentity.AuthenticationType = authenticationType;
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
helper.SignOut();
|
helper.SignOut();
|
||||||
|
|
@ -459,7 +511,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
||||||
|
|
||||||
|
|
@ -482,7 +534,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
|
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
|
||||||
|
|
@ -516,7 +568,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
var identityOptions = new IdentityOptions();
|
var identityOptions = new IdentityOptions();
|
||||||
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
var options = new Mock<IOptionsAccessor<IdentityOptions>>();
|
||||||
options.Setup(a => a.Options).Returns(identityOptions);
|
options.Setup(a => a.Options).Returns(identityOptions);
|
||||||
var helper = new SignInManager<TestUser>(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object, options.Object);
|
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
|
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
|
||||||
|
|
@ -525,6 +577,5 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
Assert.Equal(SignInStatus.LockedOut, result);
|
Assert.Equal(SignInStatus.LockedOut, result);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -24,14 +24,14 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public TestManager(IUserStore<TestUser> store, IOptionsAccessor<IdentityOptions> optionsAccessor,
|
public TestManager(IUserStore<TestUser> store, IOptionsAccessor<IdentityOptions> optionsAccessor,
|
||||||
IPasswordHasher<TestUser> passwordHasher, IUserValidator<TestUser> userValidator,
|
IPasswordHasher<TestUser> passwordHasher, IUserValidator<TestUser> userValidator,
|
||||||
IPasswordValidator<TestUser> passwordValidator)
|
IPasswordValidator<TestUser> passwordValidator)
|
||||||
: base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null) { }
|
: base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EnsureDefaultServicesDefaultsWithStoreWorks()
|
public void EnsureDefaultServicesDefaultsWithStoreWorks()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection {IdentityServices.GetDefaultUserServices<TestUser>()};
|
var services = new ServiceCollection {IdentityServices.GetDefaultServices<TestUser, IdentityRole>()};
|
||||||
services.AddInstance<IOptionsAccessor<IdentityOptions>>(new OptionsAccessor<IdentityOptions>(null));
|
services.Add(OptionsServices.GetDefaultServices());
|
||||||
services.AddTransient<IUserStore<TestUser>, NoopUserStore>();
|
services.AddTransient<IUserStore<TestUser>, NoopUserStore>();
|
||||||
services.AddTransient<TestManager>();
|
services.AddTransient<TestManager>();
|
||||||
var manager = services.BuildServiceProvider().GetService<TestManager>();
|
var manager = services.BuildServiceProvider().GetService<TestManager>();
|
||||||
|
|
@ -464,10 +464,11 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task TokenMethodsThrowWithNoTokenProvider()
|
public async Task TokenMethodsThrowWithNoTokenProvider()
|
||||||
{
|
{
|
||||||
var manager = MockHelpers.TestUserManager(new NoopUserStore());
|
var manager = MockHelpers.TestUserManager(new NoopUserStore());
|
||||||
|
var user = new TestUser();
|
||||||
await Assert.ThrowsAsync<NotSupportedException>(
|
await Assert.ThrowsAsync<NotSupportedException>(
|
||||||
async () => await manager.GenerateUserTokenAsync(null, null));
|
async () => await manager.GenerateUserTokenAsync(user, "bogus", null));
|
||||||
await Assert.ThrowsAsync<NotSupportedException>(
|
await Assert.ThrowsAsync<NotSupportedException>(
|
||||||
async () => await manager.VerifyUserTokenAsync(null, null, null));
|
async () => await manager.VerifyUserTokenAsync(user, "bogus", null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -581,13 +582,13 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var passwordValidator = new PasswordValidator<TestUser>();
|
var passwordValidator = new PasswordValidator<TestUser>();
|
||||||
|
|
||||||
Assert.Throws<ArgumentNullException>("store",
|
Assert.Throws<ArgumentNullException>("store",
|
||||||
() => new UserManager<TestUser>(null, null, null, null, null, null));
|
() => new UserManager<TestUser>(null, null, null, null, null, null, null));
|
||||||
Assert.Throws<ArgumentNullException>("optionsAccessor",
|
Assert.Throws<ArgumentNullException>("optionsAccessor",
|
||||||
() => new UserManager<TestUser>(store, null, null, null, null, null));
|
() => new UserManager<TestUser>(store, null, null, null, null, null, null));
|
||||||
Assert.Throws<ArgumentNullException>("passwordHasher",
|
Assert.Throws<ArgumentNullException>("passwordHasher",
|
||||||
() => new UserManager<TestUser>(store, optionsAccessor, null, null, null, null));
|
() => new UserManager<TestUser>(store, optionsAccessor, null, null, null, null, null));
|
||||||
|
|
||||||
var manager = new UserManager<TestUser>(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null);
|
var manager = new UserManager<TestUser>(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null);
|
||||||
|
|
||||||
Assert.Throws<ArgumentNullException>("value", () => manager.PasswordHasher = null);
|
Assert.Throws<ArgumentNullException>("value", () => manager.PasswordHasher = null);
|
||||||
Assert.Throws<ArgumentNullException>("value", () => manager.Options = null);
|
Assert.Throws<ArgumentNullException>("value", () => manager.Options = null);
|
||||||
|
|
@ -608,9 +609,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("providerKey",
|
await Assert.ThrowsAsync<ArgumentNullException>("providerKey",
|
||||||
async () => await manager.RemoveLoginAsync(null, "", null));
|
async () => await manager.RemoveLoginAsync(null, "", null));
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("email", async () => await manager.FindByEmailAsync(null));
|
await Assert.ThrowsAsync<ArgumentNullException>("email", async () => await manager.FindByEmailAsync(null));
|
||||||
Assert.Throws<ArgumentNullException>("twoFactorProvider",
|
Assert.Throws<ArgumentNullException>("provider", () => manager.RegisterTokenProvider(null));
|
||||||
() => manager.RegisterTwoFactorProvider(null, null));
|
|
||||||
Assert.Throws<ArgumentNullException>("provider", () => manager.RegisterTwoFactorProvider("bogus", null));
|
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("roles", async () => await manager.AddToRolesAsync(new TestUser(), null));
|
await Assert.ThrowsAsync<ArgumentNullException>("roles", async () => await manager.AddToRolesAsync(new TestUser(), null));
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("roles", async () => await manager.RemoveFromRolesAsync(new TestUser(), null));
|
await Assert.ThrowsAsync<ArgumentNullException>("roles", async () => await manager.RemoveFromRolesAsync(new TestUser(), null));
|
||||||
}
|
}
|
||||||
|
|
@ -619,7 +618,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task MethodsFailWithUnknownUserTest()
|
public async Task MethodsFailWithUnknownUserTest()
|
||||||
{
|
{
|
||||||
var manager = MockHelpers.TestUserManager(new EmptyStore());
|
var manager = MockHelpers.TestUserManager(new EmptyStore());
|
||||||
manager.UserTokenProvider = new NoOpTokenProvider();
|
manager.RegisterTokenProvider(new NoOpTokenProvider());
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||||
async () => await manager.GetUserNameAsync(null));
|
async () => await manager.GetUserNameAsync(null));
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||||
|
|
@ -697,7 +696,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||||
async () => await manager.GetValidTwoFactorProvidersAsync(null));
|
async () => await manager.GetValidTwoFactorProvidersAsync(null));
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||||
async () => await manager.VerifyUserTokenAsync(null, null, null));
|
async () => await manager.VerifyUserTokenAsync(null, null, null, null));
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||||
async () => await manager.AccessFailedAsync(null));
|
async () => await manager.AccessFailedAsync(null));
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||||
|
|
@ -763,7 +762,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
public const string ErrorMessage = "I'm Bad.";
|
public const string ErrorMessage = "I'm Bad.";
|
||||||
|
|
||||||
public Task<IdentityResult> ValidateAsync(string password, UserManager<TUser> manager, CancellationToken cancellationToken = default(CancellationToken))
|
public Task<IdentityResult> ValidateAsync(TUser user, string password, UserManager<TUser> manager, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
|
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
|
||||||
}
|
}
|
||||||
|
|
@ -1007,6 +1006,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
||||||
private class NoOpTokenProvider : IUserTokenProvider<TestUser>
|
private class NoOpTokenProvider : IUserTokenProvider<TestUser>
|
||||||
{
|
{
|
||||||
|
public string Name { get; } = "Noop";
|
||||||
|
|
||||||
public Task<string> GenerateAsync(string purpose, UserManager<TestUser> manager, TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
public Task<string> GenerateAsync(string purpose, UserManager<TestUser> manager, TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
return Task.FromResult("Test");
|
return Task.FromResult("Test");
|
||||||
|
|
@ -1023,7 +1024,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> IsValidProviderForUserAsync(UserManager<TestUser> manager, TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TestUser> manager, TestUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
public static class MockHelpers
|
public static class MockHelpers
|
||||||
{
|
{
|
||||||
public static UserManager<TUser> CreateManager<TUser>(Func<IUserStore<TUser>> storeFunc) where TUser : class
|
public static UserManager<TUser> CreateManager<TUser>(IUserStore<TUser> store) where TUser : class
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
services.Add(OptionsServices.GetDefaultServices());
|
services.Add(OptionsServices.GetDefaultServices());
|
||||||
services.AddIdentity<TUser>().AddUserStore(storeFunc);
|
services.AddIdentity<TUser>().AddUserStore(store);
|
||||||
services.SetupOptions<IdentityOptions>(options =>
|
services.SetupOptions<IdentityOptions>(options =>
|
||||||
{
|
{
|
||||||
options.Password.RequireDigit = false;
|
options.Password.RequireDigit = false;
|
||||||
|
|
@ -39,7 +39,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
new PasswordHasher<TUser>(),
|
new PasswordHasher<TUser>(),
|
||||||
new UserValidator<TUser>(),
|
new UserValidator<TUser>(),
|
||||||
new PasswordValidator<TUser>(),
|
new PasswordValidator<TUser>(),
|
||||||
new UpperInvariantUserNameNormalizer());
|
new UpperInvariantUserNameNormalizer(),
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Mock<RoleManager<TRole>> MockRoleManager<TRole>() where TRole : class
|
public static Mock<RoleManager<TRole>> MockRoleManager<TRole>() where TRole : class
|
||||||
|
|
@ -58,7 +59,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var options = new OptionsAccessor<IdentityOptions>(null);
|
var options = new OptionsAccessor<IdentityOptions>(null);
|
||||||
var validator = new Mock<UserValidator<TUser>>();
|
var validator = new Mock<UserValidator<TUser>>();
|
||||||
var userManager = new UserManager<TUser>(store, options, new PasswordHasher<TUser>(),
|
var userManager = new UserManager<TUser>(store, options, new PasswordHasher<TUser>(),
|
||||||
validator.Object, new PasswordValidator<TUser>(), new UpperInvariantUserNameNormalizer());
|
validator.Object, new PasswordValidator<TUser>(), new UpperInvariantUserNameNormalizer(), null);
|
||||||
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>(), CancellationToken.None))
|
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>(), CancellationToken.None))
|
||||||
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
|
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
|
||||||
return userManager;
|
return userManager;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Microsoft.AspNet.Security.DataProtection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.Test
|
namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
|
|
@ -502,6 +503,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
||||||
private class StaticTokenProvider : IUserTokenProvider<TUser>
|
private class StaticTokenProvider : IUserTokenProvider<TUser>
|
||||||
{
|
{
|
||||||
|
public string Name { get; } = "Static";
|
||||||
|
|
||||||
public Task<string> GenerateAsync(string purpose, UserManager<TUser> manager,
|
public Task<string> GenerateAsync(string purpose, UserManager<TUser> manager,
|
||||||
TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
|
|
@ -519,7 +522,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> IsValidProviderForUserAsync(UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
@ -534,7 +537,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task CanResetPasswordWithStaticTokenProvider()
|
public async Task CanResetPasswordWithStaticTokenProvider()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
manager.UserTokenProvider = new StaticTokenProvider();
|
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||||
|
manager.Options.PasswordResetTokenProvider = "Static";
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
const string password = "password";
|
const string password = "password";
|
||||||
const string newPassword = "newpassword";
|
const string newPassword = "newpassword";
|
||||||
|
|
@ -553,7 +557,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider()
|
public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
manager.UserTokenProvider = new StaticTokenProvider();
|
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||||
|
manager.Options.PasswordResetTokenProvider = "Static";
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
const string password = "password";
|
const string password = "password";
|
||||||
const string newPassword = "newpassword";
|
const string newPassword = "newpassword";
|
||||||
|
|
@ -574,7 +579,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken()
|
public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
manager.UserTokenProvider = new StaticTokenProvider();
|
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||||
|
manager.Options.PasswordResetTokenProvider = "Static";
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
const string password = "password";
|
const string password = "password";
|
||||||
const string newPassword = "newpassword";
|
const string newPassword = "newpassword";
|
||||||
|
|
@ -591,23 +597,24 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider()
|
public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
manager.UserTokenProvider = new StaticTokenProvider();
|
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
var user2 = CreateTestUser();
|
var user2 = CreateTestUser();
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
|
||||||
var token = await manager.GenerateUserTokenAsync("test", user);
|
var token = await manager.GenerateUserTokenAsync(user, "Static", "test");
|
||||||
Assert.True(await manager.VerifyUserTokenAsync(user, "test", token));
|
Assert.True(await manager.VerifyUserTokenAsync(user, "Static", "test", token));
|
||||||
Assert.False(await manager.VerifyUserTokenAsync(user, "test2", token));
|
Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test2", token));
|
||||||
Assert.False(await manager.VerifyUserTokenAsync(user, "test", token + "a"));
|
Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test", token + "a"));
|
||||||
Assert.False(await manager.VerifyUserTokenAsync(user2, "test", token));
|
Assert.False(await manager.VerifyUserTokenAsync(user2, "Static", "test", token));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CanConfirmEmailWithStaticToken()
|
public async Task CanConfirmEmailWithStaticToken()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
manager.UserTokenProvider = new StaticTokenProvider();
|
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||||
|
manager.Options.EmailConfirmationTokenProvider = "Static";
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
Assert.False(user.EmailConfirmed);
|
Assert.False(user.EmailConfirmed);
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
|
|
@ -623,7 +630,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken()
|
public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
manager.UserTokenProvider = new StaticTokenProvider();
|
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||||
|
manager.Options.EmailConfirmationTokenProvider = "Static";
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
Assert.False(user.EmailConfirmed);
|
Assert.False(user.EmailConfirmed);
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
|
|
@ -631,20 +639,22 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
Assert.False(await manager.IsEmailConfirmedAsync(user));
|
Assert.False(await manager.IsEmailConfirmedAsync(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Can't reenable til we have a SecurityStamp linked token provider
|
[Fact]
|
||||||
//[Fact]
|
public async Task ConfirmTokenFailsAfterPasswordChange()
|
||||||
//public async Task ConfirmTokenFailsAfterPasswordChange()
|
{
|
||||||
//{
|
var manager = CreateManager();
|
||||||
// var manager = CreateManager();
|
manager.RegisterTokenProvider(new DataProtectorTokenProvider<TUser>(new DataProtectionTokenProviderOptions(),
|
||||||
// var user = new TUser() { UserName = "test" };
|
DataProtectionProvider.CreateFromDpapi().CreateProtector("ASP.NET Identity")));
|
||||||
// Assert.False(user.EmailConfirmed);
|
manager.Options.EmailConfirmationTokenProvider = "DataProtection";
|
||||||
// IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
|
var user = new TUser() { UserName = "test" };
|
||||||
// var token = await manager.GenerateEmailConfirmationTokenAsync(user);
|
Assert.False(user.EmailConfirmed);
|
||||||
// Assert.NotNull(token);
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
|
||||||
// IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword"));
|
var token = await manager.GenerateEmailConfirmationTokenAsync(user);
|
||||||
// IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token.");
|
Assert.NotNull(token);
|
||||||
// Assert.False(await manager.IsEmailConfirmedAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword"));
|
||||||
//}
|
IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token.");
|
||||||
|
Assert.False(await manager.IsEmailConfirmedAsync(user));
|
||||||
|
}
|
||||||
|
|
||||||
// Lockout tests
|
// Lockout tests
|
||||||
|
|
||||||
|
|
@ -837,7 +847,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
public const string ErrorMessage = "I'm Bad.";
|
public const string ErrorMessage = "I'm Bad.";
|
||||||
|
|
||||||
public Task<IdentityResult> ValidateAsync(string password, UserManager<TUser> manager, CancellationToken cancellationToken = default(CancellationToken))
|
public Task<IdentityResult> ValidateAsync(TUser user, string password, UserManager<TUser> manager, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
|
return Task.FromResult(IdentityResult.Failed(ErrorMessage));
|
||||||
}
|
}
|
||||||
|
|
@ -1220,73 +1230,16 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2));
|
Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EmailTokenProvider : IUserTokenProvider<TUser>
|
|
||||||
{
|
|
||||||
public Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return Task.FromResult(MakeToken(purpose));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager,
|
|
||||||
TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return Task.FromResult(token == MakeToken(purpose));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task NotifyAsync(string token, UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return manager.SendEmailAsync(user, token, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsValidProviderForUserAsync(UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return !string.IsNullOrEmpty(await manager.GetEmailAsync(user));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string MakeToken(string purpose)
|
|
||||||
{
|
|
||||||
return "email:" + purpose;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SmsTokenProvider : IUserTokenProvider<TUser>
|
|
||||||
{
|
|
||||||
public Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return Task.FromResult(MakeToken(purpose));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager,
|
|
||||||
TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return Task.FromResult(token == MakeToken(purpose));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task NotifyAsync(string token, UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return manager.SendSmsAsync(user, token, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsValidProviderForUserAsync(UserManager<TUser> manager, TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return !string.IsNullOrEmpty(await manager.GetPhoneNumberAsync(user, cancellationToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string MakeToken(string purpose)
|
|
||||||
{
|
|
||||||
return "sms:" + purpose;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CanEmailTwoFactorToken()
|
public async Task CanEmailTwoFactorToken()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
var messageService = new TestMessageService();
|
var messageService = new TestMessageService();
|
||||||
manager.EmailService = messageService;
|
manager.EmailService = messageService;
|
||||||
const string factorId = "EmailCode";
|
const string factorId = "Email";
|
||||||
manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider());
|
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>());
|
||||||
var user = new TUser() { UserName = "EmailCodeTest", Email = "foo@foo.com" };
|
var user = new TUser() { UserName = "EmailCodeTest", Email = "foo@foo.com" };
|
||||||
|
user.EmailConfirmed = true;
|
||||||
const string password = "password";
|
const string password = "password";
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
|
||||||
var stamp = user.SecurityStamp;
|
var stamp = user.SecurityStamp;
|
||||||
|
|
@ -1296,8 +1249,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
Assert.Null(messageService.Message);
|
Assert.Null(messageService.Message);
|
||||||
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
||||||
Assert.NotNull(messageService.Message);
|
Assert.NotNull(messageService.Message);
|
||||||
Assert.Equal(token, messageService.Message.Subject);
|
Assert.Equal("Your security code is: "+token, messageService.Message.Body);
|
||||||
Assert.Equal(token, messageService.Message.Body);
|
|
||||||
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
|
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1310,7 +1262,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
await
|
await
|
||||||
ExceptionAssert.ThrowsAsync<NotSupportedException>(
|
ExceptionAssert.ThrowsAsync<NotSupportedException>(
|
||||||
async () => await manager.NotifyTwoFactorTokenAsync(user, "Bogus", "token"),
|
async () => await manager.NotifyTwoFactorTokenAsync(user, "Bogus", "token"),
|
||||||
"No IUserTwoFactorProvider for 'Bogus' is registered.");
|
"No IUserTokenProvider named 'Bogus' is registered.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1319,12 +1271,15 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
var messageService = new TestMessageService();
|
var messageService = new TestMessageService();
|
||||||
manager.EmailService = messageService;
|
manager.EmailService = messageService;
|
||||||
const string factorId = "EmailCode";
|
const string factorId = "EmailTestCode";
|
||||||
manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider<TUser>
|
const string subject = "Custom subject";
|
||||||
|
const string body = "Your code is {0}!";
|
||||||
|
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>(new EmailTokenProviderOptions
|
||||||
{
|
{
|
||||||
Subject = "Security Code",
|
Name = factorId,
|
||||||
BodyFormat = "Your code is: {0}"
|
Subject = subject,
|
||||||
});
|
BodyFormat = body
|
||||||
|
}));
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
user.Email = user.UserName + "@foo.com";
|
user.Email = user.UserName + "@foo.com";
|
||||||
const string password = "password";
|
const string password = "password";
|
||||||
|
|
@ -1336,8 +1291,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
Assert.Null(messageService.Message);
|
Assert.Null(messageService.Message);
|
||||||
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
||||||
Assert.NotNull(messageService.Message);
|
Assert.NotNull(messageService.Message);
|
||||||
Assert.Equal("Security Code", messageService.Message.Subject);
|
Assert.Equal(subject, messageService.Message.Subject);
|
||||||
Assert.Equal("Your code is: " + token, messageService.Message.Body);
|
Assert.Equal(string.Format(body, token), messageService.Message.Body);
|
||||||
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
|
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1345,10 +1300,11 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task EmailFactorFailsAfterSecurityStampChangeTest()
|
public async Task EmailFactorFailsAfterSecurityStampChangeTest()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
const string factorId = "EmailCode";
|
string factorId = "Email"; //default
|
||||||
manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider<TUser>());
|
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>());
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
user.Email = user.UserName + "@foo.com";
|
user.Email = user.UserName + "@foo.com";
|
||||||
|
user.EmailConfirmed = true;
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
var stamp = user.SecurityStamp;
|
var stamp = user.SecurityStamp;
|
||||||
Assert.NotNull(stamp);
|
Assert.NotNull(stamp);
|
||||||
|
|
@ -1407,8 +1363,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
var messageService = new TestMessageService();
|
var messageService = new TestMessageService();
|
||||||
manager.SmsService = messageService;
|
manager.SmsService = messageService;
|
||||||
const string factorId = "PhoneCode";
|
const string factorId = "Phone"; // default
|
||||||
manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider());
|
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
user.PhoneNumber = "4251234567";
|
user.PhoneNumber = "4251234567";
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
|
|
@ -1419,7 +1375,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
Assert.Null(messageService.Message);
|
Assert.Null(messageService.Message);
|
||||||
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
||||||
Assert.NotNull(messageService.Message);
|
Assert.NotNull(messageService.Message);
|
||||||
Assert.Equal(token, messageService.Message.Body);
|
Assert.Equal("Your security code is: "+token, messageService.Message.Body);
|
||||||
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
|
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1429,11 +1385,12 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
var messageService = new TestMessageService();
|
var messageService = new TestMessageService();
|
||||||
manager.SmsService = messageService;
|
manager.SmsService = messageService;
|
||||||
const string factorId = "PhoneCode";
|
const string factorId = "PhoneTestFactors";
|
||||||
manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider<TUser>
|
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>(new PhoneNumberTokenProviderOptions
|
||||||
{
|
{
|
||||||
|
Name = factorId,
|
||||||
MessageFormat = "Your code is: {0}"
|
MessageFormat = "Your code is: {0}"
|
||||||
});
|
}));
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
user.PhoneNumber = "4251234567";
|
user.PhoneNumber = "4251234567";
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
|
|
@ -1454,7 +1411,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
const string error = "No IUserTwoFactorProvider for 'bogus' is registered.";
|
const string error = "No IUserTokenProvider named 'bogus' is registered.";
|
||||||
await
|
await
|
||||||
ExceptionAssert.ThrowsAsync<NotSupportedException>(
|
ExceptionAssert.ThrowsAsync<NotSupportedException>(
|
||||||
() => manager.GenerateTwoFactorTokenAsync(user, "bogus"), error);
|
() => manager.GenerateTwoFactorTokenAsync(user, "bogus"), error);
|
||||||
|
|
@ -1477,35 +1434,39 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task CanGetValidTwoFactor()
|
public async Task CanGetValidTwoFactor()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
manager.RegisterTwoFactorProvider("phone", new SmsTokenProvider());
|
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||||
manager.RegisterTwoFactorProvider("email", new EmailTokenProvider());
|
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>());
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
var factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
var factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
||||||
Assert.NotNull(factors);
|
Assert.NotNull(factors);
|
||||||
Assert.True(!factors.Any());
|
Assert.False(factors.Any());
|
||||||
IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111"));
|
IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111"));
|
||||||
|
user.PhoneNumberConfirmed = true;
|
||||||
|
await manager.UpdateAsync(user);
|
||||||
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
||||||
Assert.NotNull(factors);
|
Assert.NotNull(factors);
|
||||||
Assert.True(factors.Count() == 1);
|
Assert.Equal(1, factors.Count());
|
||||||
Assert.Equal("phone", factors[0]);
|
Assert.Equal("Phone", factors[0]);
|
||||||
IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com"));
|
IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com"));
|
||||||
|
user.EmailConfirmed = true;
|
||||||
|
await manager.UpdateAsync(user);
|
||||||
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
||||||
Assert.NotNull(factors);
|
Assert.NotNull(factors);
|
||||||
Assert.True(factors.Count() == 2);
|
Assert.Equal(2, factors.Count());
|
||||||
IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null));
|
IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null));
|
||||||
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
||||||
Assert.NotNull(factors);
|
Assert.NotNull(factors);
|
||||||
Assert.True(factors.Count() == 1);
|
Assert.Equal(1, factors.Count());
|
||||||
Assert.Equal("phone", factors[0]);
|
Assert.Equal("Phone", factors[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task PhoneFactorFailsAfterSecurityStampChangeTest()
|
public async Task PhoneFactorFailsAfterSecurityStampChangeTest()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
var factorId = "PhoneCode";
|
var factorId = "Phone"; // default
|
||||||
manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider<TUser>());
|
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
user.PhoneNumber = "4251234567";
|
user.PhoneNumber = "4251234567";
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
|
|
@ -1521,26 +1482,25 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task VerifyTokenFromWrongTokenProviderFails()
|
public async Task VerifyTokenFromWrongTokenProviderFails()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
manager.RegisterTwoFactorProvider("PhoneCode", new SmsTokenProvider());
|
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||||
manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider());
|
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>());
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
user.PhoneNumber = "4251234567";
|
user.PhoneNumber = "4251234567";
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
var token = await manager.GenerateTwoFactorTokenAsync(user, "PhoneCode");
|
var token = await manager.GenerateTwoFactorTokenAsync(user, "Phone");
|
||||||
Assert.NotNull(token);
|
Assert.NotNull(token);
|
||||||
Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "EmailCode", token));
|
Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Email", token));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task VerifyWithWrongSmsTokenFails()
|
public async Task VerifyWithWrongSmsTokenFails()
|
||||||
{
|
{
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
const string factorId = "PhoneCode";
|
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||||
manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider());
|
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
user.PhoneNumber = "4251234567";
|
user.PhoneNumber = "4251234567";
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, "bogus"));
|
Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Phone", "bogus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TUser> GenerateUsers(string userNamePrefix, int count)
|
public List<TUser> GenerateUsers(string userNamePrefix, int count)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue