public async Task <IActionResult> Validate(TwoFactorValidateModel model) { if (ModelState.IsValid) { var email = model.Email.Clean(); if (string.IsNullOrEmpty(email)) { return(Unauthorized()); } var user = await _userManager.FindByEmailAsync(email); if (user == null) { return(Unauthorized()); } var totpCode = model.TotpCode?.Replace(" ", string.Empty).Replace("-", string.Empty) ?? string.Empty; var twoFactorToken = user.Tokens.Where(x => x.LoginProvider == "Two-Factor").Select(x => x.Value).FirstOrDefault(); if (string.IsNullOrEmpty(twoFactorToken)) { return(Unauthorized()); } var totp = new Totp(Base32Encoding.ToBytes(twoFactorToken)); var computeTotp = totp.ComputeTotp(DateTime.UtcNow); if (totpCode.Equals(computeTotp)) { var additionalLocalClaims = new List <Claim>(); var localSignInProps = new AuthenticationProperties(); if (model.AuthenticationToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = model.AuthenticationToken } }); } // issue authentication cookie for user // we must issue the cookie maually, and can't use the SignInManager because // it doesn't expose an API to issue additional claims from the login workflow var principal = await _signInManager.CreateUserPrincipalAsync(user); additionalLocalClaims.AddRange(principal.Claims); var name = user.FullName; //principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id; var isuser = new IdentityServerUser(user.Id) { DisplayName = name, IdentityProvider = model.Provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = model.ReturnUrl ?? _appSettings.Application.MainSiteUrlTrim + "/Home/Index"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(model.Provider, model.ProviderUserId, user.Id, name, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return(this.LoadingPage("Redirect", returnUrl)); } } return(Redirect(returnUrl)); } return(Unauthorized()); } return(View(model)); }
public async Task <IActionResult> Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { _logger.Exception(result.Failure); string errMsg = "External authentication error - " + result.Failure.Message; errMsg += (result.Failure.InnerException != null) ? (" - " + result.Failure.InnerException.Message) : string.Empty; throw new HttpRequestException( CDCavell.ClassLibrary.Web.Html.StatusCodes.ToString(401), new Exception(errMsg), HttpStatusCode.Unauthorized ); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.Debug($"External claims: {externalClaims}"); } // lookup our user and external provider info var(user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = await AutoProvisionUserAsync(provider, providerUserId, claims); } // validate email address if (!user.EmailConfirmed) { // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); TempData["Email"] = user.Email; return(RedirectToAction("Index", "Registration")); } if (user.TwoFactorEnabled) { TwoFactorValidateModel model = new TwoFactorValidateModel(); model.Email = user.Email; model.Provider = provider; model.ProviderUserId = providerUserId; model.ReturnUrl = result.Properties.Items["returnUrl"]; // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = result.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { model.SessionId = sid.Value; } // if the external provider issued an id_token, we'll keep it for signout var idToken = result.Properties.GetTokenValue("id_token"); if (idToken != null) { model.AuthenticationToken = idToken; } TempData["TwoFactorValidateModel"] = JsonConvert.SerializeObject(model); return(RedirectToAction("Validate", "TwoFactor")); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List <Claim>(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user // we must issue the cookie maually, and can't use the SignInManager because // it doesn't expose an API to issue additional claims from the login workflow var principal = await _signInManager.CreateUserPrincipalAsync(user); additionalLocalClaims.AddRange(principal.Claims); var name = user.FullName; //principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id; var isuser = new IdentityServerUser(user.Id) { DisplayName = name, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, name, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return(this.LoadingPage("Redirect", returnUrl)); } } return(Redirect(returnUrl)); }