身份服务器不返回刷新令牌

Posted

技术标签:

【中文标题】身份服务器不返回刷新令牌【英文标题】:Identity Server not returning refresh token 【发布时间】:2015-10-01 20:22:12 【问题描述】:

我正在尝试设置 Thinktecture 的 Identity Server 3,但在交换授权代码时(或使用 ResourceOwner 流时,我似乎无法让它返回刷新令牌,但我将专注于授权码,因为它现在对我来说更重要)。我取回了访问令牌并可以很好地使用它们进行身份验证,但它似乎甚至没有生成我期望取回的刷新令牌。为了让 Identity Server 返回刷新令牌,我需要做些什么特别的事情吗?

我查看了文档,但没有看到我设置错误的任何内容,而我在refresh tokens 上的页面上唯一我没有做的事情是明确请求“offline_access”范围将用户发送到那里进行身份验证时,因为每当我尝试时都会收到“无效范围”错误。因此,我将 Thinktecture 的“请求 offline_access 范围(通过代码或资源所有者流程)”的措辞表示,offline_access 范围是根据您使用的流程自动请求的。

我一直在尽可能地关注他们的示例应用程序(以及来自Katana Project 的现有 Owin 中间件的源代码),我的设置如下:

我使用他们的客户端类创建了一个客户端,手动指定以下内容:
var 客户端 = 新客户端()

    ClientId = "SomeId",
    ClientName = "具有身份验证代码流的客户端",
    RequireConsent = false, //将此设置为 true 没有帮助
    Flow = Flows.AuthorizationCode,
    ClientSecrets = 新列表() 
        新客户秘密(“秘密”)
    ,
    RedirectUris = 新列表()
    
        “本地主机:/特定重定向路径”
    
;
我正在调用授权端点,如下所示:
var 授权端点 =
    AuthorizationEndpointBase +
    "?client_id=" + Uri.EscapeDataString(Options.ClientId) +
    "&范围=默认" +
    "&response_type=代码" +
    "&redirect_uri=" + Uri.EscapeDataString(redirectUri) +
    "&state=" + Uri.EscapeDataString(state);
Response.Redirect(authorizationEndpoint);
其中“默认”是我创建的范围。 在我的回调中,我调用令牌端点如下:
IReadableStringCollection 查询 = Request.Query;
string code = getValueFromQueryString("code", query);
var tokenRequestParameters = 新列表>()
    
        新的 KeyValuePair("client_id", Options.ClientId),
        新的 KeyValuePair("redirect_uri", GenerateRedirectUri()),
        新的 KeyValuePair("client_secret", Options.ClientSecret),
        新的 KeyValuePair(“代码”,代码),
        新的 KeyValuePair("grant_type", "authorization_code"),
    ;
var requestContent = new FormUrlEncodedContent(tokenRequestParameters);
HttpResponseMessage 响应 = 等待 _httpClient.PostAsync(TokenEndpoint, requestContent, Request.CallCancelled);
response.EnsureSuccessStatusCode();
字符串 oauthTokenResponse = 等待 response.Content.ReadAsStringAsync();

当我调用令牌端点时,我在 Identity Server 上的登录会显示以下内容(在验证授权码之后):

 iisexpress.exe 信息: 0 : [Thinktecture.IdentityServer.Core.Validation.TokenRequestValidator]: 7/13/2015 1:44:07 PM +00:00 -- 令牌请求验证成功
     
      "ClientId": "SomeId",
      "ClientName": "具有身份验证代码流的客户端",
      "GrantType": "授权码",
      “授权码”:“f8f795e649044067ebd96a341c5af8c3”
    
    iisexpress.exe 信息:0:[Thinktecture.IdentityServer.Core.ResponseHandling.TokenResponseGenerator]:7/13/2015 1:44:07 PM +00:00 -- 创建令牌响应
    iisexpress.exe 信息:0 : [Thinktecture.IdentityServer.Core.ResponseHandling.TokenResponseGenerator]: 7/13/2015 1:44:07 PM +00:00 -- 处理授权码请求
    调试:[Thinktecture.IdentityServer.Core.Services.Default.DefaultTokenService]:7/13/2015 1:44:07 PM +00:00 -- 创建访问令牌
    调试:[Thinktecture.IdentityServer.Core.Services.Default.DefaultTokenService]:7/13/2015 1:44:07 PM +00:00 -- 创建参考访问令牌
    iisexpress.exe 信息:0:[Thinktecture.IdentityServer.Core.Endpoints.TokenEndpointController]:7/13/2015 1:44:07 PM +00:00 -- 结束令牌请求
    iisexpress.exe 信息:0 : [Thinktecture.IdentityServer.Core.Results.TokenResult]: 7/13/2015 1:44:07 PM +00:00 -- 返回令牌响应。

