title: Flow external login tokens from the Identity Server to the client app
body: <p>I have setup IdentityServer4 based on .net core 2.2 and configured Xero as an External Login using OpenIdConnect middleware. I have a client app which configures the IdentityServer for Authentication. What I like to access in the client app is not only authentication tokens from the IdentityServer but also the tokens from External login. There is a <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/additional-claims?view=aspnetcore-2.2" rel="nofollow noreferrer">MS documentation</a> which suggests to include the external login tokens in <code>OnGetCallbackAsync</code> :</p>
  
  <pre><code>var props = new AuthenticationProperties();
  props.StoreTokens(info.AuthenticationTokens);
  props.IsPersistent = true;
  await _signInManager.SignInAsync(user, props);
  </code></pre>
  
  <p>Since my IdentityServer template doesn't have <code>OnGetCallbackAsync</code> method, I assumed implementing above in <code>ExternalLoginCallback</code> action from <code>ExternalLoginController</code> will do the job (I may be wrong):</p>
  
  <pre><code>public async Task&lt;IActionResult&gt; 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);
          } ...
  </code></pre>
  
  <p>So the login is working and I get Identity Server tokens by <code>HttpContext.GetTokenAsync("access_token")</code> in the client app(asp.net core) however still can't figure out how to access external login tokens in the client app. I'm not sure if I'm missing something or whether this is the correct approach to flow the external login tokens to my client app and if so how to access those <code>AuthenticationTokens</code> in the client app?</p>
  
  <p>here is my client app openidconnect configuration for reference :</p>
  
  <pre><code>        services.AddAuthentication(options =&gt;
          {
              options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
              options.DefaultChallengeScheme = "oidc";
          })
          .AddCookie(
              CookieAuthenticationDefaults.AuthenticationScheme,
              options =&gt;
          {
              options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
              options.Cookie.Name = "xero";
              options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
          })
          .AddOpenIdConnect("oidc", options =&gt;
          {
              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 =&gt;
                  {
                      if (ctx.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Authentication)
                      {
                          ctx.ProtocolMessage.AcrValues = "idp:xero";
                      }
                      return Task.CompletedTask;
                  }
              };
          });
  </code></pre>