Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rotation of bound client keys #105

Closed
jricher opened this issue Nov 13, 2020 · 17 comments
Closed

Rotation of bound client keys #105

jricher opened this issue Nov 13, 2020 · 17 comments

Comments

@jricher
Copy link
Collaborator

jricher commented Nov 13, 2020

§8 Binding Keys: Editor's note:

We are going to need a way for a client to rotate its keys securely, even while an ongoing grant is in effect.

@adeinega
Copy link
Contributor

adeinega commented Mar 24, 2021

The easiest way for a client to rotate its keys is to handle a URL in the jku claim to the client's JWKs endpoint on the AS side (the same also applies to the x5u claim for certificates). "OpenID Connect Dynamic Client Registration" and "OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591)" use the jwks_uri parameter for that. Personally, I don't think this approach will work that well for all possible scenarios because we can't assume that all clients have a way to publish and make sure their JWKs endpoints are always reachable for the AS. Furthermore, a client may not have or simply lost control of the domain and an attacker may make use of it. I guess it's totally fine to use this approach in one security domain where you have full control over all components and trust them (clients, the AS, DNS servers) but IMO, it isn't going to be a secure enough approach if you don't have that. That also won't work for the cert and cert#S256 key formats defined in the current specification. I was never been a big fan of this approach (or I'm missing something important) but it may work even OTB if the specification doesn't forbid us to do that and a JWS library of your choice supports this.

Another option would be to introduce the client key management URI (similar to Token Management URI) which accepts an object with similar properties to what "DPoP proof JWT" has and where the goal of the AS to make sure that that the client does have key material.

Upsides

  1. may work for all currently defined, as well as for future key formats pretty much in the same fashion
  2. no need to advertise the client key management URI using the Discovery mechanism which is great and inlined with "the protocol minimizes the need for any pre-flight discovery"
  3. the client does not need to have any special knowledge on how to obtain and use tokens to rotate its own keys
  4. each client may have its own independent client key management URI

These are my thoughts.

@fimbault
Copy link
Collaborator

fimbault commented Mar 26, 2021

Another item that is a bit specific is 7.1.1 (key reference). Since "dereferencing this value are out of scope for this specification", so is rotation. I guess we assume there's some out of band KMS for that.

@adeinega
Copy link
Contributor

I may also add yet another item for section "10.1. Introspecting a Token"

The AS responds with a data structure describing the token's current state and any information the RS would need to validate the token's presentation, such as its intended proofing mechanism and key material

@adeinega adeinega mentioned this issue Apr 2, 2021
@jricher
Copy link
Collaborator Author

jricher commented Apr 20, 2021

I'm more in favor of a defined API that the client can use to update its keys as well as anything else it needs to, a la RFC7592. Features that rely on the AS to fetch the latest values are susceptible to a number of problems:

  • Not all clients can host URLs (mobile clients specifically, and these are most likely to want to rotate keys dynamically)
  • The AS fetching items opens it up to SSRF attacks, as has been documented in OAuth/OIDC against parameters like jwks_uri
  • It's up to the AS to get the cache right on keys during a rotation event, and detect when to invalidate the data it holds; if a client pushes key information when it changes then this isn't an issue
  • Flexibility on key formats is extremely helpful, we can't assume everyone's going to use the same kinds of keys in every environment
  • Clients are probably going to want to update other stuff about themselves, including display names and webpages and the like over time
  • Clients should be able to rotate keys to an ongoing grant in a secure fashion (prove possession of old and new keys together) without changing their overall "Registration": in other words, a client instance could use many different keys and handle them on different lifetimes, not just one "registered" key

The last point is really important: we need to be careful we don't make the same limiting assumptions that OAuth2 does in terms of client registration. This has been one of the driving limiting factors that GNAP is trying to address at a fundamental level, and we have to be careful it doesn't creep back in.

@jmandel
Copy link
Contributor

jmandel commented Sep 28, 2021

I think it'll be important to introduce something soon -- otherwise components are likely to become brittle by treating a client instance's keys (or key thumbprints, or other derived artifacts) as persistent client instance identifiers. For example, imagine a resource server that expects access tokens conveying RARs like:

{
  type: "file-management",
  actions: ["create-folder", "add-files-to-created-folders"],
  locations: ["https://rs.example.org/data"]
}

Well, the RS can see this RAR and allow a client to create some folders -- but how does it track which folders were created by a given client instance, so that it can allow the correct client instances to add files to (only) their own folders? The natural/only thing today is to use the introspection API's key as a (pseudo)-identifier for the client, correlating these across requests. But that'll become unreliable when client instances can rotate their own keys, and we'll need a way to reflect in the data model something like a "client instance identifier" (independent of keys) or a "client instance's original key" property or what-have-you.

