Identityserver4: Client relationships enhancement

Created on 26 Feb 2017  Â·  12Comments  Â·  Source: IdentityServer/IdentityServer4

Initial Problem

In some case the downstream usage of the refresh_tokens should not be require to provide a client_secret.

The following is my test client that I use to create the refresh_token.

var resourceOwnerClient = new Client
{
      ClientId = "resource-owner-client",
      AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
      AllowOfflineAccess = true,
      RefreshTokenUsage = TokenUsage.OneTimeOnly,
      ClientSecrets =
      {
           new Secret("secret".Sha256())
      },
      AllowedScopes = { "arbitrary"}
};

I successfully can get a refresh_token, and successfully get another downstream, but only when I produce the same clientId/clientSecret. _All as expected and documented_.

What IdentityServer4 code says

class ClientSecretValidator 
{
    public async Task<ClientSecretValidationResult> ValidateAsync(HttpContext context)
    {
            .....
            if (!client.RequireClientSecret || client.IsImplicitOnly())
            {
                _logger.LogDebug("Public Client - skipping secret validation success");
            }
           else
            {
                 .....
            }
            .....
    }
}

Neither of which fits here as I want the client that created this to have a client_secret.

Before I go forking

I think in all cases a relationship has to be established between 2 clients;

var resourceOwnerClient = new Client
{
      ClientId = "public-resource-owner-client",
      InheritedClient= "resource-owner-client",
      RequireClientSecret = false
};

Client Morphing during a grant_type=refresh_token call

class ClientSecretValidator 
{
    public async Task<ClientSecretValidationResult> ValidateAsync(HttpContext context)
    {
            .....
            client.MorphToParentIfInherited();
            if (!client.RequireClientSecret 
                || client.IsImplicitOnly())
            {
                _logger.LogDebug("Public Client - skipping secret validation success");
            }
           else
            {
                 .....
            }
            .....
    }
}
  1. I create a refresh_token using resource-owner-client
  2. I give out refresh_token and what client to use (public-resource-owner-client) to the public
  3. public apps will be coming in with
grant_type=refresh_token&refresh_token={refresh_token}&client_id=public-resource-owner-client
  1. IdentityServer4 will for this request morph the public-resource-owner-client into resource-owner-client taking all the settings that make sense with it. _In this case changing the RequireClientSecret from a true to a false._

Using the ClientSecret of the parent at the time or creation

In this possible solution, I would use the public-resource-owner-client to create the offline_access token using the ClientSecret of resource-owner-client. _I prefer this approach because it would result in the produced access_token to contain "client_id": "public-resource-owner-client"._

Question: I haven't played with anything where RequireClientSecret=false. If the access_token, and by association the refresh_token contain the "client_id" which created them, why is it required to be passed as what appears to be a redundant argument? I would say that at a minimum I should need nothing more than the following;

grant_type=refresh_token&refresh_token={refresh_token}
vs
grant_type=refresh_token&refresh_token={refresh_token}&client_id=public-resource-owner-client

Finally

In your opinion is this a reasonable feature that IdentityServer4 should take up, provided it doesn't break the specification?

If it does break the spec, is it still reasonable to allow it to occur through abstraction thus pushing the responsibility of breaking the spec to me?

question

Most helpful comment

Glad you have it working! Changing the interface is a breaking change and can be considered for the next major release.

Please open a new issue that details the changes you have in mind.

All 12 comments

Yea - all this morphing and inheriting is nothing I can see in our core.

Why don't you use an extension grant to implement your scenario?

OK, given that would you consider exposing a higher level way of dealing with refresh_tokens.
https://identityserver4.readthedocs.io/en/release/topics/extension_grants.html
In your extensions grant example of DelegationGrantValidator you inject ITokenValidator, which only deals with access_tokens and identity_tokens. From the research I have done, it would nice to have what exists in ITokenRequestValidator. I tried injecting ITokenRequestValidator and got DI exceptions, but I would guess you might not want me to use that one as at that point I am already being called from your instance of that object.

I am a good deal down the path of getting something working, but I had to lift a lot of your code out of TokenRequestValidator.ValidateRefreshTokenRequestAsync(NameValueCollection parameters) and injecting ITokenResponseGenerator.

