【问题标题】:Getting a Refresh Token from IdentitySever4从 IdentitySever4 获取刷新令牌
【发布时间】:2019-11-08 18:25:06
【问题描述】:

我有一个连接到不同 Identity Server 4 服务器的 Blazor Web 应用程序。我可以让登录正常工作并将访问令牌传回 Blazor。但是,当令牌到期时,我不知道如何出去获取新的访问令牌?我应该获得刷新令牌然后获得访问令牌吗?我对这一切如何运作感到困惑。

Blazor 代码

services.AddAuthentication(options =>
         {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = AzureADDefaults.AuthenticationScheme;
         })
         .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
         .AddOpenIdConnect(AzureADDefaults.AuthenticationScheme, options =>
         {
            options.Authority = "https://localhost:44382";
            options.RequireHttpsMetadata = true;

            options.ClientId = "client";
            options.ClientSecret = "secret";
            options.ResponseType = "code id_token token";
            options.SaveTokens = true;

            options.Scope.Add("IdentityServerApi");
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
            options.Scope.Add("roles");
            options.Scope.Add("offline_access");
         });

IdentityServer4 设置

...
        new Client
         {
            ClientId = "client",
            ClientSecrets = { new Secret("secret".Sha256()) },
            AllowedGrantTypes = GrantTypes.Hybrid,
            AllowAccessTokensViaBrowser = true,
            RequireClientSecret = true,

            RequireConsent = false,
            RedirectUris = { "https://localhost:44370/signin-oidc" },
            PostLogoutRedirectUris = { "https://localhost:44370/signout-callback-oidc" },
            AllowedScopes = { "openid", "profile", "email", "roles", "offline_access",
               IdentityServerConstants.LocalApi.ScopeName
            },
            AllowedCorsOrigins = { "https://localhost:44370" },

            AlwaysSendClientClaims = true,
            AlwaysIncludeUserClaimsInIdToken = true,

            AllowOfflineAccess = true,
            AccessTokenLifetime = 1,//testing
            UpdateAccessTokenClaimsOnRefresh = true
         },
...

更新:

我已将客户端和服务器的代码更新为 offline_access(感谢下面的更新)。我的下一个问题是,一旦我因为访问令牌过期而被拒绝,如何在 Blazor 中注入刷新令牌的请求?

我让 Blazor 应用回调 API(验证访问令牌)。

   public class APIClient : IAPIClient
   {
      private readonly HttpClient _httpClient;

      //add the bearer token to the APIClient when the client is used
      public APIClient(IHttpContextAccessor httpAccessor, HttpClient client, IConfiguration configuration)
      {
         var accessToken = httpAccessor.HttpContext.GetTokenAsync("access_token").Result;
         client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
         client.DefaultRequestVersion = new Version(2, 0);
         client.BaseAddress = new Uri(configuration["Api_Location"]);
         _httpClient = client;
         _logger = logger;
      }

我需要在 API 调用中添加什么来验证?

【问题讨论】:

    标签: identityserver4 refresh-token blazor-server-side


    【解决方案1】:

    是的,您还应该获取刷新令牌以继续获取新的访问令牌。要从 IdentityServer 获取刷新令牌,您需要在客户端的“AllowedScopes”属性中添加“offline_access”范围。您还需要将客户端上的“AllowOfflineAccess”属性设置为 true。

    之后,您需要在客户端发送的范围中包含“offline_access”,并且您应该在响应中收到一个刷新令牌。

    要使用刷新令牌,请使用您为代码交换发送的所有内容向令牌端点发送请求,但将“code”参数替换为“refresh_token”并将“grant_type”的值从“code”更改为“refresh_token” '。 IdentityServer4 对此请求的响应应包含一个 id_token、一个 access_token 和一个新的 refresh_token。

    【讨论】:

      【解决方案2】:

      我想我已经找到了答案(鉴于 Randy 的推动)。我对this post 做了一些熟悉的事情,在我的 APIClient 中创建了一个泛型方法。

            public async Task<T> SendAsync<T>(HttpRequestMessage requestMessage)
            {
               var response = await _httpClient.SendAsync(requestMessage);
               //test for 403 and actual bearer token in initial request
               if (response.StatusCode == HttpStatusCode.Unauthorized &&
                   requestMessage.Headers.Where(c => c.Key == "Authorization")
                           .Select(c => c.Value)
                           .Any(c => c.Any(p => p.StartsWith("Bearer"))))
               {
                  var pairs = new List<KeyValuePair<string, string>>
                  {
                      new KeyValuePair<string, string>("grant_type", "refresh_token"),
                      new KeyValuePair<string, string>("refresh_token", _httpAccessor.HttpContext.GetTokenAsync("refresh_token").Result),
                      new KeyValuePair<string, string>("client_id", "someclient"),
                      new KeyValuePair<string, string>("client_secret", "*****")
                  };
      
                  //retry do to token request
                  using (var refreshResponse = await _httpClient.SendAsync(
                      new HttpRequestMessage(HttpMethod.Post, new Uri(_authLocation + "connect/token"))
                      {
                         Content = new FormUrlEncodedContent(pairs)})
                     )
                  {
                     var rawResponse = await refreshResponse.Content.ReadAsStringAsync();
                     var x = Newtonsoft.Json.JsonConvert.DeserializeObject<Data.Models.Token>(rawResponse);
                     var info = await _httpAccessor.HttpContext.AuthenticateAsync("Cookies");
      
                     info.Properties.UpdateTokenValue("refresh_token", x.Refresh_Token);
                     info.Properties.UpdateTokenValue("access_token", x.Access_Token);
                     _httpClient.DefaultRequestHeaders.Clear();
                     _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", x.Access_Token);
      
                     //retry actual request with new tokens
                     response = await _httpClient.SendAsync(new HttpRequestMessage(requestMessage.Method, requestMessage.RequestUri));
      
                  }
               }
               if (typeof(T).Equals(typeof(HttpResponseMessage)))
                  return (T)Convert.ChangeType(response, typeof(T));
               else
                  return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
            }
      

      我不喜欢我必须调用 AuthenticateAsync。然而,这似乎是我发现访问 UpdateTokenValue 方法以删除然后重新添加新访问令牌的方式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-03-22
        • 2021-08-25
        • 2012-05-04
        • 2012-06-29
        • 2016-10-13
        • 2015-01-08
        • 2019-09-09
        相关资源
        最近更新 更多