Oauth 2.0 标识服务器未返回刷新令牌

Oauth 2.0 标识服务器未返回刷新令牌,oauth-2.0,identityserver3,Oauth 2.0,Identityserver3,我正在尝试设置Thinktecture的Identity Server 3,但在交换授权代码时(或者在使用ResourceOwner流时,我似乎无法让它返回刷新令牌,但我将重点关注授权代码,因为它现在对我更重要)。我得到了访问令牌,可以使用它们进行身份验证,但它似乎甚至没有生成我期望得到的刷新令牌。要让Identity Server返回刷新令牌,我需要做什么特殊的事情吗 我查看了文档,但没有发现任何设置错误的地方,在他们的页面上,我唯一没有做的事情是在发送用户进行身份验证时显式请求“offlin

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

我查看了文档,但没有发现任何设置错误的地方,在他们的页面上,我唯一没有做的事情是在发送用户进行身份验证时显式请求“offline_access”作用域,因为每次尝试时都会出现“invalid scope”错误。因此,我将Thinktecture的“请求脱机\u访问范围(通过代码或资源所有者流)”理解为脱机\u访问范围是根据您使用的流自动请求的内容

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

  • 我已经使用客户机类创建了一个客户机,手动指定以下内容: var client = new Client() { ClientId = "SomeId", ClientName = "Client with Authentication Code Flow", RequireConsent = false, //Setting this to true didn't help Flow = Flows.AuthorizationCode, ClientSecrets = new List() { new ClientSecret("secret") }, RedirectUris = new List() { "localhost:/specific-redirect-path" } }; var client=new client() { ClientId=“SomeId”, ClientName=“具有身份验证代码流的客户端”, RequiredSent=false,//将此设置为true没有帮助 Flow=Flows.AuthorizationCode, ClientSecrets=新列表(){ 新客户机密(“机密”) }, 重定向URI=新列表() { “本地主机:/specific重定向路径” } };
  • 我正在调用授权端点,如下所示: var authorizationEndpoint = AuthorizationEndpointBase + "?client_id=" + Uri.EscapeDataString(Options.ClientId) + "&scope=Default" + "&response_type=code" + "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + "&state=" + Uri.EscapeDataString(state); Response.Redirect(authorizationEndpoint); IReadableStringCollection query = Request.Query; string code = getValueFromQueryString("code", query); var tokenRequestParameters = new List>() { new KeyValuePair("client_id", Options.ClientId), new KeyValuePair("redirect_uri", GenerateRedirectUri()), new KeyValuePair("client_secret", Options.ClientSecret), new KeyValuePair("code", code), new KeyValuePair("grant_type", "authorization_code"), }; var requestContent = new FormUrlEncodedContent(tokenRequestParameters); HttpResponseMessage response = await _httpClient.PostAsync(TokenEndpoint, requestContent, Request.CallCancelled); response.EnsureSuccessStatusCode(); string oauthTokenResponse = await response.Content.ReadAsStringAsync(); var授权端点= 授权端点库+ “?client_id=“+Uri.EscapeDataString(Options.ClientId)+ “&scope=Default”+ “&响应类型=代码”+ “&redirect_uri=“+uri.EscapeDataString(redirectUri))+ “&state=“+Uri.EscapeDataString(state); 重定向(授权端点); 其中“Default”是我创建的范围
  • 在回调中,我调用令牌端点,如下所示: var authorizationEndpoint = AuthorizationEndpointBase + "?client_id=" + Uri.EscapeDataString(Options.ClientId) + "&scope=Default" + "&response_type=code" + "&redirect_uri=" + Uri.EscapeDataString(redirectUri) + "&state=" + Uri.EscapeDataString(state); Response.Redirect(authorizationEndpoint); IReadableStringCollection query = Request.Query; string code = getValueFromQueryString("code", query); var tokenRequestParameters = new List>() { new KeyValuePair("client_id", Options.ClientId), new KeyValuePair("redirect_uri", GenerateRedirectUri()), new KeyValuePair("client_secret", Options.ClientSecret), new KeyValuePair("code", code), new KeyValuePair("grant_type", "authorization_code"), }; var requestContent = new FormUrlEncodedContent(tokenRequestParameters); HttpResponseMessage response = await _httpClient.PostAsync(TokenEndpoint, requestContent, Request.CallCancelled); response.EnsureSuccessStatusCode(); string oauthTokenResponse = await response.Content.ReadAsStringAsync(); IReadableStringCollection query=Request.query; 字符串代码=getValueFromQueryString(“代码”,查询); var tokenRequestParameters=新列表>() { 新的KeyValuePair(“客户端id”,Options.ClientId), 新的KeyValuePair(“重定向uri”,GenerateRedirectUri()), 新的KeyValuePair(“client_secret”,Options.ClientSecret), 新的KeyValuePair(“代码”,代码), 新的KeyValuePair(“授权类型”、“授权代码”), }; var requestContent=new FormUrlEncodedContent(tokenRequestParameters); HttpResponseMessage response=wait_httpClient.PostAsync(令牌端点、请求内容、请求.CallCancelled); response.EnsureSuccessStatusCode(); 字符串oauthTokenResponse=await response.Content.ReadAsStringAsync();
当我调用令牌端点时,我在Identity Server上的登录显示以下内容(在验证授权代码之后):

iisexpress.exe信息:0:[Thinktecture.IdentityServer.Core.Validation.TokenRequestValidator]:7/13/2015 1:44:07 PM+00:00--令牌请求验证成功 { “ClientId”:“SomeId”, “客户端名称”:“具有身份验证代码流的客户端”, “授权类型”:“授权码”, “授权代码”:“f8f795e649044067ebd96a341c5af8c3” } iisexpress.exe信息:0:[Thinktecture.IdentityServer.Core.ResponseHandling.TokenResponseGenerator]:2015年7月13日下午1:44:07+00:00--创建令牌响应 iisexpress.exe信息:0:[Thinktecture.IdentityServer.Core.ResponseHandling.TokenResponseGenerator]:2015年7月13日下午1:44:07+00:00--处理授权码请求 调试:[Thinktecture.IdentityServer.Core.Services.Default.DefaultTokenService]:2015年7月13日下午1:44:07+00:00--创建访问令牌 调试:[Thinktecture.IdentityServer.Core.Services.Default.DefaultTokenService]:2015年7月13日下午1:44:07+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]:2015年7月13日下午1:44:07+00:00--返回令牌响应。
我不确定还有什么是相关的,因此我将根据需要提供更多信息。

您必须在请求中明确要求“脱机访问”。用空格分隔您请求的其他作用域。(在我下面的示例中,我将“Default”替换为“MyApi”,以明确我们讨论的是应用程序定义的范围。)

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

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"

    },
}
var client=new client()
{
…//你以前做过的所有事情
ScopeRestrictions=新列表
{ 
“MyApi”,
StandardScopes.OfflineAccess.Name,//“脱机访问”-用于刷新令牌
//其他通常要求的范围:
//StandardScopes.OpenId.Name,//“OpenId”
//StandardScopes.Email.Name,//“电子邮件”
},
}
您可能还需要将“脱机访问”添加到范围存储中。作用域存储是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"
    },
}
var scopeStore=newinmemoryscopestore(新范围[]){
StandardScopes.OpenId,
StandardScopes.Profile,
StandardScopes.Email,

StandardScopes.OfflineAccess,//在发送令牌请求时在作用域中添加脱机访问值

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

            }