I am basically reproducing a refresh_token version of TokenRequestValidator, which is starting to feel wrong.

Since your stuff created the offline_access to begin with, I was hoping for you to expose a way to refresh it.
Thanks

Maybe it would help if you explain what you really want to achieve and why. I still don't get it.

  1. I have a client that is configured as follows;
var resourceOwnerClient = new Client
{
      ClientId = "resource-owner-client",
      AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
      AllowOfflineAccess = true,
      RefreshTokenUsage = TokenUsage.OneTimeOnly,
      ClientSecrets =
      {
           new Secret("secret".Sha256())
      },
      AllowedScopes = { "arbitrary"}
};
  1. I am able to use this client to give me an access_token, refresh_token, etc. using the offline_access scope.
  2. I am able to use this refresh_token to refresh, provided I pass all the necessary arguement. clientId, clientSecret, refresh_token, etc.
  3. Everything up to this point is as expected, documented, all good.
  4. I am giving this refresh_token, to an untrusted endpoint. This is very similar to what is described here. https://identityserver4.readthedocs.io/en/release/topics/extension_grants.html
  5. The untrusted endpoint is a mobile app.
  6. I would like the Mobil App to have the ability to use the refresh_token, as I did in step 3 to get new tokens, however I will not be sharing my client_secret with them.

Going down the extension_grants path, I theorized that I should be able to do the following;

http://localhost:7791/connect/token POST
grant_type=public_refresh_token&refresh_token={token created by resource-owner-client}&client_id=public-resource-owner-client
  1. I created another client, that by convention was named based upon another client. Please note the AllowedGrantTypes and RequireClientSecret in "public-resource-owner-client"
new Client
            {
                ClientId = "public-resource-owner-client",
                AllowedGrantTypes = GrantTypes.List("public_refresh_token"),
                RequireClientSecret = false,
                AllowOfflineAccess = true
            }
  1. So work to date. I am able to get the public_refresh_token request and successfully validate the refresh_token against the client that created it, but I am stopping short of finishing this work until I get some sort of validation from you. I am finding that I am copying most your code from TokenRequestValidator to get as far as I did.
My PublicRefreshTokenExtensionGrantValidator  
is staring to look like a mini version of your TokenRequestValidator.

 public class PublicRefreshTokenExtensionGrantValidator : IExtensionGrantValidator
    {
        private readonly ILogger<PublicRefreshTokenExtensionGrantValidator> _logger;
        private readonly IEventService _events;
        private readonly IdentityServerOptions _options;
        private readonly IRefreshTokenStore _refreshTokenStore;
        private readonly IProfileService _profile;
        private readonly IClientStore _clientStore;
        private readonly ITokenResponseGenerator _tokenResponseGenerator;

        private ValidatedTokenRequest _validatedRequest;
        private const string PrependPublic = "public-";
        private const int PrependPublicIndex = 7;
        public PublicRefreshTokenExtensionGrantValidator(
            IdentityServerOptions options,
            IRefreshTokenStore refreshTokenStore,
            ITokenResponseGenerator tokenResponseGenerator,
            IProfileService profile,
            IClientStore clientStore,
            IEventService events,
            ILogger<PublicRefreshTokenExtensionGrantValidator> logger)
        {
            _logger = logger;
            _events = events;
            _options = options;
            _refreshTokenStore = refreshTokenStore;
            _profile = profile;
            _clientStore = clientStore;
            _tokenResponseGenerator = tokenResponseGenerator;
        }
}

And WHY are you doing 3 (and expect 7 to work?)

3 works, but it is not my final use case. However, 3 is a critical piece for me and has to have occurred as is. Only later, with proper configuration, 7 can occur as described.

7 is my ask.

7 currently does not work, BUT given your approval I expect it to work.

The reason I expect 7 to work is that I have used other OAuth 2.0 systems where this was allowed. I had to configure it, but nevertheless allowed.

The benefit for me, if you agree that this is valid, is that IdentityServer4 would take up the work to allow it. I am more than willing to help with this.

