Token introspection of client creds does not return meta information

Reposting here at the suggestion of @hackerman from the slack channel;

The Problem:
When making a introspection request for a token that was generated via a registered auth code client, extra information that was assigned when the session is created is returned in the response allowing oathkeeper to use it within the id_token mutator.

However, when a client is created with the client_credentials grant type, when a token that was generated by the token endpoint is inspected, it does not return the extra meta information (Or indeed owner values) that where submitted when the client was created.

It is slightly unclear from the the documentation as to if this should be the case, and if this is a deliberate design decision.

The long version*

We want to allow users to create their own oauth clients using client credentials grant but constrain such client so as to operate only within the context of the user and their associated realm.

When a user signs in via an auth_code flow, they are issued an access token that can be inspected via oathkeeper and thus generate an id_token that can be passed to backend applications.

{
  "active": true,
  "scope": "openid profile myapp offline_access",
  "client_id": "test_ui_dev",
  "sub": "1234567890",
  "exp": 1594892078,
 "iat": 1594888477,
 "iss": "https://hydra.foo.dev/",
 "token_type": "access_token",
 "ext": {
      "realm": {
      "id": 1234567890
   },
 "user": {
    "id": 9876543210
  }
 }
}

Backend applications can then rely on the id_token custom claims generated via oathkeeper for the required information (user.id and realm.id in this case)

However this breaks down when a request is made via a client with the client_creds grant type. The client was created using the standard method (http client create body to hydra admin endpoint). Amongst its provisions, is the ability to provide extra information (metadata and owner).

If this client requests a token from the hydra/oath2/token endpoint a valid access token is issued. However upon introspection, the response does not include any of the metadata values associated with the client.

{
  "active": true,
  "client_id": "test_client_creds_client",
  "sub": "1234567890",
  "exp": 1594891895,
  "iat": 1594888295,
  "iss": "https://hydra.foo.dev/",
  "token_type": "access_token"
} 

My expectation here is that the introspection endpoint should have included the metadata information associated with the client.

My questions are;

  1. Is this a design decision or oversight?
  2. Is what I am trying to do a good idea (I do want 3rd party clients to register but I want them scoped to just their realm so they get to work only on their data) and consistent with the values of hydra/oathkeeper

Workaround
On oathkeeper using a hydrator to detect the subject name and determine if it is a client or user can be performed to obtain the correct information and pass down onto the next mutator (id_token). While this is fine, it is another small application to maintain and introduces another http request in an increasing chain of them.

Observations
The “issue” around this appears to stem from the fact that an auth code flow requires a session and during that session the developer is free to add data to the session (access_token, id_token) when accepting the request.

However in this particular use case, a 3rd party registered a client with client creds flow only. As part of that process meta data is attached to the client within hydra. As there is no session active when a request is made for a token to the token endpoint, there is no session information. In pseudo code this in my expectation would have been

 if (grant_type === "client_credentials") {
    meta = fetchClientMetaData(client_id)
}

Appreciate that hydra is golang (I’m just not that comfortable with its sources yet to be able to write a sample in go)

It would make sense to me that a token issued via a client creds grant in the absence of session information should return the static meta_data attached to that client when it was created as part of the introspection request. This would negate the use for a hydrator handler.

Similar, if using the oauth2_client_credentials authenticator for oathkeeper (which I would prefer not to use in the first instance) would still induce this issue.

I would value your thoughts on this use case and if I should progress this to a request for change.

TIA

Thank you for raising this issue, I believe this is in part already discussed in one of the GitHub issues if I’m not mistaken: https://github.com/ory/hydra/issues/1748

@hackerman Yes it is, you are right. My thoughts on it would be simply if the meta_data is provided by a client definition, it would appear the intention of the end developer would be to use that again via another exposed endpoint, in this instance introspection one. Especially when you take into account the ecosystem (hydra and oathkeeper together) it would be beneficial IMHO.

I can get around this using a hydrator, but its rather ugly and introduces this additional lookup that could be mitigated. Under some circumstance I can see a use case for this (Perhaps behind a config setting with a default off would make sense too)

The problem with metadata is that it can be set by the client. Using that as information in your application is like allowing someone to change the cookie session data, which would allow me - for example - to say that I’m an administrator. That’s why this isn’t included and why I would be very wary of implementing this.

Yes, that is a fair point. However unlike https://github.com/ory/hydra/issues/1383 this is not customising the claim of the access token, but rather providing that meta data on introspection (Indeed it does not have to be client provided data, but maybe some operator customised data, but when I thinking about it, the data is in the db and stored for a reason but only a small fragment of it is available on introspection, it appears logical to me to provide it and let the end user decide if they want it or not).

Should an end developer do this… maybe not, but the cats out of the bag :slight_smile: What people do with the application as it grows is up to them.

Without conditionality within oathkeeper, the mutator will hit the hydrator on each request before passing to the next mutator.

For me it is a matter of where does it seem there is a logical fit for this solution;

  1. Don’t provide custom data on token introspection for client_credentials
  2. Do provide custom data on token introspection for client_credentials but must be enabled via config flag (Downstream apps could determine if they want that info then and the level of trust they apply to it)
  3. Leave as it is but switch over to conditionals within oathkeeper (Which seems like a bunch of work), i.e. if token was generated via client credentials, do x otherwise continue pipeline. This would still involve telling oathkeeper on the introspection side to hydra that a client_creds client generated this

If it were up to me (and it’s not) I’d provide the meta data (Or full subset of client data) on introspection (We’re following best practices as much as possible but they are only practices). Is the token a session token… well no, should introspection provide more data… probably - you can see I am biased here.

It is fair to say at the moment it is a design decision and one I understand. But I would fall into the camp that client_credentials introspection should supply ext data in its response if available.

What we could probably do is include a client subfield in the introspection response which includes all fields of the client (except password hash obviously) and clarify that the data should not be trusted if set by the user in the docs.

Yes that appears to be a pragmatic and balanced solution. I’m for it :slight_smile:

Nice, would you also be open to contribute that change? :slight_smile:

Well I’ve only just started learning GoLang and its a lot to take in. But I can shadow and test if that helps, if not I will add it to my todo list and see what I can come up with as a pr towards the end of next week prob

Okay so I have had some spare time to have a look at this issue and would value your advice @hackerman on this.

I was looking at taking your suggestion and writing the changes necessary for this to happen, i.e. provide a client property in the introspection response.

Well this got bigger than me quickly :slight_smile:

Within oath2/hander.go I’ve been looking at IntrospectHandler function.

From what I can see, the resp.GetAccessRequester().GetClient()returns a fosite Client interface (client.go). This interface does not have a getMetaData() sig.

This has me somewhat reluctant as it may be an issue as its a change to a core lib, fosite as well as to hydra.

My question here is, a) Is my observation correct and b) what way should I proceed. I would be happy to write the code and submit a PR (Least I could do) but as this is change to fosite I guess I am unsure which road to take.

Thanks
AJ

I would also have to look into the code a bit, and it’s possible that we need to either define a new interface for metadata providers or change this another way. The best thing is probably throwing up a draft PR so I can look at the code :slight_smile: