Identity Server not returning refresh token
Asked Answered
T

2

16

I'm trying to set up Thinktecture's Identity Server 3, but I can't seem to get it to return a refresh token when exchanging an authorization code (or when using the ResourceOwner flow, but I'm going to focus on the authorization code as it's more important to me right now). I get back access tokens and can use them to authenticate just fine, but it doesn't seem to even be generating the refresh tokens that I'm expecting to get back. Is there anything special that I need to do to get Identity Server to return refresh tokens?

I've looked through the documentation, but haven't seen anything that I've set up wrong, and the only thing on their page on refresh tokens that I'm not doing is explicitly requesting the "offline_access" scope when sending the user there for authentication, because whenever I try I get an "invalid scope" error. Therefore, I'm taking Thinktecture's phrasing of "Request the offline_access scope (via code or resource owner flow)" to mean that the offline_access scope is something automatically requested based on the flow you're using.

I've been trying to follow their sample applications (And the source code for the existing Owin Middleware from the Katana Project) as best I can, and my setup is as follows:

  • I've created a client using their client class, manually specifying the following:
    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"
        }
    };
  • I'm making a call to the Authorization endpoint as follows:
    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);
    where "Default" is a scope I created.
  • In my callback, I call the token endpoint as follows:
    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();
    

When I make the call to the token endpoint, my logging on Identity Server displays the following (after the validation of the authorization code):

    iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.Validation.TokenRequestValidator]: 7/13/2015 1:44:07 PM +00:00 -- Token request validation success
     {
      "ClientId": "SomeId",
      "ClientName": "Client with Authentication Code Flow",
      "GrantType": "authorization_code",
      "AuthorizationCode": "f8f795e649044067ebd96a341c5af8c3"
    }
    iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.ResponseHandling.TokenResponseGenerator]: 7/13/2015 1:44:07 PM +00:00 -- Creating token response
    iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.ResponseHandling.TokenResponseGenerator]: 7/13/2015 1:44:07 PM +00:00 -- Processing authorization code request
    Debug: [Thinktecture.IdentityServer.Core.Services.Default.DefaultTokenService]: 7/13/2015 1:44:07 PM +00:00 -- Creating access token
    Debug: [Thinktecture.IdentityServer.Core.Services.Default.DefaultTokenService]: 7/13/2015 1:44:07 PM +00:00 -- Creating reference access token
    iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.Endpoints.TokenEndpointController]: 7/13/2015 1:44:07 PM +00:00 -- End token request
    iisexpress.exe Information: 0 : [Thinktecture.IdentityServer.Core.Results.TokenResult]: 7/13/2015 1:44:07 PM +00:00 -- Returning token response.

I'm not sure what else would be pertinent, so I'll provide more information as needed.

Tyrr answered 13/7, 2015 at 14:5 Comment(2)
Hi, sorry for unrelated comment, but is there anyway I can get the template you have built using identity server that generates refresh tokens, etc.?Wintergreen
Regrettably, I was building that as a part of a staff augmentation that I'm no longer on, so I don't have access to the codebase nor permission to post it.Tyrr
Q
48

You do have to explicitly ask for 'offline_access' in your request. Separate the other scopes you are requesting with a space. (In my examples below I am replacing 'Default' with 'MyApi' to be clear that we are talking about a scope defined by your app.)

&scope=MyApi offline_access 

However, you must also grant that client the right to get refresh tokens, it doesn't just happen based on the flow you pick:

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"

    },
}

You may need to add 'offline_access' to your scope store as well. The scope store is the list of scopes that Identity Server knows about. Your question doesn't mention how your scope store is set up in your project, so you may already have it. But if the above doesn't immediately work for you, you may want to look around for code like this in the example you're working from and add 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"
    },
}
Qualm answered 14/7, 2015 at 17:12 Comment(4)
Thanks, that did it! I don't know how I pulled it off, but somehow the scope didn't get included when I added the StandardScopes to the database, and I still managed to miss it when manually looking at them earlier, which is why I then thought that maybe it was auto-magically determined. This makes MUCH more sense.Tyrr
I had forgotten to include the StandardScopes.OfflineAccess in my scope store, thanks for mentioning that part.Petronius
A note: StandardScopes.All does not include the offline access scope. (Which is very strange in my opinion).Peddler
Just what I needed!Pedagogy
N
4

Add offline_access value in scope while sending token request

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

            }

Get Refresh token

Get new access token with refresh token

Ninurta answered 5/12, 2019 at 7:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.