So I got it working using extension grants, but I feel a bit dirty about it ;)
https://gist.github.com/ghstahl/6c22ff4d82e686e52b5334c9d2424b3e

My extension does the following;

  1. Validate a refresh_token that was created by another client
  2. refresh it. I don't like this step, but had to do it under the current IdentityServer4 constraints.
  3. Turn that refresh result into a custom response, and call it inner_response.

if you Jwt.io this you can see the clients in play.
public-resource-owner-client response, which wraps the resource-owner-client response { "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhlNDJiNTI0OTZmNTM5Nzk2MjA5YzNjZDAyOWJmM2FhIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODgzODQ0MjgsImV4cCI6MTQ4ODM4ODAyOCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicHVibGljLXJlc291cmNlLW93bmVyLWNsaWVudCIsInNjb3BlIjpbImFyYml0cmFyeSJdfQ.Qe9yjSJN6r1oHRPecBpxA2VdyhJMvZX5gfH6495lQb8-m6kkuE_0AMmFsIpexsvaHLtHTYztZvRgiDXVzb49eqwBUPrmsOrXidzQTiEKfHZXfnPBAxOaWyE2EXq418_Uv1cU3z6OlmQjGg7CEsrcyWx68xlwZ3wDxnwLCri6eo6pF28ClD2vr_IEPyeym5ej8ybgIApcR6uzzRdHAiKXiSzMq2WnSpQED6eJ_Mj0Leg-QJd3_3eo42GexeFgu109aiAB697iCVnAA5jpXo5NsSp1wXKgHQVdNGIfhi39LuAryl1vn5D6zKWGiNmb-u-X9YQlYsR8miCewCNJRtZmYg", "expires_in": 3600, "token_type": "Bearer", "inner_response": { "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhlNDJiNTI0OTZmNTM5Nzk2MjA5YzNjZDAyOWJmM2FhIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODgzODQ0MjMsImV4cCI6MTQ4ODM4ODAyMywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicmVzb3VyY2Utb3duZXItY2xpZW50Iiwic3ViIjoicmF0IiwiYXV0aF90aW1lIjoxNDg4Mzg0NDA0LCJpZHAiOiJsb2NhbCIsIm5hZ3VpZCI6IjEyMzRhYmNkIiwiSW4iOiJGbGFtZXMiLCJzY29wZSI6WyJhcmJpdHJhcnkiLCJvZmZsaW5lX2FjY2VzcyIsIkEiLCJxdWljayIsImJyb3duIiwiZm94Il0sImFtciI6WyJwd2QiXX0.ot1nMhbyTwmOpG6G6ghFkG9PrZCtXO_4nonHb0NEaDLXmI7aBcXEx9WiYcugz_J0-AHkPjCP8n67VI1Z4oIp7iyD7o_37ydFbGGnc2V4NtD_GzwV_rqtk6rtfNKKQpkOU9MTI_KJzoVGjgrYDjyoK8iFm828jzuRBr2xmLdvakL1-fwitGeWxU-NJPrzJA8AQothhLvOjTqQem6-O8LBM7wVAOXQGirfSghfvz61D2hv8qp0k1VmxL1XUz_ESy5Bp0Wg8QxkupS1lsg_6RdAqNzOEKyZpdZT_j3Wy2rg3-c8HLokK_IOm9Ek6xr9plf60FfqjiYmLDXGJIXSxnYkXg", "expires_in": 3600, "token_type": "Bearer", "refresh_token": "b1cca6c78cefcef17259b9c9636c9beae05238f96d1a36f07dacdd983beffe7f" } }

ideally I would have liked the response to be this;

