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
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.21722.1
|
||||
VisualStudioVersion = 14.0.22013.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}"
|
||||
EndProject
|
||||
|
|
@ -11,12 +11,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity",
|
|||
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}"
|
||||
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}"
|
||||
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}"
|
||||
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}"
|
||||
|
|
@ -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.Build.0 = 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.Build.0 = 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.Build.0 = 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.Build.0 = Debug|Any CPU
|
||||
{65161409-C4C4-4D63-A73B-231FCFF4D503}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -139,9 +115,7 @@ Global
|
|||
GlobalSection(NestedProjects) = preSolution
|
||||
{1729302E-A58E-4652-B639-5B6B68DA2748} = {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}
|
||||
{823453CC-5846-4D49-B343-15BC0074CA60} = {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}
|
||||
{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 IdentitySample.Models;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IdentitySample.Models
|
||||
{
|
||||
[Authorize]
|
||||
public class AccountController : Controller
|
||||
public class AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
||||
: Controller
|
||||
{
|
||||
public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
UserManager = userManager;
|
||||
SignInManager = signInManager;
|
||||
}
|
||||
public UserManager<ApplicationUser> UserManager { get; } = userManager;
|
||||
|
||||
public UserManager<ApplicationUser> UserManager { get; private set; }
|
||||
|
||||
public SignInManager<ApplicationUser> SignInManager { get; private set; }
|
||||
public SignInManager<ApplicationUser> SignInManager { get; } = signInManager;
|
||||
|
||||
//
|
||||
// GET: /Account/Login
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Login(string returnUrl = null)
|
||||
{
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
ViewBag.LoginProviders = Context.GetExternalAuthenticationTypes().ToList();
|
||||
return View();
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +45,8 @@ namespace IdentitySample.Models
|
|||
case SignInStatus.LockedOut:
|
||||
ModelState.AddModelError("", "User is locked out, try again later.");
|
||||
return View(model);
|
||||
case SignInStatus.RequiresVerification:
|
||||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
|
||||
case SignInStatus.Failure:
|
||||
default:
|
||||
ModelState.AddModelError("", "Invalid username or password.");
|
||||
|
|
@ -58,6 +60,7 @@ namespace IdentitySample.Models
|
|||
|
||||
//
|
||||
// GET: /Account/Register
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Register()
|
||||
{
|
||||
|
|
@ -73,54 +76,17 @@ namespace IdentitySample.Models
|
|||
{
|
||||
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);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
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>");
|
||||
ViewBag.Link = callbackUrl;
|
||||
return View("DisplayEmail");
|
||||
}
|
||||
AddErrors(result);
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
|
|
@ -137,6 +103,277 @@ namespace IdentitySample.Models
|
|||
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
|
||||
|
||||
private void AddErrors(IdentityResult result)
|
||||
|
|
@ -152,12 +389,6 @@ namespace IdentitySample.Models
|
|||
return await UserManager.FindByIdAsync(Context.User.Identity.GetUserId());
|
||||
}
|
||||
|
||||
public enum ManageMessageId
|
||||
{
|
||||
ChangePasswordSuccess,
|
||||
Error
|
||||
}
|
||||
|
||||
private IActionResult RedirectToLocal(string returnUrl)
|
||||
{
|
||||
if (Url.IsLocalUrl(returnUrl))
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ namespace IdentitySample.Models
|
|||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public IActionResult About()
|
||||
{
|
||||
ViewBag.Message = "Your app description page.";
|
||||
|
|
@ -17,6 +18,7 @@ namespace IdentitySample.Models
|
|||
return View();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Contact()
|
||||
{
|
||||
ViewBag.Message = "Your contact page.";
|
||||
|
|
|
|||
|
|
@ -1,26 +1,24 @@
|
|||
using IdentitySample.Models;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using IdentitySample.Models;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IdentitySample
|
||||
{
|
||||
[Authorize]
|
||||
public class ManageController : Controller
|
||||
public class ManageController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
||||
: Controller
|
||||
{
|
||||
public ManageController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
UserManager = userManager;
|
||||
SignInManager = signInManager;
|
||||
}
|
||||
public UserManager<ApplicationUser> UserManager { get; } = userManager;
|
||||
|
||||
public UserManager<ApplicationUser> UserManager { get; private set; }
|
||||
|
||||
public SignInManager<ApplicationUser> SignInManager { get; private set; }
|
||||
public SignInManager<ApplicationUser> SignInManager { get; } = signInManager;
|
||||
|
||||
//
|
||||
// GET: /Account/Index
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index(ManageMessageId? message = null)
|
||||
{
|
||||
ViewBag.StatusMessage =
|
||||
|
|
@ -46,6 +44,7 @@ namespace IdentitySample
|
|||
|
||||
//
|
||||
// GET: /Account/RemoveLogin
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> RemoveLogin()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
|
|
@ -92,7 +91,6 @@ namespace IdentitySample
|
|||
return View(model);
|
||||
}
|
||||
// Generate the token and send it
|
||||
#if ASPNET50
|
||||
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), model.Number);
|
||||
if (UserManager.SmsService != null)
|
||||
{
|
||||
|
|
@ -103,19 +101,19 @@ namespace IdentitySample
|
|||
};
|
||||
await UserManager.SmsService.SendAsync(message);
|
||||
}
|
||||
#endif
|
||||
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Manage/RememberBrowser
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RememberBrowser()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
await SignInManager.RememberTwoFactorClient(user);
|
||||
await SignInManager.RememberTwoFactorClientAsync(user);
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
}
|
||||
return RedirectToAction("Index", "Manage");
|
||||
|
|
@ -124,6 +122,7 @@ namespace IdentitySample
|
|||
//
|
||||
// POST: /Manage/ForgetBrowser
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> ForgetBrowser()
|
||||
{
|
||||
await SignInManager.ForgetTwoFactorClientAsync();
|
||||
|
|
@ -133,6 +132,7 @@ namespace IdentitySample
|
|||
//
|
||||
// POST: /Manage/EnableTFA
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> EnableTFA()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
|
|
@ -148,6 +148,7 @@ namespace IdentitySample
|
|||
//
|
||||
// POST: /Manage/DisableTFA
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DisableTFA()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
|
|
@ -161,14 +162,13 @@ namespace IdentitySample
|
|||
|
||||
//
|
||||
// GET: /Account/VerifyPhoneNumber
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber)
|
||||
{
|
||||
// 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.
|
||||
#if ASPNET50
|
||||
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber);
|
||||
ViewBag.Status = "For DEMO purposes only, the current code is " + code;
|
||||
#endif
|
||||
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
|
||||
}
|
||||
|
||||
|
|
@ -199,6 +199,7 @@ namespace IdentitySample
|
|||
|
||||
//
|
||||
// GET: /Account/RemovePhoneNumber
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> RemovePhoneNumber()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
|
|
@ -216,6 +217,7 @@ namespace IdentitySample
|
|||
|
||||
//
|
||||
// GET: /Manage/ChangePassword
|
||||
[HttpGet]
|
||||
public IActionResult ChangePassword()
|
||||
{
|
||||
return View();
|
||||
|
|
@ -248,6 +250,7 @@ namespace IdentitySample
|
|||
|
||||
//
|
||||
// GET: /Manage/SetPassword
|
||||
[HttpGet]
|
||||
public IActionResult SetPassword()
|
||||
{
|
||||
return View();
|
||||
|
|
@ -279,55 +282,63 @@ namespace IdentitySample
|
|||
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
|
||||
//public async Task<IActionResult> ManageLogins(ManageMessageId? message)
|
||||
//{
|
||||
// ViewBag.StatusMessage =
|
||||
// message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
|
||||
// : 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 = 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
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public IActionResult LinkLogin(string provider)
|
||||
{
|
||||
// Request a redirect to the external login provider to link a login for the current user
|
||||
var redirectUrl = Url.Action("LinkLoginCallback", "Manage");
|
||||
var properties = Context.ConfigureExternalAuthenticationProperties(provider, redirectUrl, User.Identity.GetUserId());
|
||||
return new ChallengeResult(provider, properties);
|
||||
}
|
||||
|
||||
////
|
||||
//// POST: /Manage/LinkLogin
|
||||
//[HttpPost]
|
||||
//[ValidateAntiForgeryToken]
|
||||
//public IActionResult LinkLogin(string provider)
|
||||
//{
|
||||
// // Request a redirect to the external login provider to link a login for the current user
|
||||
// return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
|
||||
//}
|
||||
|
||||
////
|
||||
//// GET: /Manage/LinkLoginCallback
|
||||
//public async Task<ActionResult> LinkLoginCallback()
|
||||
//{
|
||||
// var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
|
||||
// if (loginInfo == null)
|
||||
// {
|
||||
// return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
|
||||
// }
|
||||
// var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
|
||||
// return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
|
||||
//}
|
||||
//
|
||||
// GET: /Manage/LinkLoginCallback
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> LinkLoginCallback()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
var info = await Context.GetExternalLoginInfo(User.Identity.GetUserId());
|
||||
if (info == null)
|
||||
{
|
||||
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
|
||||
}
|
||||
var result = await UserManager.AddLoginAsync(user, info);
|
||||
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
|
||||
return RedirectToAction("ManageLogins", new { Message = message });
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
// Used for XSRF protection when adding external logins
|
||||
//private const string XsrfKey = "XsrfId";
|
||||
|
||||
private void AddErrors(IdentityResult result)
|
||||
{
|
||||
|
|
@ -350,6 +361,7 @@ namespace IdentitySample
|
|||
public enum ManageMessageId
|
||||
{
|
||||
AddPhoneSuccess,
|
||||
AddLoginSuccess,
|
||||
ChangePasswordSuccess,
|
||||
SetTwoFactorSuccess,
|
||||
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
|
||||
{
|
||||
public class ExternalLoginConfirmationViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "User name")]
|
||||
public string UserName { get; set; }
|
||||
[Display(Name = "Email")]
|
||||
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]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Current password")]
|
||||
public string OldPassword { get; set; }
|
||||
public string Provider { 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]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "New password")]
|
||||
public string NewPassword { get; set; }
|
||||
[Display(Name = "Password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm new password")]
|
||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||
[Display(Name = "Confirm password")]
|
||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||
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
|
||||
|
|
@ -46,8 +81,8 @@ namespace IdentitySample.Models
|
|||
public class RegisterViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "User name")]
|
||||
public string UserName { get; set; }
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required]
|
||||
[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 System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
|
@ -14,11 +15,11 @@ namespace IdentitySample.Models
|
|||
public bool BrowserRemembered { get; set; }
|
||||
}
|
||||
|
||||
//public class ManageLoginsViewModel
|
||||
//{
|
||||
// public IList<UserLoginInfo> CurrentLogins { get; set; }
|
||||
// public IList<AuthenticationDescription> OtherLogins { get; set; }
|
||||
//}
|
||||
public class ManageLoginsViewModel
|
||||
{
|
||||
public IList<UserLoginInfo> CurrentLogins { get; set; }
|
||||
public IList<AuthenticationDescription> OtherLogins { get; set; }
|
||||
}
|
||||
|
||||
public class FactorViewModel
|
||||
{
|
||||
|
|
@ -84,5 +85,4 @@ namespace IdentitySample.Models
|
|||
public string SelectedProvider { 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.Diagnostics;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Identity.Authentication;
|
||||
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.Framework.ConfigurationModel;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using IdentitySample.Models;
|
||||
using System;
|
||||
|
||||
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)
|
||||
{
|
||||
/* 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 =>
|
||||
{
|
||||
// Add EF services to the services container
|
||||
|
|
@ -34,21 +98,52 @@ namespace IdentitySamples
|
|||
// Configure DbContext
|
||||
services.SetupOptions<IdentityDbContextOptions>(options =>
|
||||
{
|
||||
options.DefaultAdminUserName = configuration.Get("DefaultAdminUsername");
|
||||
options.DefaultAdminPassword = configuration.Get("DefaultAdminPassword");
|
||||
options.UseSqlServer(configuration.Get("Data:IdentityConnection:ConnectionString"));
|
||||
options.DefaultAdminUserName = Configuration.Get("DefaultAdminUsername");
|
||||
options.DefaultAdminPassword = Configuration.Get("DefaultAdminPassword");
|
||||
options.UseSqlServer(Configuration.Get("Data:IdentityConnection:ConnectionString"));
|
||||
});
|
||||
|
||||
// Add Identity services to the services container
|
||||
services.AddIdentitySqlServer<ApplicationDbContext, ApplicationUser>()
|
||||
.AddAuthentication()
|
||||
.SetupOptions(options =>
|
||||
{
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequireLowercase = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireNonLetterOrDigit = false;
|
||||
});
|
||||
services.AddDefaultIdentity<ApplicationDbContext, ApplicationUser, IdentityRole>(Configuration);
|
||||
|
||||
// move this into add identity along with the
|
||||
//service.SetupOptions<ExternalAuthenticationOptions>(options => options.SignInAsAuthenticationType = "External")
|
||||
|
||||
services.SetupOptions<IdentityOptions>(options =>
|
||||
{
|
||||
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
|
||||
services.AddMvc();
|
||||
|
|
@ -62,19 +157,13 @@ namespace IdentitySamples
|
|||
// Add static files to the request pipeline
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Setup identity cookie middleware
|
||||
// Add cookie-based authentication to the request pipeline
|
||||
app.UseCookieAuthentication(new CookieAuthenticationOptions
|
||||
{
|
||||
AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType,
|
||||
LoginPath = new PathString("/Account/Login"),
|
||||
Notifications = new CookieAuthenticationNotifications
|
||||
{
|
||||
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUser>(
|
||||
validateInterval: TimeSpan.FromMinutes(0))
|
||||
}
|
||||
});
|
||||
app.UseIdentity();
|
||||
|
||||
app.UseTwoFactorSignInCookies();
|
||||
app.UseGoogleAuthentication();
|
||||
app.UseFacebookAuthentication();
|
||||
app.UseTwitterAuthentication();
|
||||
|
||||
// Add MVC to the request pipeline
|
||||
app.UseMvc(routes =>
|
||||
|
|
@ -85,8 +174,15 @@ namespace IdentitySamples
|
|||
defaults: new { controller = "Home", action = "Index" });
|
||||
});
|
||||
|
||||
//Populates the MusicStore sample data
|
||||
//Populates the Admin user and role
|
||||
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
|
||||
@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.
|
||||
|
|
@ -44,8 +47,39 @@
|
|||
</div>
|
||||
</div>
|
||||
<p>
|
||||
@Html.ActionLink("Register", "Register") if you don't have a local account.
|
||||
@Html.ActionLink("Register a new user?", "Register")
|
||||
</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>
|
||||
</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 />
|
||||
@Html.ValidationSummary()
|
||||
<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">
|
||||
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
|
||||
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
|
||||
</div>
|
||||
</div>
|
||||
<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="col-md-8">
|
||||
<p>
|
||||
@if (Model.HasPassword)
|
||||
{
|
||||
@Html.ActionLink("Change your password", "ChangePassword")
|
||||
}
|
||||
else
|
||||
{
|
||||
@Html.ActionLink("Pick a password", "SetPassword")
|
||||
}
|
||||
@(Model.HasPassword ? Html.ActionLink("Change your password", "ChangePassword")
|
||||
: Html.ActionLink("Set your password", "SetPassword"))
|
||||
</p>
|
||||
<p>
|
||||
Phone Number: @(Model.PhoneNumber ?? "None") [
|
||||
|
|
@ -39,39 +33,47 @@
|
|||
</p>
|
||||
@if (Model.TwoFactor)
|
||||
{
|
||||
<form method="post" action="/Manage/DisableTFA">
|
||||
<p>
|
||||
Two factor is currently enabled:
|
||||
<input type="submit" value="Disable" class="btn btn-default"/>
|
||||
</p>
|
||||
</form>
|
||||
using (Html.BeginForm("DisableTFA", "Manage", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<p>
|
||||
Two factor is currently enabled:
|
||||
<input type="submit" value="Disable" class="btn btn-default"/>
|
||||
</p>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<form method="post" action="/Manage/EnableTFA">
|
||||
using (Html.BeginForm("EnableTFA", "Manage", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<p>
|
||||
Two factor is currently disabled:
|
||||
<input type="submit" value="Enable" class="btn btn-default"/>
|
||||
</p>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
@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>
|
||||
Browser is curently remembered for two factor:
|
||||
<input type="submit" value="Forget Browser" class="btn btn-default" />
|
||||
</p>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<form method="post" action="/Manage/RememberBrowser">
|
||||
using (Html.BeginForm("RememberBrowser", "Manage", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<p>
|
||||
Browser is curently not remembered for two factor:
|
||||
<input type="submit" value="Remember Browser" class="btn btn-default"/>
|
||||
</p>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@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.
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
@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">
|
||||
You do not have a local username/password for this site. Add a local
|
||||
|
|
@ -9,7 +14,7 @@
|
|||
{
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<h4>Create Local Login</h4>
|
||||
<h4>Set your password</h4>
|
||||
<hr />
|
||||
@Html.ValidationSummary("", new { @class = "text-danger" })
|
||||
<div class="form-group">
|
||||
|
|
@ -31,5 +36,8 @@
|
|||
</div>
|
||||
}
|
||||
@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",
|
||||
"version": "1.0.0-*",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Http": "1.0.0-*",
|
||||
"Microsoft.AspNet.Server.IIS": "1.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.Diagnostics": "1.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.Facebook": "1.0.0-*",
|
||||
"Microsoft.AspNet.Security.Google": "1.0.0-*",
|
||||
"Microsoft.AspNet.Security.Twitter": "1.0.0-*",
|
||||
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
|
||||
"EntityFramework.SqlServer": "7.0.0-*",
|
||||
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
|
||||
"Microsoft.Framework.OptionsModel": "1.0.0-*"
|
||||
},
|
||||
"commands": {
|
||||
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5002",
|
||||
"run": "run server.urls=http://localhost:5003"
|
||||
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:41532",
|
||||
"run": "run server.urls=http://localhost:41532"
|
||||
},
|
||||
"frameworks": {
|
||||
"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>
|
||||
{
|
||||
public IdentityDbContext() { }
|
||||
public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
|
||||
public IdentityDbContext(DbContextOptions options) : base(options) { }
|
||||
public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
|
||||
}
|
||||
|
||||
|
|
@ -21,8 +19,6 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
|||
where TUser : IdentityUser
|
||||
{
|
||||
public IdentityDbContext() { }
|
||||
public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
|
||||
public IdentityDbContext(DbContextOptions options) : base(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 IdentityDbContext() { }
|
||||
public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
|
||||
public IdentityDbContext(DbContextOptions options) : base(options) { }
|
||||
public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
|
|
@ -48,26 +42,26 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
|||
builder.Entity<TUser>(b =>
|
||||
{
|
||||
b.Key(u => u.Id);
|
||||
b.Property(u => u.UserName);
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
builder.Entity<TRole>(b =>
|
||||
{
|
||||
b.Key(r => r.Id);
|
||||
b.Property(r => r.Name);
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
builder.Entity<IdentityUserClaim<TKey>>(b =>
|
||||
{
|
||||
b.Key(uc => uc.Id);
|
||||
b.ManyToOne<TUser>().ForeignKey(uc => uc.UserId);
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
|
||||
|
|
@ -76,8 +70,8 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
|||
var userClaimType = builder.Model.GetEntityType(typeof(IdentityUserClaim<TKey>));
|
||||
var roleClaimType = builder.Model.GetEntityType(typeof(IdentityRoleClaim<TKey>));
|
||||
var userRoleType = builder.Model.GetEntityType(typeof(IdentityUserRole<TKey>));
|
||||
var ucfk = userClaimType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userClaimType.GetProperty("UserId") });
|
||||
userType.AddNavigation(new Navigation(ucfk, "Claims", false));
|
||||
//var ucfk = userClaimType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userClaimType.GetProperty("UserId") });
|
||||
//userType.AddNavigation(new Navigation(ucfk, "Claims", false));
|
||||
//userClaimType.AddNavigation(new Navigation(ucfk, "User", true));
|
||||
//var urfk = userRoleType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userRoleType.GetProperty("UserId") });
|
||||
//userType.AddNavigation(new Navigation(urfk, "Roles", false));
|
||||
|
|
@ -99,10 +93,10 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
|||
|
||||
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");
|
||||
});
|
||||
//.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.SqlServer;
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.Framework.ConfigurationModel;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Framework.DependencyInjection
|
||||
{
|
||||
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>();
|
||||
}
|
||||
|
||||
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentitySqlServer<TContext>(this ServiceCollection services)
|
||||
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentitySqlServer<TContext>(this IServiceCollection services)
|
||||
where TContext : DbContext
|
||||
{
|
||||
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 TContext : DbContext
|
||||
{
|
||||
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 TRole : IdentityRole, new()
|
||||
where TContext : DbContext
|
||||
|
|
|
|||
|
|
@ -12,43 +12,39 @@ using Microsoft.Data.Entity;
|
|||
|
||||
namespace Microsoft.AspNet.Identity.SqlServer
|
||||
{
|
||||
public class RoleStore<TRole> : RoleStore<TRole, DbContext, string> where TRole : IdentityRole
|
||||
{
|
||||
public RoleStore(DbContext context) : base(context) { }
|
||||
}
|
||||
public class RoleStore<TRole>(DbContext context) : RoleStore<TRole, DbContext, string>(context)
|
||||
where TRole : IdentityRole
|
||||
{ }
|
||||
|
||||
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 TContext : DbContext
|
||||
{
|
||||
public RoleStore(TContext context) : base(context) { }
|
||||
}
|
||||
{ }
|
||||
|
||||
public class RoleStore<TRole, TContext, TKey> :
|
||||
IQueryableRoleStore<TRole>,
|
||||
public class RoleStore<TRole, TContext, TKey>(TContext context) :
|
||||
IQueryableRoleStore<TRole>,
|
||||
IRoleClaimStore<TRole>
|
||||
where TRole : IdentityRole<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TContext : DbContext
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
public RoleStore(TContext context)
|
||||
// Primary constructor
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException("context");
|
||||
}
|
||||
Context = context;
|
||||
AutoSaveChanges = true;
|
||||
}
|
||||
|
||||
public TContext Context { get; private set; }
|
||||
private bool _disposed;
|
||||
|
||||
|
||||
public TContext Context { get; } = context;
|
||||
|
||||
/// <summary>
|
||||
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
|
||||
/// </summary>
|
||||
public bool AutoSaveChanges { get; set; }
|
||||
public bool AutoSaveChanges { get; set; } = true;
|
||||
|
||||
private async Task SaveChanges(CancellationToken cancellationToken)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,25 +13,20 @@ using Microsoft.Data.Entity;
|
|||
|
||||
namespace Microsoft.AspNet.Identity.SqlServer
|
||||
{
|
||||
public class UserStore : UserStore<IdentityUser>
|
||||
{
|
||||
public UserStore(DbContext context) : base(context) { }
|
||||
}
|
||||
public class UserStore(DbContext context) : UserStore<IdentityUser>(context)
|
||||
{ }
|
||||
|
||||
public class UserStore<TUser> : UserStore<TUser, IdentityRole, DbContext> where TUser : IdentityUser, new()
|
||||
{
|
||||
public UserStore(DbContext context) : base(context) { }
|
||||
}
|
||||
public class UserStore<TUser>(DbContext context) : UserStore<TUser, IdentityRole, DbContext>(context)
|
||||
where TUser : IdentityUser, new()
|
||||
{ }
|
||||
|
||||
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 TRole : IdentityRole, new()
|
||||
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>,
|
||||
IUserRoleStore<TUser>,
|
||||
IUserClaimStore<TUser>,
|
||||
|
|
@ -47,24 +42,22 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
|||
where TContext : DbContext
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
public UserStore(TContext context)
|
||||
// Primary constructor
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException("context");
|
||||
}
|
||||
Context = context;
|
||||
AutoSaveChanges = true;
|
||||
}
|
||||
|
||||
public TContext Context { get; private set; }
|
||||
private bool _disposed;
|
||||
|
||||
public TContext Context { get; } = context;
|
||||
|
||||
/// <summary>
|
||||
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
|
||||
/// </summary>
|
||||
public bool AutoSaveChanges { get; set; }
|
||||
public bool AutoSaveChanges { get; set; } = true;
|
||||
|
||||
private Task SaveChanges(CancellationToken cancellationToken)
|
||||
{
|
||||
|
|
@ -507,7 +500,7 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
|||
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();
|
||||
ThrowIfDisposed();
|
||||
|
|
@ -516,9 +509,11 @@ namespace Microsoft.AspNet.Identity.SqlServer
|
|||
throw new ArgumentNullException("user");
|
||||
}
|
||||
// todo: ensure logins loaded
|
||||
IList<UserLoginInfo> result = user.Logins
|
||||
.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToList();
|
||||
return Task.FromResult(result);
|
||||
//IList<UserLoginInfo> result = user.Logins
|
||||
// .Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToList();
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// ClaimType used for the security stamp by default
|
||||
/// </summary>
|
||||
public static readonly string DefaultSecurityStampClaimType = "AspNet.Identity.SecurityStamp";
|
||||
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()
|
||||
{
|
||||
AuthenticationType = DefaultAuthenticationType;
|
||||
RoleClaimType = ClaimTypes.Role;
|
||||
SecurityStampClaimType = DefaultSecurityStampClaimType;
|
||||
UserIdClaimType = ClaimTypes.NameIdentifier;
|
||||
UserNameClaimType = ClaimTypes.Name;
|
||||
}
|
||||
|
||||
public string AuthenticationType { get; set; }
|
||||
public string AuthenticationType { get; set; } = DefaultAuthenticationType;
|
||||
|
||||
/// <summary>
|
||||
/// Claim type used for role claims
|
||||
/// </summary>
|
||||
public string RoleClaimType { get; set; }
|
||||
public string RoleClaimType { get; set; } = ClaimTypes.Role;
|
||||
|
||||
/// <summary>
|
||||
/// Claim type used for the user name
|
||||
/// </summary>
|
||||
public string UserNameClaimType { get; set; }
|
||||
public string UserNameClaimType { get; set; } = ClaimTypes.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Claim type used for the user id
|
||||
/// </summary>
|
||||
public string UserIdClaimType { get; set; }
|
||||
public string UserIdClaimType { get; set; } = ClaimTypes.NameIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Claim type used for the user security stamp
|
||||
/// </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
|
||||
{
|
||||
/// <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> : TotpSecurityStampBasedTokenProvider<TUser>
|
||||
where TUser : class
|
||||
public class EmailTokenProviderOptions
|
||||
{
|
||||
private string _body;
|
||||
private string _subject;
|
||||
public string Name { get; set; } = Resources.DefaultEmailTokenProviderName;
|
||||
|
||||
/// <summary>
|
||||
/// Email subject used when a token notification is received
|
||||
/// </summary>
|
||||
public string Subject
|
||||
{
|
||||
get { return _subject ?? string.Empty; }
|
||||
set { _subject = value; }
|
||||
}
|
||||
public string Subject { get; set; } = "Security Code";
|
||||
|
||||
/// <summary>
|
||||
/// Format string which will be used for the email body, it will be passed the token for the first parameter
|
||||
/// </summary>
|
||||
public string BodyFormat
|
||||
{
|
||||
get { return _body ?? "{0}"; }
|
||||
set { _body = value; }
|
||||
}
|
||||
public string BodyFormat { get; set; } = "Your security code is: {0}";
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// True if the user has an email set
|
||||
|
|
@ -39,7 +36,7 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <param name="manager"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <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))
|
||||
{
|
||||
var email = await manager.GetEmailAsync(user, cancellationToken);
|
||||
|
|
@ -74,7 +71,7 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Validate the item
|
||||
/// Validate the password for the user
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<IdentityResult> ValidateAsync(string password, UserManager<TUser> manager,
|
||||
Task<IdentityResult> ValidateAsync(TUser user, string password, UserManager<TUser> manager,
|
||||
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.
|
||||
// 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.Tasks;
|
||||
|
||||
|
|
@ -12,6 +11,11 @@ namespace Microsoft.AspNet.Identity
|
|||
/// </summary>
|
||||
public interface IUserTokenProvider<TUser> where TUser : class
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the token provider
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Generate a token for a user
|
||||
/// </summary>
|
||||
|
|
@ -47,13 +51,13 @@ namespace Microsoft.AspNet.Identity
|
|||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <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>
|
||||
/// <param name="manager"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> IsValidProviderForUserAsync(UserManager<TUser> manager, TUser user,
|
||||
Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
||||
|
|
@ -9,39 +9,44 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -66,11 +71,5 @@ namespace Microsoft.AspNet.Identity
|
|||
Services.AddScoped<TManager>();
|
||||
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.
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -8,24 +13,58 @@ namespace Microsoft.AspNet.Identity
|
|||
/// </summary>
|
||||
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();
|
||||
User = new UserOptions();
|
||||
Password = new PasswordOptions();
|
||||
Lockout = new LockoutOptions();
|
||||
SignIn = new SignInOptions();
|
||||
}
|
||||
AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType,
|
||||
LoginPath = new PathString("/Account/Login"),
|
||||
Notifications = new CookieAuthenticationNotifications
|
||||
{
|
||||
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
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="roleName"></param>
|
||||
public IdentityRole(string roleName)
|
||||
: this()
|
||||
public IdentityRole(string roleName) : this()
|
||||
{
|
||||
Name = roleName;
|
||||
}
|
||||
|
|
@ -57,12 +47,12 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <summary>
|
||||
/// Navigation property for users in the role
|
||||
/// </summary>
|
||||
public virtual ICollection<IdentityUserRole<TKey>> Users { get; private set; }
|
||||
public virtual ICollection<IdentityUserRole<TKey>> Users { get; private set; } = new List<IdentityUserRole<TKey>>();
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property for claims in the role
|
||||
/// </summary>
|
||||
public virtual ICollection<IdentityRoleClaim<TKey>> Claims { get; private set; }
|
||||
public virtual ICollection<IdentityRoleClaim<TKey>> Claims { get; private set; } = new List<IdentityRoleClaim<TKey>>();
|
||||
|
||||
/// <summary>
|
||||
/// Role id
|
||||
|
|
@ -74,4 +64,4 @@ namespace Microsoft.AspNet.Identity
|
|||
/// </summary>
|
||||
public virtual string Name { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,52 +4,64 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.Framework.ConfigurationModel;
|
||||
using Microsoft.AspNet.Security.DataProtection;
|
||||
|
||||
namespace Microsoft.Framework.DependencyInjection
|
||||
{
|
||||
public static class IdentityServiceCollectionExtensions
|
||||
{
|
||||
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentity(this ServiceCollection services,
|
||||
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentity(this IServiceCollection services,
|
||||
IConfiguration identityConfig)
|
||||
{
|
||||
services.SetupOptions<IdentityOptions>(identityConfig);
|
||||
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>();
|
||||
}
|
||||
|
||||
public static IdentityBuilder<TUser, TRole> AddIdentity<TUser, TRole>(this ServiceCollection services,
|
||||
IConfiguration identityConfig)
|
||||
public static IdentityBuilder<TUser, TRole> AddIdentity<TUser, TRole>(this IServiceCollection services,
|
||||
IConfiguration identityConfig = null)
|
||||
where TUser : class
|
||||
where TRole : class
|
||||
{
|
||||
services.SetupOptions<IdentityOptions>(identityConfig);
|
||||
return services.AddIdentity<TUser, TRole>();
|
||||
}
|
||||
|
||||
public static IdentityBuilder<TUser, TRole> AddIdentity<TUser, TRole>(this ServiceCollection services)
|
||||
where TUser : class
|
||||
where TRole : class
|
||||
{
|
||||
services.Add(IdentityServices.GetDefaultUserServices<TUser>());
|
||||
services.Add(IdentityServices.GetDefaultRoleServices<TRole>());
|
||||
if (identityConfig != null)
|
||||
{
|
||||
services.SetupOptions<IdentityOptions>(identityConfig);
|
||||
}
|
||||
services.Add(IdentityServices.GetDefaultServices<TUser, TRole>(identityConfig));
|
||||
services.AddScoped<UserManager<TUser>>();
|
||||
services.AddScoped<SignInManager<TUser>>();
|
||||
services.AddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
|
||||
services.AddScoped<RoleManager<TRole>>();
|
||||
services.AddScoped<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser, TRole>>();
|
||||
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
|
||||
{
|
||||
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)
|
||||
where TUser : class
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,34 +13,23 @@ namespace Microsoft.AspNet.Identity
|
|||
/// </summary>
|
||||
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());
|
||||
}
|
||||
|
||||
public static IEnumerable<IServiceDescriptor> GetDefaultUserServices<TUser>(IConfiguration configuration)
|
||||
where TUser : class
|
||||
{
|
||||
var describe = new ServiceDescriber(configuration);
|
||||
|
||||
ServiceDescriber describe;
|
||||
if (config == null)
|
||||
{
|
||||
describe = new ServiceDescriber();
|
||||
}
|
||||
else
|
||||
{
|
||||
describe = new ServiceDescriber(config);
|
||||
}
|
||||
yield return describe.Transient<IUserValidator<TUser>, UserValidator<TUser>>();
|
||||
yield return describe.Transient<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
|
||||
yield return describe.Transient<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
|
||||
yield return describe.Transient<IUserNameNormalizer, UpperInvariantUserNameNormalizer>();
|
||||
|
||||
// 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>());
|
||||
yield return describe.Transient<IRoleValidator<TRole>, RoleValidator<TRole>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,16 +19,8 @@ namespace Microsoft.AspNet.Identity
|
|||
}
|
||||
}
|
||||
|
||||
public class IdentityUser<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
public class IdentityUser<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()
|
||||
{
|
||||
UserName = userName;
|
||||
|
|
@ -91,16 +83,16 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <summary>
|
||||
/// Roles for the user
|
||||
/// </summary>
|
||||
public virtual ICollection<IdentityUserRole<TKey>> Roles { get; private set; }
|
||||
public virtual ICollection<IdentityUserRole<TKey>> Roles { get; } = new List<IdentityUserRole<TKey>>();
|
||||
|
||||
/// <summary>
|
||||
/// Claims for the user
|
||||
/// </summary>
|
||||
public virtual ICollection<IdentityUserClaim<TKey>> Claims { get; private set; }
|
||||
public virtual ICollection<IdentityUserClaim<TKey>> Claims { get; } = new List<IdentityUserClaim<TKey>>();
|
||||
|
||||
/// <summary>
|
||||
/// Associated logins for the user
|
||||
/// </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 LockoutOptions()
|
||||
{
|
||||
DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
|
||||
EnabledByDefault = false;
|
||||
MaxFailedAccessAttempts = 5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, will enable user lockout when users are created
|
||||
/// </summary>
|
||||
public bool EnabledByDefault { get; set; }
|
||||
public bool EnabledByDefault { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Number of access attempts allowed for a user before lockout (if enabled)
|
||||
/// </summary>
|
||||
public int MaxFailedAccessAttempts { get; set; }
|
||||
public int MaxFailedAccessAttempts { get; set; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Default amount of time an user is locked out for after MaxFailedAccessAttempsBeforeLockout is reached
|
||||
/// </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 PasswordOptions()
|
||||
{
|
||||
RequireDigit = true;
|
||||
RequireLowercase = true;
|
||||
RequireNonLetterOrDigit = true;
|
||||
RequireUppercase = true;
|
||||
RequiredLength = 6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum required length
|
||||
/// </summary>
|
||||
public int RequiredLength { get; set; }
|
||||
public int RequiredLength { get; set; } = 6;
|
||||
|
||||
/// <summary>
|
||||
/// Require a non letter or digit character
|
||||
/// </summary>
|
||||
public bool RequireNonLetterOrDigit { get; set; }
|
||||
public bool RequireNonLetterOrDigit { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Require a lower case letter ('a' - 'z')
|
||||
/// </summary>
|
||||
public bool RequireLowercase { get; set; }
|
||||
public bool RequireLowercase { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Require an upper case letter ('A' - 'Z')
|
||||
/// </summary>
|
||||
public bool RequireUppercase { get; set; }
|
||||
public bool RequireUppercase { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Require a digit ('0' - '9')
|
||||
/// </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="cancellationToken"></param>
|
||||
/// <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))
|
||||
{
|
||||
if (password == null)
|
||||
|
|
|
|||
|
|
@ -5,23 +5,28 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Microsoft.AspNet.Identity
|
||||
{
|
||||
/// <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> : TotpSecurityStampBasedTokenProvider<TUser>
|
||||
where TUser : class
|
||||
public class PhoneNumberTokenProviderOptions
|
||||
{
|
||||
private string _body;
|
||||
public string Name { get; set; } = Resources.DefaultPhoneNumberTokenProviderName;
|
||||
|
||||
/// <summary>
|
||||
/// Message contents which should contain a format string which the token will be the only argument
|
||||
/// </summary>
|
||||
public string MessageFormat
|
||||
{
|
||||
get { return _body ?? "{0}"; }
|
||||
set { _body = value; }
|
||||
}
|
||||
public string MessageFormat { get; set; } = "Your security code is: {0}";
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Returns true if the user has a phone number set
|
||||
|
|
@ -29,7 +34,7 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <param name="manager"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <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))
|
||||
{
|
||||
if (manager == null)
|
||||
|
|
@ -72,7 +77,7 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
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
|
||||
= 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>
|
||||
/// An unknown failure has occured.
|
||||
/// </summary>
|
||||
|
|
@ -26,6 +74,54 @@ namespace Microsoft.AspNet.Identity
|
|||
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>
|
||||
/// Email '{0}' is already taken.
|
||||
/// </summary>
|
||||
|
|
@ -139,7 +235,7 @@ namespace Microsoft.AspNet.Identity
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// No IUserTokenProvider is registered.
|
||||
/// No IUserTokenProvider named '{0}' is registered.
|
||||
/// </summary>
|
||||
internal static string NoTokenProvider
|
||||
{
|
||||
|
|
@ -147,27 +243,11 @@ namespace Microsoft.AspNet.Identity
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// No IUserTokenProvider is registered.
|
||||
/// No IUserTokenProvider named '{0}' is registered.
|
||||
/// </summary>
|
||||
internal static string FormatNoTokenProvider()
|
||||
internal static string FormatNoTokenProvider(object p0)
|
||||
{
|
||||
return GetString("NoTokenProvider");
|
||||
}
|
||||
|
||||
/// <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);
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("NoTokenProvider"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -117,10 +117,34 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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">
|
||||
<value>An unknown failure has occured.</value>
|
||||
<comment>Default identity result error message</comment>
|
||||
</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">
|
||||
<value>Email '{0}' is already taken.</value>
|
||||
<comment>error for duplicate emails</comment>
|
||||
|
|
@ -150,13 +174,9 @@
|
|||
<comment>error when lockout is not enabled</comment>
|
||||
</data>
|
||||
<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>
|
||||
</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">
|
||||
<value>Incorrect password.</value>
|
||||
<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)
|
||||
{
|
||||
// # of 0's = length of pin
|
||||
const int mod = 1000000;
|
||||
const int Mod = 1000000;
|
||||
|
||||
// See https://tools.ietf.org/html/rfc4226
|
||||
// We can add an optional modifier
|
||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Identity
|
|||
| (hash[offset + 2] & 0xff) << 8
|
||||
| (hash[offset + 3] & 0xff);
|
||||
|
||||
return binaryCode % mod;
|
||||
return binaryCode % Mod;
|
||||
}
|
||||
|
||||
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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Security;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Identity
|
||||
|
|
@ -16,33 +19,33 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <typeparam name="TUser"></typeparam>
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
throw new ArgumentNullException("claimsFactory");
|
||||
throw new ArgumentNullException(nameof(claimsFactory));
|
||||
}
|
||||
if (optionsAccessor == null || optionsAccessor.Options == null)
|
||||
{
|
||||
throw new ArgumentNullException("optionsAccessor");
|
||||
throw new ArgumentNullException(nameof(optionsAccessor));
|
||||
}
|
||||
UserManager = userManager;
|
||||
AuthenticationManager = authenticationManager;
|
||||
Context = contextAccessor.Value;
|
||||
ClaimsFactory = claimsFactory;
|
||||
Options = optionsAccessor.Options;
|
||||
}
|
||||
|
||||
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 IdentityOptions Options { get; private set; }
|
||||
|
||||
|
|
@ -50,7 +53,6 @@ namespace Microsoft.AspNet.Identity
|
|||
public virtual async Task<ClaimsIdentity> CreateUserIdentityAsync(TUser user,
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -68,18 +70,22 @@ namespace Microsoft.AspNet.Identity
|
|||
// 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))
|
||||
{
|
||||
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?
|
||||
public virtual void SignOut()
|
||||
{
|
||||
// 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)
|
||||
|
|
@ -138,16 +144,31 @@ namespace Microsoft.AspNet.Identity
|
|||
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,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var userId = await AuthenticationManager.RetrieveUserId();
|
||||
if (userId == null)
|
||||
var twoFactorInfo = await RetrieveTwoFactorInfoAsync(cancellationToken);
|
||||
if (twoFactorInfo == null || twoFactorInfo.UserId == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var user = await UserManager.FindByIdAsync(userId, cancellationToken);
|
||||
var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -162,31 +183,35 @@ namespace Microsoft.AspNet.Identity
|
|||
CancellationToken cancellationToken = default(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))
|
||||
{
|
||||
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()
|
||||
{
|
||||
AuthenticationManager.ForgetClient();
|
||||
Context.Response.SignOut(ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public virtual async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
public virtual async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent,
|
||||
bool rememberClient, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var userId = await AuthenticationManager.RetrieveUserId();
|
||||
if (userId == null)
|
||||
var twoFactorInfo = await RetrieveTwoFactorInfoAsync(cancellationToken);
|
||||
if (twoFactorInfo == null || twoFactorInfo.UserId == null)
|
||||
{
|
||||
return SignInStatus.Failure;
|
||||
}
|
||||
var user = await UserManager.FindByIdAsync(userId, cancellationToken);
|
||||
var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
return SignInStatus.Failure;
|
||||
|
|
@ -199,7 +224,11 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
// When token is verified correctly, clear the access failed count used for lockout
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <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,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
|
|
@ -219,24 +265,57 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
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,
|
||||
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))
|
||||
{
|
||||
// Store the userId for use after two factor check
|
||||
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
||||
await AuthenticationManager.StoreUserId(userId);
|
||||
Context.Response.SignIn(StoreTwoFactorInfo(userId, loginProvider));
|
||||
return SignInStatus.RequiresVerification;
|
||||
}
|
||||
}
|
||||
await SignInAsync(user, isPersistent, cancellationToken);
|
||||
await SignInAsync(user, isPersistent, loginProvider, cancellationToken);
|
||||
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 SignInOptions() { }
|
||||
|
||||
/// <summary>
|
||||
/// If set, requires a confirmed email to sign in
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ namespace Microsoft.AspNet.Identity
|
|||
/// </summary>
|
||||
/// <typeparam name="TUser"></typeparam>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
public class TotpSecurityStampBasedTokenProvider<TUser> : IUserTokenProvider<TUser>
|
||||
public abstract class TotpSecurityStampBasedTokenProvider<TUser> : IUserTokenProvider<TUser>
|
||||
where TUser : class
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This token provider does not notify the user by default
|
||||
/// </summary>
|
||||
|
|
@ -26,23 +28,6 @@ namespace Microsoft.AspNet.Identity
|
|||
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>
|
||||
/// Generate a token for the user using their security stamp
|
||||
/// </summary>
|
||||
|
|
@ -101,8 +86,10 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
throw new ArgumentNullException("manager");
|
||||
}
|
||||
string userId = await manager.GetUserIdAsync(user);
|
||||
var userId = await manager.GetUserIdAsync(user);
|
||||
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>
|
||||
/// Represents a linked login for a user (i.e. a local username/password or a facebook/google account
|
||||
/// </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>
|
||||
/// Provider for the linked login, i.e. Local, Facebook, Google, etc.
|
||||
/// </summary>
|
||||
public string LoginProvider { get; set; }
|
||||
public string LoginProvider { get; set; } = loginProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Key for the linked login at the provider
|
||||
/// </summary>
|
||||
public string ProviderKey { get; set; }
|
||||
public string ProviderKey { get; set; } = providerKey;
|
||||
|
||||
/// <summary>
|
||||
/// Display name for the provider
|
||||
/// </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.Tasks;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Microsoft.AspNet.Security.DataProtection;
|
||||
|
||||
namespace Microsoft.AspNet.Identity
|
||||
{
|
||||
|
|
@ -19,7 +20,7 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <typeparam name="TUser"></typeparam>
|
||||
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>>();
|
||||
|
||||
private TimeSpan _defaultLockout = TimeSpan.Zero;
|
||||
|
|
@ -38,19 +39,20 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <param name="claimsIdentityFactory"></param>
|
||||
public UserManager(IUserStore<TUser> store, IOptionsAccessor<IdentityOptions> optionsAccessor,
|
||||
IPasswordHasher<TUser> passwordHasher, IUserValidator<TUser> userValidator,
|
||||
IPasswordValidator<TUser> passwordValidator, IUserNameNormalizer userNameNormalizer)
|
||||
IPasswordValidator<TUser> passwordValidator, IUserNameNormalizer userNameNormalizer,
|
||||
IEnumerable<IUserTokenProvider<TUser>> tokenProviders)
|
||||
{
|
||||
if (store == null)
|
||||
{
|
||||
throw new ArgumentNullException("store");
|
||||
throw new ArgumentNullException(nameof(store));
|
||||
}
|
||||
if (optionsAccessor == null || optionsAccessor.Options == null)
|
||||
{
|
||||
throw new ArgumentNullException("optionsAccessor");
|
||||
throw new ArgumentNullException(nameof(optionsAccessor));
|
||||
}
|
||||
if (passwordHasher == null)
|
||||
{
|
||||
throw new ArgumentNullException("passwordHasher");
|
||||
throw new ArgumentNullException(nameof(passwordHasher));
|
||||
}
|
||||
Store = store;
|
||||
Options = optionsAccessor.Options;
|
||||
|
|
@ -59,6 +61,13 @@ namespace Microsoft.AspNet.Identity
|
|||
PasswordValidator = passwordValidator;
|
||||
UserNameNormalizer = userNameNormalizer;
|
||||
// TODO: Email/Sms/Token services
|
||||
|
||||
if (tokenProviders != null) {
|
||||
foreach (var tokenProvider in tokenProviders)
|
||||
{
|
||||
RegisterTokenProvider(tokenProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -112,11 +121,6 @@ namespace Microsoft.AspNet.Identity
|
|||
/// </summary>
|
||||
public IIdentityMessageService SmsService { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used for generating ResetPassword and Confirmation Tokens
|
||||
/// </summary>
|
||||
public IUserTokenProvider<TUser> UserTokenProvider { get; set; }
|
||||
|
||||
public IdentityOptions Options
|
||||
{
|
||||
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>
|
||||
/// Dispose the store context
|
||||
/// </summary>
|
||||
|
|
@ -451,7 +447,7 @@ namespace Microsoft.AspNet.Identity
|
|||
public virtual async Task UpdateNormalizedUserName(TUser user,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
string userName = await GetUserNameAsync(user, cancellationToken);
|
||||
var userName = await GetUserNameAsync(user, cancellationToken);
|
||||
await Store.SetNormalizedUserNameAsync(user, NormalizeUserName(userName), cancellationToken);
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +493,6 @@ namespace Microsoft.AspNet.Identity
|
|||
await UpdateNormalizedUserName(user, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the user's id
|
||||
/// </summary>
|
||||
|
|
@ -651,7 +646,7 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
if (PasswordValidator != null)
|
||||
{
|
||||
var result = await PasswordValidator.ValidateAsync(newPassword, this, cancellationToken);
|
||||
var result = await PasswordValidator.ValidateAsync(user, newPassword, this, cancellationToken);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return result;
|
||||
|
|
@ -736,7 +731,7 @@ namespace Microsoft.AspNet.Identity
|
|||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return await GenerateUserTokenAsync("ResetPassword", user, cancellationToken);
|
||||
return await GenerateUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -756,7 +751,7 @@ namespace Microsoft.AspNet.Identity
|
|||
throw new ArgumentNullException("user");
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -974,7 +969,6 @@ namespace Microsoft.AspNet.Identity
|
|||
return RemoveClaimsAsync(user, new Claim[] { claim }, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remove a user claim
|
||||
/// </summary>
|
||||
|
|
@ -1271,7 +1265,7 @@ namespace Microsoft.AspNet.Identity
|
|||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return GenerateUserTokenAsync("Confirmation", user, cancellationToken);
|
||||
return GenerateUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1290,7 +1284,7 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
|
@ -1461,20 +1455,24 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <param name="token"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <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))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (UserTokenProvider == null)
|
||||
{
|
||||
throw new NotSupportedException(Resources.NoTokenProvider);
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
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
|
||||
return await UserTokenProvider.ValidateAsync(purpose, token, this, user, cancellationToken);
|
||||
return await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1484,38 +1482,38 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <param name="user"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <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))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (UserTokenProvider == null)
|
||||
{
|
||||
throw new NotSupportedException(Resources.NoTokenProvider);
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
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>
|
||||
/// Register a user two factor provider
|
||||
/// Register a user token provider
|
||||
/// </summary>
|
||||
/// <param name="twoFactorProvider"></param>
|
||||
/// <param name="provider"></param>
|
||||
public virtual void RegisterTwoFactorProvider(string twoFactorProvider, IUserTokenProvider<TUser> provider)
|
||||
public virtual void RegisterTokenProvider(IUserTokenProvider<TUser> provider)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (twoFactorProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException("twoFactorProvider");
|
||||
}
|
||||
if (provider == null)
|
||||
{
|
||||
throw new ArgumentNullException("provider");
|
||||
}
|
||||
TwoFactorProviders[twoFactorProvider] = provider;
|
||||
_tokenProviders[provider.Name] = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1533,9 +1531,9 @@ namespace Microsoft.AspNet.Identity
|
|||
throw new ArgumentNullException("user");
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
|
@ -1551,7 +1549,7 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <param name="token"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <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))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
|
@ -1559,14 +1557,13 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
throw new ArgumentNullException("user");
|
||||
}
|
||||
if (!_factors.ContainsKey(twoFactorProvider))
|
||||
if (!_tokenProviders.ContainsKey(tokenProvider))
|
||||
{
|
||||
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
|
||||
Resources.NoTwoFactorProvider, twoFactorProvider));
|
||||
Resources.NoTokenProvider, tokenProvider));
|
||||
}
|
||||
// Make sure the token is valid
|
||||
var provider = _factors[twoFactorProvider];
|
||||
return await provider.ValidateAsync(twoFactorProvider, token, this, user, cancellationToken);
|
||||
return await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -1576,7 +1573,7 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <param name="twoFactorProvider"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <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))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
|
@ -1584,23 +1581,23 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
throw new ArgumentNullException("user");
|
||||
}
|
||||
if (!_factors.ContainsKey(twoFactorProvider))
|
||||
if (!_tokenProviders.ContainsKey(tokenProvider))
|
||||
{
|
||||
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>
|
||||
/// Notify a user with a token from a specific user factor provider
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="twoFactorProvider"></param>
|
||||
/// <param name="tokenProvider"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <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))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
|
@ -1608,12 +1605,16 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
{
|
||||
"version": "3.0.0-*",
|
||||
"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.DependencyInjection" : "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 =>
|
||||
{
|
||||
services.AddInstance(contextAccessor.Object);
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>().AddInMemory().AddAuthentication();
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>().AddInMemory();
|
||||
});
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
"dependencies": {
|
||||
"Microsoft.AspNet.Http" : "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-*",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.InMemory.Test
|
|||
var services = new ServiceCollection();
|
||||
services.AddEntityFramework().AddInMemoryStore();
|
||||
var store = new RoleStore<IdentityRole>(new InMemoryContext());
|
||||
services.AddIdentity().AddRoleStore(() => store);
|
||||
services.AddIdentity().AddRoleStore(store);
|
||||
var provider = services.BuildServiceProvider();
|
||||
var manager = provider.GetService<RoleManager<IdentityRole>>();
|
||||
Assert.NotNull(manager);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.InMemory.Test
|
|||
|
||||
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()
|
||||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.InMemory.Test
|
|||
public static RoleManager<IdentityRole> CreateRoleManager(InMemoryContext context)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddIdentity().AddRoleStore(() => new RoleStore<IdentityRole>(context));
|
||||
services.AddIdentity().AddRoleStore(new RoleStore<IdentityRole>(context));
|
||||
return services.BuildServiceProvider().GetService<RoleManager<IdentityRole>>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
|||
{
|
||||
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)
|
||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
|||
context = CreateTestContext();
|
||||
}
|
||||
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>>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
|||
{
|
||||
context = CreateTestContext();
|
||||
}
|
||||
return MockHelpers.CreateManager(() => new ApplicationUserStore((ApplicationDbContext)context));
|
||||
return MockHelpers.CreateManager(new ApplicationUserStore((ApplicationDbContext)context));
|
||||
}
|
||||
|
||||
protected override RoleManager<GuidRole> CreateRoleManager(object context)
|
||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
|||
context = CreateTestContext();
|
||||
}
|
||||
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>>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
|||
|
||||
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)
|
||||
|
|
@ -187,7 +187,7 @@ namespace Microsoft.AspNet.Identity.SqlServer.Test
|
|||
public static RoleManager<IdentityRole> CreateRoleManager(IdentityDbContext context)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddIdentity().AddRoleStore(() => new RoleStore<IdentityRole>(context));
|
||||
services.AddIdentity().AddRoleStore(new RoleStore<IdentityRole>(context));
|
||||
return services.BuildServiceProvider().GetService<RoleManager<IdentityRole>>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
{
|
||||
var services = new ServiceCollection();
|
||||
var validator = new UserValidator<IdentityUser>();
|
||||
services.AddIdentity<IdentityUser>().AddUserValidator(() => validator);
|
||||
services.AddIdentity<IdentityUser>().AddUserValidator(validator);
|
||||
Assert.Equal(validator, services.BuildServiceProvider().GetService<IUserValidator<IdentityUser>>());
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
{
|
||||
var services = new ServiceCollection();
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
services.AddIdentity<IdentityUser>().AddPasswordValidator(() => validator);
|
||||
services.AddIdentity<IdentityUser>().AddPasswordValidator(validator);
|
||||
Assert.Equal(validator, services.BuildServiceProvider().GetService<IPasswordValidator<IdentityUser>>());
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
private static void CanOverride<TService>(TService instance)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddIdentity<IdentityUser>().AddInstance(() => instance);
|
||||
services.AddIdentity<IdentityUser>().AddInstance(instance);
|
||||
Assert.Equal(instance, services.BuildServiceProvider().GetService<TService>());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public class PasswordsNegativeLengthSetup : IOptionsSetup<IdentityOptions>
|
||||
{
|
||||
public int Order { get { return 0; } }
|
||||
public string Name { get; set; }
|
||||
public void Setup(IdentityOptions options)
|
||||
{
|
||||
options.Password.RequiredLength = -1;
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
|
||||
// Act
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("password", () => validator.ValidateAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("manager", () => validator.ValidateAsync("foo", null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("password", () => validator.ValidateAsync(null, null, 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.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
IdentityResultAssert.IsFailure(await valid.ValidateAsync(input, manager), error);
|
||||
IdentityResultAssert.IsFailure(await valid.ValidateAsync(null, input, manager), error);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
manager.Options.Password.RequireNonLetterOrDigit = false;
|
||||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(null, input, manager));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
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.");
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
manager.Options.Password.RequireLowercase = false;
|
||||
manager.Options.Password.RequireDigit = false;
|
||||
manager.Options.Password.RequiredLength = 0;
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(null, input, manager));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -135,11 +135,11 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
}
|
||||
if (errors.Count == 0)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(input, manager));
|
||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(null, input, manager));
|
||||
}
|
||||
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 Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Security;
|
||||
using Microsoft.AspNet.Identity.Test;
|
||||
using Microsoft.AspNet.Security;
|
||||
using Microsoft.AspNet.Security.Cookies;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
|
@ -16,7 +15,7 @@ using Microsoft.Framework.OptionsModel;
|
|||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||
namespace Microsoft.AspNet.Identity.Test
|
||||
{
|
||||
public class SecurityStampTest
|
||||
{
|
||||
|
|
@ -28,7 +27,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow });
|
||||
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]
|
||||
|
|
@ -37,19 +36,22 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
public async Task OnValidateIdentityTestSuccess(bool isPersistent)
|
||||
{
|
||||
var user = new IdentityUser("test");
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||
var authManager = new Mock<IAuthenticationManager>();
|
||||
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||
var identityOptions = new IdentityOptions();
|
||||
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.Zero };
|
||||
var options = new Mock<IOptionsAccessor<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,
|
||||
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.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();
|
||||
services.AddInstance(options.Object);
|
||||
services.AddInstance(signInManager.Object);
|
||||
services.AddInstance<ISecurityStampValidator>(new SecurityStampValidator<IdentityUser>());
|
||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||
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.Identity);
|
||||
await
|
||||
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
||||
SecurityStampValidator.ValidateIdentityAsync(context);
|
||||
Assert.NotNull(context.Identity);
|
||||
signInManager.VerifyAll();
|
||||
}
|
||||
|
|
@ -69,18 +71,21 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails()
|
||||
{
|
||||
var user = new IdentityUser("test");
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||
var authManager = new Mock<IAuthenticationManager>();
|
||||
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||
var identityOptions = new IdentityOptions();
|
||||
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.Zero };
|
||||
var options = new Mock<IOptionsAccessor<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,
|
||||
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();
|
||||
var services = new ServiceCollection();
|
||||
services.AddInstance(options.Object);
|
||||
services.AddInstance(signInManager.Object);
|
||||
services.AddInstance<ISecurityStampValidator>(new SecurityStampValidator<IdentityUser>());
|
||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||
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.Identity);
|
||||
await
|
||||
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
||||
SecurityStampValidator.ValidateIdentityAsync(context);
|
||||
Assert.Null(context.Identity);
|
||||
signInManager.VerifyAll();
|
||||
}
|
||||
|
|
@ -102,16 +107,19 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
var user = new IdentityUser("test");
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||
var authManager = new Mock<IAuthenticationManager>();
|
||||
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||
var identityOptions = new IdentityOptions();
|
||||
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.Zero };
|
||||
var options = new Mock<IOptionsAccessor<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,
|
||||
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();
|
||||
var services = new ServiceCollection();
|
||||
services.AddInstance(options.Object);
|
||||
services.AddInstance(signInManager.Object);
|
||||
services.AddInstance<ISecurityStampValidator>(new SecurityStampValidator<IdentityUser>());
|
||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||
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.Identity);
|
||||
await
|
||||
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
||||
SecurityStampValidator.ValidateIdentityAsync(context);
|
||||
Assert.Null(context.Identity);
|
||||
signInManager.VerifyAll();
|
||||
}
|
||||
|
|
@ -133,17 +141,20 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
var user = new IdentityUser("test");
|
||||
var httpContext = new Mock<HttpContext>();
|
||||
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||
var authManager = new Mock<IAuthenticationManager>();
|
||||
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||
var identityOptions = new IdentityOptions();
|
||||
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.FromDays(1) };
|
||||
var options = new Mock<IOptionsAccessor<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,
|
||||
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.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();
|
||||
services.AddInstance(options.Object);
|
||||
services.AddInstance(signInManager.Object);
|
||||
services.AddInstance<ISecurityStampValidator>(new SecurityStampValidator<IdentityUser>());
|
||||
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
|
||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultAuthenticationType);
|
||||
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.Identity);
|
||||
await
|
||||
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.FromDays(1)).Invoke(context);
|
||||
SecurityStampValidator.ValidateIdentityAsync(context);
|
||||
Assert.NotNull(context.Identity);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,23 +3,21 @@
|
|||
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Security;
|
||||
using Microsoft.AspNet.Identity.Test;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||
namespace Microsoft.AspNet.Identity.Test
|
||||
{
|
||||
public class ApplicationUser : IdentityUser { }
|
||||
|
||||
public class HttpSignInTest
|
||||
public class SignManagerInTest
|
||||
{
|
||||
#if ASPNET50
|
||||
//[Theory]
|
||||
//[InlineData(true)]
|
||||
//[InlineData(false)]
|
||||
|
|
@ -71,11 +69,14 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
{
|
||||
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<IdentityUser>(null, null, null, null));
|
||||
var userManager = MockHelpers.MockUserManager<IdentityUser>().Object;
|
||||
Assert.Throws<ArgumentNullException>("authenticationManager", () => new SignInManager<IdentityUser>(userManager, null, null, null));
|
||||
var authManager = new Mock<IAuthenticationManager>().Object;
|
||||
Assert.Throws<ArgumentNullException>("claimsFactory", () => new SignInManager<IdentityUser>(userManager, authManager, null, null));
|
||||
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<IdentityUser>(userManager, null, null, null));
|
||||
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
|
||||
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;
|
||||
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)
|
||||
|
|
@ -125,7 +126,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
var identityOptions = new IdentityOptions();
|
||||
var options = new Mock<IOptionsAccessor<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
|
||||
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();
|
||||
var options = new Mock<IOptionsAccessor<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
|
||||
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 options = new Mock<IOptionsAccessor<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
|
||||
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();
|
||||
}
|
||||
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.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).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 options = new Mock<IOptionsAccessor<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
|
||||
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 response = new Mock<HttpResponse>();
|
||||
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>>();
|
||||
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||
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();
|
||||
var options = new Mock<IOptionsAccessor<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
|
||||
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent);
|
||||
|
|
@ -293,11 +299,24 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, false)]
|
||||
public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout)
|
||||
[InlineData(true, true, true, true)]
|
||||
[InlineData(true, true, false, true)]
|
||||
[InlineData(true, false, true, true)]
|
||||
[InlineData(true, false, false, true)]
|
||||
[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
|
||||
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();
|
||||
var context = new Mock<HttpContext>();
|
||||
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>>();
|
||||
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 claimsFactory = new ClaimsIdentityFactory<TestUser, TestRole>(manager.Object, roleManager.Object);
|
||||
var identityOptions = new IdentityOptions();
|
||||
var options = new Mock<IOptionsAccessor<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
|
||||
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent);
|
||||
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent, rememberClient);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(SignInStatus.Success, result);
|
||||
|
|
@ -342,28 +381,39 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void RememberClientStoresUserId()
|
||||
public async Task RememberClientStoresUserId()
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = MockHelpers.MockUserManager<TestUser>();
|
||||
var context = new Mock<HttpContext>();
|
||||
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>>();
|
||||
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||
var signInService = new HttpAuthenticationManager(contextAccessor.Object);
|
||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||
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
|
||||
signInService.RememberClient(user.Id);
|
||||
await helper.RememberTwoFactorClientAsync(user);
|
||||
|
||||
// Assert
|
||||
manager.VerifyAll();
|
||||
context.VerifyAll();
|
||||
response.VerifyAll();
|
||||
contextAccessor.VerifyAll();
|
||||
options.VerifyAll();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -375,6 +425,9 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
var user = new TestUser { UserName = "Foo" };
|
||||
var manager = MockHelpers.MockUserManager<TestUser>();
|
||||
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.SupportsUserTwoFactor).Returns(true).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>();
|
||||
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();
|
||||
var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType);
|
||||
var id = new ClaimsIdentity(ClaimsIdentityOptions.DefaultTwoFactorRememberMeAuthenticationType);
|
||||
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
|
||||
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>>();
|
||||
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||
var signInService = new HttpAuthenticationManager(contextAccessor.Object);
|
||||
var roleManager = MockHelpers.MockRoleManager<TestRole>();
|
||||
var identityOptions = new IdentityOptions();
|
||||
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();
|
||||
var options = new Mock<IOptionsAccessor<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
|
||||
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>>();
|
||||
options.Setup(a => a.Options).Returns(identityOptions);
|
||||
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
|
||||
helper.SignOut();
|
||||
|
|
@ -459,7 +511,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
var identityOptions = new IdentityOptions();
|
||||
var options = new Mock<IOptionsAccessor<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
|
||||
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 options = new Mock<IOptionsAccessor<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
|
||||
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
|
||||
|
|
@ -516,7 +568,7 @@ namespace Microsoft.AspNet.Identity.Authentication.Test
|
|||
var identityOptions = new IdentityOptions();
|
||||
var options = new Mock<IOptionsAccessor<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
|
||||
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);
|
||||
manager.VerifyAll();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -24,14 +24,14 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public TestManager(IUserStore<TestUser> store, IOptionsAccessor<IdentityOptions> optionsAccessor,
|
||||
IPasswordHasher<TestUser> passwordHasher, IUserValidator<TestUser> userValidator,
|
||||
IPasswordValidator<TestUser> passwordValidator)
|
||||
: base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null) { }
|
||||
: base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null) { }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnsureDefaultServicesDefaultsWithStoreWorks()
|
||||
{
|
||||
var services = new ServiceCollection {IdentityServices.GetDefaultUserServices<TestUser>()};
|
||||
services.AddInstance<IOptionsAccessor<IdentityOptions>>(new OptionsAccessor<IdentityOptions>(null));
|
||||
var services = new ServiceCollection {IdentityServices.GetDefaultServices<TestUser, IdentityRole>()};
|
||||
services.Add(OptionsServices.GetDefaultServices());
|
||||
services.AddTransient<IUserStore<TestUser>, NoopUserStore>();
|
||||
services.AddTransient<TestManager>();
|
||||
var manager = services.BuildServiceProvider().GetService<TestManager>();
|
||||
|
|
@ -464,10 +464,11 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task TokenMethodsThrowWithNoTokenProvider()
|
||||
{
|
||||
var manager = MockHelpers.TestUserManager(new NoopUserStore());
|
||||
var user = new TestUser();
|
||||
await Assert.ThrowsAsync<NotSupportedException>(
|
||||
async () => await manager.GenerateUserTokenAsync(null, null));
|
||||
async () => await manager.GenerateUserTokenAsync(user, "bogus", null));
|
||||
await Assert.ThrowsAsync<NotSupportedException>(
|
||||
async () => await manager.VerifyUserTokenAsync(null, null, null));
|
||||
async () => await manager.VerifyUserTokenAsync(user, "bogus", null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -581,13 +582,13 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
var passwordValidator = new PasswordValidator<TestUser>();
|
||||
|
||||
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",
|
||||
() => new UserManager<TestUser>(store, null, null, null, null, null));
|
||||
() => new UserManager<TestUser>(store, null, null, null, null, null, null));
|
||||
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.Options = null);
|
||||
|
|
@ -608,9 +609,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
await Assert.ThrowsAsync<ArgumentNullException>("providerKey",
|
||||
async () => await manager.RemoveLoginAsync(null, "", null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("email", async () => await manager.FindByEmailAsync(null));
|
||||
Assert.Throws<ArgumentNullException>("twoFactorProvider",
|
||||
() => manager.RegisterTwoFactorProvider(null, null));
|
||||
Assert.Throws<ArgumentNullException>("provider", () => manager.RegisterTwoFactorProvider("bogus", null));
|
||||
Assert.Throws<ArgumentNullException>("provider", () => manager.RegisterTokenProvider(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));
|
||||
}
|
||||
|
|
@ -619,7 +618,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task MethodsFailWithUnknownUserTest()
|
||||
{
|
||||
var manager = MockHelpers.TestUserManager(new EmptyStore());
|
||||
manager.UserTokenProvider = new NoOpTokenProvider();
|
||||
manager.RegisterTokenProvider(new NoOpTokenProvider());
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await manager.GetUserNameAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
|
|
@ -697,7 +696,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await manager.GetValidTwoFactorProvidersAsync(null));
|
||||
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",
|
||||
async () => await manager.AccessFailedAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
|
|
@ -763,7 +762,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
{
|
||||
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));
|
||||
}
|
||||
|
|
@ -1007,6 +1006,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
|
||||
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))
|
||||
{
|
||||
return Task.FromResult("Test");
|
||||
|
|
@ -1023,7 +1024,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
{
|
||||
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();
|
||||
services.Add(OptionsServices.GetDefaultServices());
|
||||
services.AddIdentity<TUser>().AddUserStore(storeFunc);
|
||||
services.AddIdentity<TUser>().AddUserStore(store);
|
||||
services.SetupOptions<IdentityOptions>(options =>
|
||||
{
|
||||
options.Password.RequireDigit = false;
|
||||
|
|
@ -39,7 +39,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
new PasswordHasher<TUser>(),
|
||||
new UserValidator<TUser>(),
|
||||
new PasswordValidator<TUser>(),
|
||||
new UpperInvariantUserNameNormalizer());
|
||||
new UpperInvariantUserNameNormalizer(),
|
||||
null);
|
||||
}
|
||||
|
||||
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 validator = new Mock<UserValidator<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))
|
||||
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
|
||||
return userManager;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Xunit;
|
||||
using Microsoft.AspNet.Security.DataProtection;
|
||||
|
||||
namespace Microsoft.AspNet.Identity.Test
|
||||
{
|
||||
|
|
@ -502,6 +503,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
|
||||
private class StaticTokenProvider : IUserTokenProvider<TUser>
|
||||
{
|
||||
public string Name { get; } = "Static";
|
||||
|
||||
public Task<string> GenerateAsync(string purpose, UserManager<TUser> manager,
|
||||
TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
|
|
@ -519,7 +522,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
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);
|
||||
}
|
||||
|
|
@ -534,7 +537,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task CanResetPasswordWithStaticTokenProvider()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.UserTokenProvider = new StaticTokenProvider();
|
||||
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||
manager.Options.PasswordResetTokenProvider = "Static";
|
||||
var user = CreateTestUser();
|
||||
const string password = "password";
|
||||
const string newPassword = "newpassword";
|
||||
|
|
@ -553,7 +557,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.UserTokenProvider = new StaticTokenProvider();
|
||||
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||
manager.Options.PasswordResetTokenProvider = "Static";
|
||||
var user = CreateTestUser();
|
||||
const string password = "password";
|
||||
const string newPassword = "newpassword";
|
||||
|
|
@ -574,7 +579,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.UserTokenProvider = new StaticTokenProvider();
|
||||
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||
manager.Options.PasswordResetTokenProvider = "Static";
|
||||
var user = CreateTestUser();
|
||||
const string password = "password";
|
||||
const string newPassword = "newpassword";
|
||||
|
|
@ -591,23 +597,24 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.UserTokenProvider = new StaticTokenProvider();
|
||||
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||
var user = CreateTestUser();
|
||||
var user2 = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
|
||||
var token = await manager.GenerateUserTokenAsync("test", user);
|
||||
Assert.True(await manager.VerifyUserTokenAsync(user, "test", token));
|
||||
Assert.False(await manager.VerifyUserTokenAsync(user, "test2", token));
|
||||
Assert.False(await manager.VerifyUserTokenAsync(user, "test", token + "a"));
|
||||
Assert.False(await manager.VerifyUserTokenAsync(user2, "test", token));
|
||||
var token = await manager.GenerateUserTokenAsync(user, "Static", "test");
|
||||
Assert.True(await manager.VerifyUserTokenAsync(user, "Static", "test", token));
|
||||
Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test2", token));
|
||||
Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test", token + "a"));
|
||||
Assert.False(await manager.VerifyUserTokenAsync(user2, "Static", "test", token));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanConfirmEmailWithStaticToken()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.UserTokenProvider = new StaticTokenProvider();
|
||||
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||
manager.Options.EmailConfirmationTokenProvider = "Static";
|
||||
var user = CreateTestUser();
|
||||
Assert.False(user.EmailConfirmed);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
|
|
@ -623,7 +630,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.UserTokenProvider = new StaticTokenProvider();
|
||||
manager.RegisterTokenProvider(new StaticTokenProvider());
|
||||
manager.Options.EmailConfirmationTokenProvider = "Static";
|
||||
var user = CreateTestUser();
|
||||
Assert.False(user.EmailConfirmed);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
|
|
@ -631,20 +639,22 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
Assert.False(await manager.IsEmailConfirmedAsync(user));
|
||||
}
|
||||
|
||||
// TODO: Can't reenable til we have a SecurityStamp linked token provider
|
||||
//[Fact]
|
||||
//public async Task ConfirmTokenFailsAfterPasswordChange()
|
||||
//{
|
||||
// var manager = CreateManager();
|
||||
// var user = new TUser() { UserName = "test" };
|
||||
// Assert.False(user.EmailConfirmed);
|
||||
// IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
|
||||
// var token = await manager.GenerateEmailConfirmationTokenAsync(user);
|
||||
// Assert.NotNull(token);
|
||||
// IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword"));
|
||||
// IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token.");
|
||||
// Assert.False(await manager.IsEmailConfirmedAsync(user));
|
||||
//}
|
||||
[Fact]
|
||||
public async Task ConfirmTokenFailsAfterPasswordChange()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.RegisterTokenProvider(new DataProtectorTokenProvider<TUser>(new DataProtectionTokenProviderOptions(),
|
||||
DataProtectionProvider.CreateFromDpapi().CreateProtector("ASP.NET Identity")));
|
||||
manager.Options.EmailConfirmationTokenProvider = "DataProtection";
|
||||
var user = new TUser() { UserName = "test" };
|
||||
Assert.False(user.EmailConfirmed);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
|
||||
var token = await manager.GenerateEmailConfirmationTokenAsync(user);
|
||||
Assert.NotNull(token);
|
||||
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
|
||||
|
||||
|
|
@ -837,7 +847,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
{
|
||||
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));
|
||||
}
|
||||
|
|
@ -1220,73 +1230,16 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
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]
|
||||
public async Task CanEmailTwoFactorToken()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var messageService = new TestMessageService();
|
||||
manager.EmailService = messageService;
|
||||
const string factorId = "EmailCode";
|
||||
manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider());
|
||||
const string factorId = "Email";
|
||||
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>());
|
||||
var user = new TUser() { UserName = "EmailCodeTest", Email = "foo@foo.com" };
|
||||
user.EmailConfirmed = true;
|
||||
const string password = "password";
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
|
||||
var stamp = user.SecurityStamp;
|
||||
|
|
@ -1296,8 +1249,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
Assert.Null(messageService.Message);
|
||||
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
||||
Assert.NotNull(messageService.Message);
|
||||
Assert.Equal(token, messageService.Message.Subject);
|
||||
Assert.Equal(token, messageService.Message.Body);
|
||||
Assert.Equal("Your security code is: "+token, messageService.Message.Body);
|
||||
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
|
||||
}
|
||||
|
||||
|
|
@ -1310,7 +1262,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
await
|
||||
ExceptionAssert.ThrowsAsync<NotSupportedException>(
|
||||
async () => await manager.NotifyTwoFactorTokenAsync(user, "Bogus", "token"),
|
||||
"No IUserTwoFactorProvider for 'Bogus' is registered.");
|
||||
"No IUserTokenProvider named 'Bogus' is registered.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -1319,12 +1271,15 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
var manager = CreateManager();
|
||||
var messageService = new TestMessageService();
|
||||
manager.EmailService = messageService;
|
||||
const string factorId = "EmailCode";
|
||||
manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider<TUser>
|
||||
const string factorId = "EmailTestCode";
|
||||
const string subject = "Custom subject";
|
||||
const string body = "Your code is {0}!";
|
||||
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>(new EmailTokenProviderOptions
|
||||
{
|
||||
Subject = "Security Code",
|
||||
BodyFormat = "Your code is: {0}"
|
||||
});
|
||||
Name = factorId,
|
||||
Subject = subject,
|
||||
BodyFormat = body
|
||||
}));
|
||||
var user = CreateTestUser();
|
||||
user.Email = user.UserName + "@foo.com";
|
||||
const string password = "password";
|
||||
|
|
@ -1336,8 +1291,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
Assert.Null(messageService.Message);
|
||||
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
||||
Assert.NotNull(messageService.Message);
|
||||
Assert.Equal("Security Code", messageService.Message.Subject);
|
||||
Assert.Equal("Your code is: " + token, messageService.Message.Body);
|
||||
Assert.Equal(subject, messageService.Message.Subject);
|
||||
Assert.Equal(string.Format(body, token), messageService.Message.Body);
|
||||
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
|
||||
}
|
||||
|
||||
|
|
@ -1345,10 +1300,11 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task EmailFactorFailsAfterSecurityStampChangeTest()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
const string factorId = "EmailCode";
|
||||
manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider<TUser>());
|
||||
string factorId = "Email"; //default
|
||||
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>());
|
||||
var user = CreateTestUser();
|
||||
user.Email = user.UserName + "@foo.com";
|
||||
user.EmailConfirmed = true;
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
var stamp = user.SecurityStamp;
|
||||
Assert.NotNull(stamp);
|
||||
|
|
@ -1407,8 +1363,8 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
var manager = CreateManager();
|
||||
var messageService = new TestMessageService();
|
||||
manager.SmsService = messageService;
|
||||
const string factorId = "PhoneCode";
|
||||
manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider());
|
||||
const string factorId = "Phone"; // default
|
||||
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||
var user = CreateTestUser();
|
||||
user.PhoneNumber = "4251234567";
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
|
|
@ -1419,7 +1375,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
Assert.Null(messageService.Message);
|
||||
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
@ -1429,11 +1385,12 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
var manager = CreateManager();
|
||||
var messageService = new TestMessageService();
|
||||
manager.SmsService = messageService;
|
||||
const string factorId = "PhoneCode";
|
||||
manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider<TUser>
|
||||
const string factorId = "PhoneTestFactors";
|
||||
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>(new PhoneNumberTokenProviderOptions
|
||||
{
|
||||
Name = factorId,
|
||||
MessageFormat = "Your code is: {0}"
|
||||
});
|
||||
}));
|
||||
var user = CreateTestUser();
|
||||
user.PhoneNumber = "4251234567";
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
|
|
@ -1454,7 +1411,7 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
var manager = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
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
|
||||
ExceptionAssert.ThrowsAsync<NotSupportedException>(
|
||||
() => manager.GenerateTwoFactorTokenAsync(user, "bogus"), error);
|
||||
|
|
@ -1477,35 +1434,39 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task CanGetValidTwoFactor()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.RegisterTwoFactorProvider("phone", new SmsTokenProvider());
|
||||
manager.RegisterTwoFactorProvider("email", new EmailTokenProvider());
|
||||
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>());
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
var factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
||||
Assert.NotNull(factors);
|
||||
Assert.True(!factors.Any());
|
||||
Assert.False(factors.Any());
|
||||
IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111"));
|
||||
user.PhoneNumberConfirmed = true;
|
||||
await manager.UpdateAsync(user);
|
||||
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
||||
Assert.NotNull(factors);
|
||||
Assert.True(factors.Count() == 1);
|
||||
Assert.Equal("phone", factors[0]);
|
||||
Assert.Equal(1, factors.Count());
|
||||
Assert.Equal("Phone", factors[0]);
|
||||
IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com"));
|
||||
user.EmailConfirmed = true;
|
||||
await manager.UpdateAsync(user);
|
||||
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
||||
Assert.NotNull(factors);
|
||||
Assert.True(factors.Count() == 2);
|
||||
Assert.Equal(2, factors.Count());
|
||||
IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null));
|
||||
factors = await manager.GetValidTwoFactorProvidersAsync(user);
|
||||
Assert.NotNull(factors);
|
||||
Assert.True(factors.Count() == 1);
|
||||
Assert.Equal("phone", factors[0]);
|
||||
Assert.Equal(1, factors.Count());
|
||||
Assert.Equal("Phone", factors[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PhoneFactorFailsAfterSecurityStampChangeTest()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var factorId = "PhoneCode";
|
||||
manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider<TUser>());
|
||||
var factorId = "Phone"; // default
|
||||
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||
var user = CreateTestUser();
|
||||
user.PhoneNumber = "4251234567";
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
|
|
@ -1521,26 +1482,25 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
public async Task VerifyTokenFromWrongTokenProviderFails()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
manager.RegisterTwoFactorProvider("PhoneCode", new SmsTokenProvider());
|
||||
manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider());
|
||||
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||
manager.RegisterTokenProvider(new EmailTokenProvider<TUser>());
|
||||
var user = CreateTestUser();
|
||||
user.PhoneNumber = "4251234567";
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
var token = await manager.GenerateTwoFactorTokenAsync(user, "PhoneCode");
|
||||
var token = await manager.GenerateTwoFactorTokenAsync(user, "Phone");
|
||||
Assert.NotNull(token);
|
||||
Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "EmailCode", token));
|
||||
Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Email", token));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task VerifyWithWrongSmsTokenFails()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
const string factorId = "PhoneCode";
|
||||
manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider());
|
||||
manager.RegisterTokenProvider(new PhoneNumberTokenProvider<TUser>());
|
||||
var user = CreateTestUser();
|
||||
user.PhoneNumber = "4251234567";
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue