首页 > 解决方案 > 将外部登录令牌从 Identity Server 流向客户端应用程序

问题描述

我已经基于 .net core 2.2 设置了 IdentityServer4,并使用 OpenIdConnect 中间件将 Xero 配置为外部登录。我有一个为身份验证配置 IdentityServer 的客户端应用程序。我喜欢在客户端应用程序中访问的不仅是来自 IdentityServer 的身份验证令牌,还有来自外部登录的令牌。有一个MS 文档建议将外部登录令牌包含在OnGetCallbackAsync

var props = new AuthenticationProperties();
props.StoreTokens(info.AuthenticationTokens);
props.IsPersistent = true;
await _signInManager.SignInAsync(user, props);

由于我的 IdentityServer 模板没有OnGetCallbackAsync方法,我假设从上面的ExternalLoginCallback操作中实现ExternalLoginController将完成这项工作(我可能错了):

public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
        var context = await _interactionService.GetAuthorizationContextAsync(returnUrl);

        if (remoteError != null)
        {
            ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");

            return View($"~/Login/{nameof(LoginController.Login)}");
        }

        var info = await _signInManager.GetExternalLoginInfoAsync();

        if (info == null)
        {
            return RedirectToAction(nameof(LoginController.Login), "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,
            Constants.AuthenticationProps.Defaults.IsPersistent);

        var emailClaim = ClaimTypes.Email;

        if (result.Succeeded)
        {                
            var user = await _userManager.FindByNameAsync(info.Principal.FindFirstValue(emailClaim));

            var props = new AuthenticationProperties();
            props.StoreTokens(info.AuthenticationTokens);
            props.IsPersistent = true;
            await _signInManager.SignInAsync(user, props, info.LoginProvider);

            await _events.RaiseAsync(new UserLoginSuccessEvent(user.Email, user.Id.ToString(), $"{user.GivenName} {user.FamilyName}"));
            await _events.RaiseAsync(new UserLoginEvent(user.Id, context?.ClientId));
            _logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
            return RedirectToLocal(returnUrl);
        } ...

所以登录工作正常,我HttpContext.GetTokenAsync("access_token")在客户端应用程序(asp.net core)中获得了身份服务器令牌,但仍然无法弄清楚如何在客户端应用程序中访问外部登录令牌。我不确定我是否遗漏了什么,或者这是否是将外部登录令牌流向我的客户端应用程序的正确方法,如果是,如何访问AuthenticationTokens客户端应用程序中的那些?

这是我的客户端应用程序 openidconnect 配置供参考:

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie(
            CookieAuthenticationDefaults.AuthenticationScheme,
            options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
            options.Cookie.Name = "xero";
            options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        })
        .AddOpenIdConnect("oidc", options =>
        {
            options.Authority = "https://localhost:44333";//IdentityServer address
            options.RequireHttpsMetadata = true;
            options.ClientId = "MyAppClientId";
            options.ClientSecret = "MyAppClientSecret";
            options.ResponseType = "code id_token";
            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;
            options.Scope.Clear();
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
            options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce", "iat", "c_hash");

            options.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.Name,
                RoleClaimType = JwtClaimTypes.Role
            };

            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true
            };

            options.Events = new OpenIdConnectEvents
            {
                OnRedirectToIdentityProvider = ctx =>
                {
                    if (ctx.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Authentication)
                    {
                        ctx.ProtocolMessage.AcrValues = "idp:xero";
                    }
                    return Task.CompletedTask;
                }
            };
        });

标签: authenticationasp.net-coreidentityserver4access-tokenopenid-connect

解决方案


如果在 Identity Server 中实现外部登录,Identity server 从外部提供者接收到 id 令牌/访问令牌后,它将解码令牌并获取用户的声明,登录用户,然后创建身份服务器自己的令牌,最后返回到您的客户端应用程序。Identity Server 不会处理来自外部提供者的令牌,但您可以通过以下Callback方法获取令牌ExternalController

var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);

//get the tokens
var tokens = result.Properties.GetTokens();
var idToken = tokens.Where(x => x.Name.Equals("id_token")).FirstOrDefault().Value;

然后,您可以保留所需的任何数据、缓存令牌并返回到客户端,例如,在此示例中的令牌响应中。当然,您的客户端应用程序也可以在身份验证后发出另一个请求以获取令牌(在 中保留令牌ExternalController)。


推荐阅读