resource-owner-client response
{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhlNDJiNTI0OTZmNTM5Nzk2MjA5YzNjZDAyOWJmM2FhIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODgzODQ0MjMsImV4cCI6MTQ4ODM4ODAyMywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicmVzb3VyY2Utb3duZXItY2xpZW50Iiwic3ViIjoicmF0IiwiYXV0aF90aW1lIjoxNDg4Mzg0NDA0LCJpZHAiOiJsb2NhbCIsIm5hZ3VpZCI6IjEyMzRhYmNkIiwiSW4iOiJGbGFtZXMiLCJzY29wZSI6WyJhcmJpdHJhcnkiLCJvZmZsaW5lX2FjY2VzcyIsIkEiLCJxdWljayIsImJyb3duIiwiZm94Il0sImFtciI6WyJwd2QiXX0.ot1nMhbyTwmOpG6G6ghFkG9PrZCtXO_4nonHb0NEaDLXmI7aBcXEx9WiYcugz_J0-AHkPjCP8n67VI1Z4oIp7iyD7o_37ydFbGGnc2V4NtD_GzwV_rqtk6rtfNKKQpkOU9MTI_KJzoVGjgrYDjyoK8iFm828jzuRBr2xmLdvakL1-fwitGeWxU-NJPrzJA8AQothhLvOjTqQem6-O8LBM7wVAOXQGirfSghfvz61D2hv8qp0k1VmxL1XUz_ESy5Bp0Wg8QxkupS1lsg_6RdAqNzOEKyZpdZT_j3Wy2rg3-c8HLokK_IOm9Ek6xr9plf60FfqjiYmLDXGJIXSxnYkXg",
    "expires_in": 3600,
    "token_type": "Bearer",
    "refresh_token": "b1cca6c78cefcef17259b9c9636c9beae05238f96d1a36f07dacdd983beffe7f"
}

I get that one might be concerned that I am passing back a response from another client, but I think it is just delegation and the security issues are accounted for.

Also, I used all your code to achieve this, so I was hoping you would expose the parts I lifted without change be exposed by IdentityServer4. Mainly to validate a refresh_token.

-H

I am curious - what’s the business use case around this technical
requirement?


Dominick Baier

On 1 March 2017 at 17:32:25, Herb Stahl ([email protected]) wrote:

3 is works, but it is not my final use case. However, 3 is a critical piece
for me and has to have occurred as is. Only later, with proper
configuration, 7 can occur as described.

7 is my ask.

7 currently does not work, BUT given your approval I expect it to work.

The reason I expect 7 to work is that I have used other OAuth 2.0 systems
where this was allowed. I had to configure it, but nevertheless allowed.

The benefit for me, if you agree that this is valid, is that
IdentityServer4 would take up the work to allow it. I am more than willing
to help with this.

So I got it working using extension grants, but I feel a bit dirty about
it ;)

https://gist.github.com/ghstahl/6c22ff4d82e686e52b5334c9d2424b3e

My extension does the following;

  1. Validate a refresh_token that was created by another client
  2. refresh it. I don't like this step, but had to do it under the
    current IdentityServer4 constraints.
  3. Turn that refresh result into a custom response, and call it
    inner_response.

if you Jwt.io this you can see the clients in play.