(Now you could argue that this style of RAR modeling is broken, but I think it's actually quite common and expressive. Compare for example with Google Drive OAuth permissions for add-ons like "Can read documents it's installed in" -- you don't say which documents at the time of approval, and the system needs to track installs relative to this client and authorization).

@agropper
Copy link

agropper commented Sep 28, 2021 via email

@jmandel
Copy link
Contributor

jmandel commented Sep 28, 2021

I think you've misunderstood my point @agropper -- I'm not suggesting requests would be sent to the resource server. Rather, as GNAP specifies: an access token (issued by the authorization server) is sent by the client to the resource server, and the RS introspects this token to learn what Resource Access Rights the token conveys. I'm pointing out here that the RS
often needs some stable identifier for the client instance to do its job -- and in a world where client instances can rotate their own keys, the client instance key cannot be that stable identifier.

@agropper
Copy link

@jmandel Sorry for the confusion.

I've been tutored by capabilities gurus to be suspicious of any identity-related access controls and I consider client identity to be part of that design. That's not to say that GNAP must normatively forbid client credentials, of course.

When clients need pre-registration and rotation is important, couldn't client instances be identified by DIDs just like anything else?

@jmandel
Copy link
Contributor

jmandel commented Sep 28, 2021

When clients need pre-registration and rotation is important, couldn't client instances be identified by DIDs just like anything else?

Client instances could be identified in all kinds of ways; my point is that currently (at least, if I'm reading the spec correctly -- and it's entirely possible I'm just missing something) GNAP doesn't model this information in an explicit RS-facing data element.

@agropper
Copy link

agropper commented Sep 28, 2021 via email

@jmandel
Copy link
Contributor

jmandel commented Sep 28, 2021

The point I've trying to make from my initial comment (and it seems I'm failing to communicate clearly -- sorry!) is that GNAP can and should provide a way to do (b). It's as "simple" as deciding this is a problem worth addressing, and standardizing a solution (or else deciding it's out of scope and leaving it to extension space.

(I should note that even under (b), the RS absolutely needs to persist client instance identifiers some kind of grant-specific identifier in order to associate with certain resources, at least for certain styles of RAR modeling -- see my initial comment. If you don't like this, you can always avoid those styles of RAR modeling, but they do exist, and they're expressive and useful.)

@jricher
Copy link
Collaborator Author

jricher commented Sep 28, 2021

@jmandel You are correct that there is not a persistent client instance identifier that's exposed throughout the system, and that's been a deliberate design decision. While there are many kinds of software for which persistent identifiers make sense, we've seen with SPAs and mobile apps in OAuth that client_id gets in the way in a lot of places and doesn't end up telling you what you might think it does. Also note that the key bound to an access token might vary over time more than the client's "registered" key. We don't want to assume this will all be the same all the time, OAuth has already shown us that not to be the case.

To the scenario you presented, it sounds like a requirement of the API you're protecting that there be some kind of authorization identifier that persists over time. After all, isn't it more than just the client software -- it's also who approved the software and for what purpose. This gets close to a grant identifier (like in #146) but exposing that to an RS sounds wrong. Still, I question whether the RS actually needs to know that information all the time. And if it does, then it's up to the AS to provide an RS-facing identifier for the software, user, grant, or whatever else, either in the introspection response or the token itself. The AS would be able to map this identifier across any kind of key rotation, since it would be able to trace the history of key usage and rotation for a client instance over time. Importantly, the result of this might be an identifier that the client instance itself never sees, since it's part of the internal agreement between the RS and AS. To have something like that we'd need to expand the model of what a token represents here with an AS-to-RS-identifier for the client instance.

@jmandel
Copy link
Contributor

jmandel commented Sep 28, 2021

Agreed with your assessment @jricher that a grant identifier solves the problem just fine, and may be invisible to the client itself. My point here on this issue that that if we don't surface something like this in the standard protocol, RSs will (incorrectly) try to use client instance keys for this purpose, and that'll break even worse once those keys can be rotated. So I'm just calling out that #105 is linked to #146 in perhaps a non-obvious way.

@jricher
Copy link
Collaborator Author

jricher commented Sep 28, 2021

@jmandel that subtle connection's a fair point, especially when the client instance is using some-other-key for the access token. GNAP is built to enable (and perhaps even encourage) diversity of keys, especially for client software that can securely generate a key locally, so we'd expect much client software to use different keys over time. Rotating would tie those together at the AS.

What I'm wondering is if we should actually put some warnings in the RS document against the RS using the client's key as a a stable identifier at all, and instead rely on things returned from the AS for all of these reasons.

@jmandel
Copy link
Contributor

jmandel commented Sep 28, 2021

Yeah, I'd love to see 1) an explicit warning against using a client's key for this purpose and 2) an explicit field introduced in the introspection response (and eventually, incorporated into self-contained token formats) to allow the AS to communicate a stable grant identifier to the RS.

@jricher
Copy link
Collaborator Author

jricher commented Dec 15, 2021

We've got two types of bound keys:

  • tied to a client's identity (and therefore a registered client's record at the AS, either dynamic or static)
  • tied to an access token (including the continuation access token, since it's not a separate item anymore)

The keys for an access token could be a simple pass-through pointer to the client instance's key, and this will probably be the default style of implementation.

We can to solve these problems separately, but probably using the same key-presentation mechanisms for rotating the key material itself. We could have a client-management API as an extension that allows rotation of the client instance's key itself, and a key-rotation mechanism available as part of token rotation. The token rotation will be part of core.

@jricher
Copy link
Collaborator Author

jricher commented Oct 6, 2022

This should be in the same extension as discussed in #103

@jricher jricher closed this as completed Oct 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants