private (TestUser user, string provider, string providerUserId, IEnumerable <Claim> claims) FindUserFromExternalProvider(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); return(user, provider, providerUserId, claims); }
public async Task <IActionResult> ExternalLoginCallback(string returnUrl) { // read external identity from the temporary cookie var info = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); var tempUser = info?.Principal; if (tempUser == null) { throw new Exception("External authentication error"); } // retrieve claims of the external user var claims = tempUser.Claims.ToList(); // try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject); if (userIdClaim == null) { userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier); } if (userIdClaim == null) { throw new Exception("Unknown userid"); } // remove the user id claim from the claims collection and move to the userId property // also set the name of the external authentication provider claims.Remove(userIdClaim); var provider = info.Properties.Items["scheme"]; var userId = userIdClaim.Value; // check if the external user is already provisioned var user = _users.FindByExternalProvider(provider, userId); if (user == null) { // this sample simply auto-provisions new external user // another common approach is to start a registrations workflow first user = _users.AutoProvisionUser(provider, userId, claims); } var additionalClaims = new List <Claim>(); // if the external system sent a session id claim, copy it over var sid = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { additionalClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout AuthenticationProperties props = null; var id_token = info.Properties.GetTokenValue("id_token"); if (id_token != null) { props = new AuthenticationProperties(); props.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); } // issue authentication cookie for user await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, props, additionalClaims.ToArray()); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // validate return URL and redirect back to authorization endpoint if (_interaction.IsValidReturnUrl(returnUrl)) { return(Redirect(returnUrl)); } return(Redirect("~/")); }
public async Task <IActionResult> Callback() { // 从临时cookie读取外部标识 //var result = await HttpContext.AuthenticateAsync("QQ"); var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } // 检索外部用户的索赔 var externalUser = result.Principal; var claims = externalUser.Claims.ToList(); // 尝试确定外部用户的唯一ID(由提供商发布) // 最普通类型的索赔,索赔是子和nameidentifier // 根据外部提供商,可能会使用其他一些声明类型 var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject); if (userIdClaim == null) { userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier); } if (userIdClaim == null) { throw new Exception("Unknown userid"); } // 从声明集合中删除用户标识声明并移至userId属性 //还设置外部身份验证提供程序的名称 claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var userId = userIdClaim.Value; //这是最有可能需要自定义逻辑来匹配您的用户的地方 //外部提供商的身份验证结果,并根据需要配置用户。 // //检查外部用户是否已配置内部用户 var user = _users.FindByExternalProvider(provider, userId); if (user == null) { //此示例只是自动提供新的外部用户 //另一种常见方法是首先启动注册工作流程 user = _users.AutoProvisionUser(provider, userId, claims); } var additionalClaims = new List <Claim>(); //如果外部系统发送了会话ID声明,请复制它 //所以我们可以用它来进行单点登出 var sid = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { additionalClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } //如果外部提供者发出了id_token,我们会保留它以进行注销 AuthenticationProperties props = null; var id_token = result.Properties.GetTokenValue("id_token"); if (id_token != null) { props = new AuthenticationProperties(); props.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); } // 为用户发出身份验证Cookie await _events.RaiseAsync(new UserLoginSuccessEvent(provider, userId, user.SubjectId, user.Username)); await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, props, additionalClaims.ToArray()); //删除外部认证期间使用的临时cookie await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); //验证返回URL并重定向回授权端点或本地页面 var returnUrl = result.Properties.Items["returnUrl"]; if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl)) { return(Redirect(returnUrl)); } return(Redirect("~/")); }
public async Task <IActionResult> ExternalLoginCallback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); var returnUrl = result.Properties.Items["returnUrl"]; var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } // retrieve claims of the external user var externalUser = result.Principal; var claims = externalUser.Claims.ToList(); // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject); if (userIdClaim == null) { userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier); } if (userIdClaim == null) { throw new Exception("Unknown userid"); } // remove the user id claim from the claims collection and move to the userId property // also set the name of the external authentication provider claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var userId = userIdClaim.Value; // this is where custom logic would most likely be needed to match your users from the // external provider's authentication result, and provision the user as you see fit. // // check if the external user is already provisioned var user = _users.FindByExternalProvider(provider, userId); if (user != null) { claims.AddRange(user.Claims.ToList()); } if (user == null) { claims.Add(new Claim(JwtClaimTypes.Role, "User")); } var tenant = new Claim("tenant", context.Tenant.Split(".").First()); claims.Add(tenant); // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { claims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout AuthenticationProperties props = null; var id_token = result.Properties.GetTokenValue("id_token"); if (id_token != null) { props = new AuthenticationProperties(); props.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); } // issue authentication cookie for user await _events.RaiseAsync(new UserLoginSuccessEvent(provider, userId, user.SubjectId, user.Username)); await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, props, claims.ToArray()); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); // validate return URL and redirect back to authorization endpoint or a local page if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl)) { return(Redirect(returnUrl)); } return(Redirect("~/")); }
public async Task <IActionResult> OnGet() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } var externalUser = result.Principal; if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = externalUser.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); 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 // // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); } // 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(); CaptureExternalLoginContext(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, 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.SubjectId, user.Username, 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(returnUrl)); } } return(Redirect(returnUrl)); }
public async Task <IActionResult> IdpInitiatedCallback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = "saml"; // Cannot use scheme property... var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); 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 = AutoProvisionUser(provider, providerUserId, claims); } // this allows us to collect any additonal claims or properties // for the specific prtotocols 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(); ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps); ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps); ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username)); await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, localSignInProps, additionalLocalClaims.ToArray()); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); var returnUrl = "~/"; // return url must be known. It won't be in the cookie... // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context != null) { if (await _clientStore.IsPkceClientAsync(context.ClientId)) { // if the client is PKCE then we assume it's native, so this change in how to // return the response is for better UX for the end user. return(View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl })); } } return(Redirect(returnUrl)); }