public-resource-owner-client response, which wraps the
resource-owner-client response
{
"access_token":
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjhlNDJiNTI0OTZmNTM5Nzk2MjA5YzNjZDAyOWJmM2FhIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODgzODQ0MjgsImV4cCI6MTQ4ODM4ODAyOCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicHVibGljLXJlc291cmNlLW93bmVyLWNsaWVudCIsInNjb3BlIjpbImFyYml0cmFyeSJdfQ.Qe9yjSJN6r1oHRPecBpxA2VdyhJMvZX5gfH6495lQb8-m6kkuE_0AMmFsIpexsvaHLtHTYztZvRgiDXVzb49eqwBUPrmsOrXidzQTiEKfHZXfnPBAxOaWyE2EXq418_Uv1cU3z6OlmQjGg7CEsrcyWx68xlwZ3wDxnwLCri6eo6pF28ClD2vr_IEPyeym5ej8ybgIApcR6uzzRdHAiKXiSzMq2WnSpQED6eJ_Mj0Leg-QJd3_3eo42GexeFgu109aiAB697iCVnAA5jpXo5NsSp1wXKgHQVdNGIfhi39LuAryl1vn5D6zKWGiNmb-u-X9YQlYsR8miCewCNJRtZmYg",
"expires_in": 3600,
"token_type": "Bearer",
"inner_response": {
"access_token":
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjhlNDJiNTI0OTZmNTM5Nzk2MjA5YzNjZDAyOWJmM2FhIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODgzODQ0MjMsImV4cCI6MTQ4ODM4ODAyMywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicmVzb3VyY2Utb3duZXItY2xpZW50Iiwic3ViIjoicmF0IiwiYXV0aF90aW1lIjoxNDg4Mzg0NDA0LCJpZHAiOiJsb2NhbCIsIm5hZ3VpZCI6IjEyMzRhYmNkIiwiSW4iOiJGbGFtZXMiLCJzY29wZSI6WyJhcmJpdHJhcnkiLCJvZmZsaW5lX2FjY2VzcyIsIkEiLCJxdWljayIsImJyb3duIiwiZm94Il0sImFtciI6WyJwd2QiXX0.ot1nMhbyTwmOpG6G6ghFkG9PrZCtXO_4nonHb0NEaDLXmI7aBcXEx9WiYcugz_J0-AHkPjCP8n67VI1Z4oIp7iyD7o_37ydFbGGnc2V4NtD_GzwV_rqtk6rtfNKKQpkOU9MTI_KJzoVGjgrYDjyoK8iFm828jzuRBr2xmLdvakL1-fwitGeWxU-NJPrzJA8AQothhLvOjTqQem6-O8LBM7wVAOXQGirfSghfvz61D2hv8qp0k1VmxL1XUz_ESy5Bp0Wg8QxkupS1lsg_6RdAqNzOEKyZpdZT_j3Wy2rg3-c8HLokK_IOm9Ek6xr9plf60FfqjiYmLDXGJIXSxnYkXg",
"expires_in": 3600,
"token_type": "Bearer",
"refresh_token":
"b1cca6c78cefcef17259b9c9636c9beae05238f96d1a36f07dacdd983beffe7f"
}
}

ideally I would have liked the response to be this;

resource-owner-client response
{
"access_token":
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjhlNDJiNTI0OTZmNTM5Nzk2MjA5YzNjZDAyOWJmM2FhIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODgzODQ0MjMsImV4cCI6MTQ4ODM4ODAyMywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicmVzb3VyY2Utb3duZXItY2xpZW50Iiwic3ViIjoicmF0IiwiYXV0aF90aW1lIjoxNDg4Mzg0NDA0LCJpZHAiOiJsb2NhbCIsIm5hZ3VpZCI6IjEyMzRhYmNkIiwiSW4iOiJGbGFtZXMiLCJzY29wZSI6WyJhcmJpdHJhcnkiLCJvZmZsaW5lX2FjY2VzcyIsIkEiLCJxdWljayIsImJyb3duIiwiZm94Il0sImFtciI6WyJwd2QiXX0.ot1nMhbyTwmOpG6G6ghFkG9PrZCtXO_4nonHb0NEaDLXmI7aBcXEx9WiYcugz_J0-AHkPjCP8n67VI1Z4oIp7iyD7o_37ydFbGGnc2V4NtD_GzwV_rqtk6rtfNKKQpkOU9MTI_KJzoVGjgrYDjyoK8iFm828jzuRBr2xmLdvakL1-fwitGeWxU-NJPrzJA8AQothhLvOjTqQem6-O8LBM7wVAOXQGirfSghfvz61D2hv8qp0k1VmxL1XUz_ESy5Bp0Wg8QxkupS1lsg_6RdAqNzOEKyZpdZT_j3Wy2rg3-c8HLokK_IOm9Ek6xr9plf60FfqjiYmLDXGJIXSxnYkXg",
"expires_in": 3600,
"token_type": "Bearer",
"refresh_token":
"b1cca6c78cefcef17259b9c9636c9beae05238f96d1a36f07dacdd983beffe7f"
}

I get that one might be concerned that I am passing back a response from
another client, but I think it is just delegation and the security issues
are accounted for.

Also, I used all your code to achieve this, so I was hoping you would
expose the parts I lifted without change be exposed by IdentityServer4.
Mainly to validate a refresh_token.

-H

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/IdentityServer/IdentityServer4/issues/847#issuecomment-283391931,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABYv-7PxxVyPJuzOGj59Q0eDgdJPRbGKks5rhZ1zgaJpZM4MMbpe
.