我不确定还有什么相关的,所以我会根据需要提供更多信息。

【问题讨论】:

您好,对于不相关的评论很抱歉,但无论如何我可以获取您使用生成刷新令牌等的身份服务器构建的模板吗? 很遗憾,我正在构建它作为我不再参与的人员扩充的一部分,因此我无权访问代码库,也无权发布它。 【参考方案1】:

您必须在请求中明确要求“offline_access”。用空格分隔您请求的其他范围。 (在下面的示例中,我将 'Default' 替换为 'MyApi' 以明确我们正在谈论您的应用定义的范围。)

&scope=MyApi offline_access 

但是,您还必须授予该客户端获取刷新令牌的权利,这不仅仅取决于您选择的流程:

var client = new Client()

    ... //All the stuff you were doing before

    ScopeRestrictions = new List<string>
     
        "MyApi",
        StandardScopes.OfflineAccess.Name, //"offline_access" -for refresh tokens
        //Other commonly requested scopes:
        //StandardScopes.OpenId.Name, //"openid"
        //StandardScopes.Email.Name,  //"email"

    ,

您可能还需要将“offline_access”添加到您的范围存储。范围存储是 Identity Server 知道的范围列表。您的问题没有提到您的范围存储是如何在您的项目中设置的,因此您可能已经拥有它。但是,如果上述方法不能立即为您工作,您可能需要在您正在使用的示例中查找类似这样的代码并添加 OfflineAccess。

var scopeStore = new InMemoryScopeStore(new Scope[]
    StandardScopes.OpenId,
    StandardScopes.Profile,
    StandardScopes.Email,
    StandardScopes.OfflineAccess,  //<--- ensure this is here to allow refresh tokens
    new Scope
        Enabled = true,
        Name = "MyApi"
    ,

【讨论】:

谢谢,成功了!我不知道我是怎么做到的,但不知何故,当我将 StandardScopes 添加到数据库时,范围没有被包括在内,而且我在之前手动查看它们时仍然设法错过了它,这就是为什么我当时认为也许它是自动神奇地确定的。这更有意义。 我忘记在我的范围存储中包含StandardScopes.OfflineAccess,感谢您提及该部分。 注意:StandardScopes.All 不包括离线访问范围。 (在我看来这很奇怪)。 正是我所需要的!【参考方案2】:

在发送令牌请求时在范围内添加 offline_access 值

new Client
            
                ClientId = "ro.angular",
                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

                ClientSecrets =
                
                    new Secret("secret".Sha256())
                ,
                AllowedScopes = 
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.Email,
                    IdentityServerConstants.StandardScopes.Address,
                    "api1",
                    IdentityServerConstants.StandardScopes.OfflineAccess
                ,
                AllowOfflineAccess = true,
                RefreshTokenUsage = TokenUsage.ReUse,
                RefreshTokenExpiration = TokenExpiration.Sliding

            

【讨论】:

以上是关于身份服务器不返回刷新令牌的主要内容,如果未能解决你的问题,请参考以下文章

使用访问令牌和刷新令牌保持身份验证

Android 刷新令牌

何时用刷新令牌交换访问令牌

带有刷新令牌的 Spring Google OAuth2

Spring OAuth2刷新令牌刷新访问令牌后更改

OpenID Connect:不同身份提供者之间的刷新令牌行为不一致