امن سازی برنامههای ASP.NET Core توسط IdentityServer 4x - قسمت سیزدهم- فعالسازی اعتبارسنجی دو مرحلهای
نویسنده: وحید نصیری
تاریخ: ۱۳۹۷/۰۶/۲۸ ۱۵:۲۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
namespace DNT.IDP
{
public class Startup
{
public const string TwoFactorAuthenticationScheme = "idsrv.2FA";
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddAuthentication()
.AddCookie(authenticationScheme: TwoFactorAuthenticationScheme)
.AddGoogle(authenticationScheme: "Google", configureOptions: options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = Configuration["Authentication:Google:ClientId"];
options.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
});
} namespace DNT.IDP.Controllers.Account
{
[SecurityHeaders]
[AllowAnonymous]
public class AccountController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
// ...
if (ModelState.IsValid)
{
if (await _usersService.AreUserCredentialsValidAsync(model.Username, model.Password))
{
var user = await _usersService.GetUserByUsernameAsync(model.Username);
var id = new ClaimsIdentity();
id.AddClaim(new Claim(JwtClaimTypes.Subject, user.SubjectId));
await HttpContext.SignInAsync(scheme: Startup.TwoFactorAuthenticationScheme, principal: new ClaimsPrincipal(id));
await _twoFactorAuthenticationService.SendTemporaryCodeAsync(user.SubjectId);
var redirectToAdditionalFactorUrl =
Url.Action("AdditionalAuthenticationFactor",
new
{
returnUrl = model.ReturnUrl,
rememberLogin = model.RememberLogin
});
// request for a local page
if (Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(redirectToAdditionalFactorUrl);
}
if (string.IsNullOrEmpty(model.ReturnUrl))
{
return Redirect("~/");
}
// user might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL");
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await BuildLoginViewModelAsync(model);
return View(vm);
} await HttpContext.SignInAsync(scheme: Startup.TwoFactorAuthenticationScheme, principal: new ClaimsPrincipal(id));
public interface ITwoFactorAuthenticationService
{
Task SendTemporaryCodeAsync(string subjectId);
Task<bool> IsValidTemporaryCodeAsync(string subjectId, string code);
} using System.ComponentModel.DataAnnotations;
namespace DNT.IDP.Controllers.Account
{
public class AdditionalAuthenticationFactorViewModel
{
[Required]
public string Code { get; set; }
public string ReturnUrl { get; set; }
public bool RememberLogin { get; set; }
}
} namespace DNT.IDP.Controllers.Account
{
public class AccountController : Controller
{
[HttpGet]
public IActionResult AdditionalAuthenticationFactor(string returnUrl, bool rememberLogin)
{
// create VM
var vm = new AdditionalAuthenticationFactorViewModel
{
RememberLogin = rememberLogin,
ReturnUrl = returnUrl
};
return View(vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AdditionalAuthenticationFactor(
AdditionalAuthenticationFactorViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// read identity from the temporary cookie
var info = await HttpContext.AuthenticateAsync(Startup.TwoFactorAuthenticationScheme);
var tempUser = info?.Principal;
if (tempUser == null)
{
throw new Exception("2FA error");
}
// ... check code for user
if (!await _twoFactorAuthenticationService.IsValidTemporaryCodeAsync(tempUser.GetSubjectId(), model.Code))
{
ModelState.AddModelError("code", "2FA code is invalid.");
return View(model);
}
// login the user
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
}
// issue authentication cookie for user
var user = await _usersService.GetUserBySubjectIdAsync(tempUser.GetSubjectId());
await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username));
await HttpContext.SignInAsync(user.SubjectId, user.Username, props);
// delete temporary cookie used for 2FA
await HttpContext.SignOutAsync(Startup.TwoFactorAuthenticationScheme);
if (_interaction.IsValidReturnUrl(model.ReturnUrl) || Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
return Redirect("~/");
} @model AdditionalAuthenticationFactorViewModel
<div>
<div class="page-header">
<h1>2-Factor Authentication</h1>
</div>
@Html.Partial("_ValidationSummary")
<div class="row">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Input your 2FA code</h3>
</div>
<div class="panel-body">
<form asp-route="Login">
<input type="hidden" asp-for="ReturnUrl" />
<input type="hidden" asp-for="RememberLogin" />
<fieldset>
<div class="form-group">
<label asp-for="Code"></label>
<input class="form-control" placeholder="Code" asp-for="Code" autofocus>
</div>
<div class="form-group">
<button class="btn btn-primary">Submit code</button>
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div>