Imagine a mobile app, that by a lottery system is given out access to resources.
No user needed.
First 100 in get access, and access is for 1 day.
Following good practices, I wanted my access_token to be short lived (10 minutes), and refresh_token to be long lived (1 day).
All this stuff is configurable.

I wanted my MobileApps to use a standard way to get tokens, a la IdentityServer4. I didn't want to write a poor mans delegation endpoint that now is yet another standard.

We really wanted a client_credential flow with offline_access, where our backed using its clientId/clientSecret would create a token. This token was given to our MobilApp. We also wanted the client to be given a refresh_token, so they could refresh this particular access_token. client_credential flow doesn't allow offline_access, and in this case there is no user in the picture.

So I went down the path of Resource_Owner, which does allow offline_access.
This lead me to building out something where I could create a Resource_Owner offline_access token that simply accepted any user without question. Its basically a glorified client_credentials flow, which now allows offline_access.

Then I needed to let these unsecured clients refresh the tokens, without a client_secret.
That brought in an enhanced_grant flow.

This is what I have so far.

Probably should make this an enhanced grant as well.  The username is carried through, but the password can be anything.
http://localhost:7791/connect/token POST
grant_type=password&scope=arbitrary offline_access&client_id=resource-owner-client&client_secret=secret&handler=arbitrary-claims-service&arbitrary-claims={"naguid":"1234abcd","In":"Flames"}&username=rat&password=poison&arbitrary-scopes=A quick brown fox

produces

{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImI5ODQyMzIyYWFiNmRhMWY4OGI2ZDdkYmVhMmY4MTdmIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODg0MDUzMjUsImV4cCI6MTQ4ODQwODkyNSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicmVzb3VyY2Utb3duZXItY2xpZW50Iiwic3ViIjoicmF0IiwiYXV0aF90aW1lIjoxNDg4NDA1MzI1LCJpZHAiOiJsb2NhbCIsIm5hZ3VpZCI6IjEyMzRhYmNkIiwiSW4iOiJGbGFtZXMiLCJzY29wZSI6WyJhcmJpdHJhcnkiLCJvZmZsaW5lX2FjY2VzcyIsIkEiLCJxdWljayIsImJyb3duIiwiZm94Il0sImFtciI6WyJwd2QiXX0.AV9xo_a0YC2vSoAgV5sqlSUea2De7iYhwCIneBz_4m2Z1dnuf_XMMJrlZyUj2fg8zvUAtoRl9_epb-jSrYvzeQRqX6c-0jq_gs8emhWscU2X9UCr-KwZJG23WFLu_yHzPpfeoYDwUl8E7P1hdRZC4hol6c6cJrFChA9go5hAcy5pGyeNXTM3iyR3TRDfGT3abqeV1mxrgBA6RxA3i0oPS9_LYpuXbxR7bl-mmMI49hwOpjDJRhmR9EUThBUw51hW0xfMUK9-_Qv2nojjPJUSy7FFOuu2FL1-qsHSZjiIB3JT3dTxWx9RK6PeF75dwMKN0neoKHiAWbFf-eZ2sqTPoQ",
    "expires_in": 3600,
    "token_type": "Bearer",
    "refresh_token": "5ed1395bedc0af8d4faa2e11a9b301e6c6387826c7d1ea35d78f06a908455a48"
}

taking the refresh_token from above;

http://localhost:7791/connect/token POST
grant_type=public_refresh_token&refresh_token=5ed1395bedc0af8d4faa2e11a9b301e6c6387826c7d1ea35d78f06a908455a48&client_id=public-resource-owner-client

produces the following;

{
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImI5ODQyMzIyYWFiNmRhMWY4OGI2ZDdkYmVhMmY4MTdmIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODg0MDU0NzMsImV4cCI6MTQ4ODQwOTA3MywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicHVibGljLXJlc291cmNlLW93bmVyLWNsaWVudCIsInNjb3BlIjpbImFyYml0cmFyeSJdfQ.pdiEnPT_V5I1I9E5q5fg9k_FPbiwWbQMzhVrhXJEJO5Kkx88GBekpSHq0DkkWP3DKn8gIAHmIduuHO3Pyuu6q379A4axh639ix4Dkmi6gvL0wFlrrt9GvtdmyeQkLchQFIXxTEtzRXHEuGStpSzkxjEDjbP56pEWWbeBTAHvBb52Zb1WuA31uRL1NV1Xb3YFc6gIrql7t88lX0jnr26A0M34VQhBjBkx2zfo67M_r-Wi2YhXRip2UZKPh1mdlgSQ9KzHi_Ah_YeFvDJbffAHCf-zgxF4f6ZkXPFN9bcKERBR3WbspmZhU3RdquzndMvW9kg7T4-vUfoXzDHvL7r0bA",
    "expires_in": 3600,
    "token_type": "Bearer",
    "inner_response": {
        "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImI5ODQyMzIyYWFiNmRhMWY4OGI2ZDdkYmVhMmY4MTdmIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0ODg0MDU0NzIsImV4cCI6MTQ4ODQwOTA3MiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo3NzkxIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6Nzc5MS9yZXNvdXJjZXMiLCJhcmJpdHJhcnkiXSwiY2xpZW50X2lkIjoicmVzb3VyY2Utb3duZXItY2xpZW50Iiwic3ViIjoicmF0IiwiYXV0aF90aW1lIjoxNDg4NDA1MzI1LCJpZHAiOiJsb2NhbCIsIm5hZ3VpZCI6IjEyMzRhYmNkIiwiSW4iOiJGbGFtZXMiLCJzY29wZSI6WyJhcmJpdHJhcnkiLCJvZmZsaW5lX2FjY2VzcyIsIkEiLCJxdWljayIsImJyb3duIiwiZm94Il0sImFtciI6WyJwd2QiXX0.ZORVofGs00YvWUWU_dUrNGxVOO2pQebEVkUom8zB2ibpW8Hy6a2dgNnQKZet4op1X7-8LiiW3wXuiQKd5pVgKBjTgGbyAJBoVnzHl2Js1xgxOkm5lKimQNKeVHQ9dW747TxHBHuS-9q7L-CMltfYfUKvsNb1bKHdRpEu1lJ4senKK6fiFLLdklabqIQ7QTopFi3koFpILO4gw9WAVDTnLiIyynl4vY6vdlvHMuGBfwkipjPeu7MTGKdAL5Wu-IJeeEXN1a1NZSO1gXPFed_L3pyRMrZn-ygloL1M-CN_i09ThJVBTpSOsam2sLlO9snd4cOSZ1d7F0FmijA4IbTbyg",
        "expires_in": 3600,
        "token_type": "Bearer",
        "refresh_token": "50dcb1ef15113115a9b1d1ebf3873fabbab4d1a67790e547f765a6559c0f36e6"
    }
}

This seems all way too complicated for me.

Have you considered giving out reference tokens? This way you could make them longer lived - but still are able to revoke them at any point in time.

Good point, and I have that at my disposal. The design pattern that has been most reliable for me has been to put in rules on the edge, as opposed to writing code that say sweeps a backend database going on a revocation spree.

My choices are based upon what constraints any given organization imposes on me, and admin access to databases to revoke references in a sweep may actually be banned. So I find other ways more tolerably accepted, and have turned out to be more reliable.

Lets say I have 50Million+ endpoints that I handed out refresh_tokens. In a lot of our cases an access_token is for 1 day, with a refresh_token for 30 days. As these endpoints come trickling in trying to refresh, I can apply my rules right there and on-demand, I can also change my mind and only a small portion of the population is affected.

To your(IdentityServer4) credit, it resulted in very little code of which most of it I copied from you :).

Now that it is in place, its usage is quite simple, and really flexible.

Did you give any thought on exposing the ability to validate a refresh_token? Perhaps a tiny little new method in ITokenValidator?

Glad you have it working! Changing the interface is a breaking change and can be considered for the next major release.

Please open a new issue that details the changes you have in mind.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

osmankibar picture osmankibar  Â·  3Comments

brockallen picture brockallen  Â·  3Comments

user1336 picture user1336  Â·  3Comments

garymacpherson picture garymacpherson  Â·  3Comments

eshorgan picture eshorgan  Â·  3Comments