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

kid value in headers in different format after upgrading from 1.8.3 to 1.8.5 #193

Closed
jessieay opened this issue Apr 5, 2023 · 2 comments · Fixed by #194
Closed

kid value in headers in different format after upgrading from 1.8.3 to 1.8.5 #193

jessieay opened this issue Apr 5, 2023 · 2 comments · Fixed by #194

Comments

@jessieay
Copy link

jessieay commented Apr 5, 2023

This may not be an issue for everyone but I wanted to post an issue warning to others who are upgrading or might be facing an issue with the upgrade as this upgrade (1.8.3 -> 1.8.5) caused an authentication incident for my team today.

Source of the problem

The root of the issue is this pull request: #177

This was called out as a potential issue in the PR itself by @nbulaj .

Specifically, this line of code.

The OpenID kid is an opaque value that is supposed to identify public keys. json-jwt and ruby-jwt implement the kid slightly differently:

kid format for json-jwt (used by 1.8.3)

kid format for ruby-jwt (used by 1.8.5)

Normally doorkeeper-openid_connect manages both the generation of an OpenID token and the endpoint that allows clients to discover the public keys.

In our application, however, we override Doorkeeper::OpenidConnect::DiscoveryController and for the JWK output in our controller we use call to_jwk, which uses json-jwt NOT ruby-jwt

Because our override of Doorkeeper::OpenidConnect::DiscoveryController assumes that the kid format will be the format used by json-jwt, an upgrade to doorkeeper-openid_connect1.8.5 resulted in an incident for us because any service that validates the signature of a JWT using the kid header value against the kid in our keys discovery endpoint will raise an error (because the kid s do not match).

What I actually see

Screenshot of a (test) decoded JWT generated with 1.8.5:
1 8 5

What I expect to see

The kid should match the kid value of my JWK#kid in my discovery keys endpoint ("OcGcrSBrUtT8mNNNkm1eofXGmOgM4SQtqFgFT8BCszU")
Screenshot of a (test) decoded JWT generated with 1.8.3:
1 8 3

@jessieay jessieay changed the title kid value in headers invalid after upgrading from 1.8.3 to 1.8.5 kid value in headers in different format ater upgrading from 1.8.3 to 1.8.5 Apr 6, 2023
@jessieay jessieay changed the title kid value in headers in different format ater upgrading from 1.8.3 to 1.8.5 kid value in headers in different format after upgrading from 1.8.3 to 1.8.5 Apr 6, 2023
@stanhu
Copy link
Contributor

stanhu commented Apr 13, 2023

The issue here is that with the switch to #177, the jwt gem defaults to a key generator of JWT::JWK::KidAsKeyDigest instead of JWT::JWK::Thumbprint, which implements RFC 7638: https://www.rfc-editor.org/rfc/rfc7638.

It's not obvious that applications need to set the generator to make the kid values backwards compatible with json-jwt:

JWT.configuration.jwk.kid_generator_type = :rfc7638_thumbprint

This broke our application because we overrode Doorkeeper::OpenidConnect::DiscoveryController to export additional keys with RFC 7638 format via json-jwt, but Doorkeeper::OpenidConnect::IdToken uses a kid in the JWT::JWK::KidAsKeyDigest format. Clients would fail because the kid listed in the JWKS URL did not match the one provided in the token.

There is a json-jwt discussion to make RFC 7638 the default in a future version.

I think v1.8.x of this gem should default to the RFC7638 behavior:

diff --git a/lib/doorkeeper/openid_connect.rb b/lib/doorkeeper/openid_connect.rb
index b2bca97..d20642e 100644
--- a/lib/doorkeeper/openid_connect.rb
+++ b/lib/doorkeeper/openid_connect.rb
@@ -48,7 +48,7 @@ module Doorkeeper
         else
           OpenSSL::PKey.read(configuration.signing_key)
         end
-      ::JWT::JWK.new(key)
+      ::JWT::JWK.new(key, { kid_generator: JWT::JWK::Thumbprint })
     end
 
     def self.signing_key_normalized

I'm not sure if this needs to be customized. What do you think @nbulaj @kristof-mattei?

stanhu added a commit to stanhu/doorkeeper-openid_connect that referenced this issue Apr 13, 2023
The switch from the `json-jwt` to `jwt` gem in doorkeeper-gem#177 changed the
default `kid` generation from RFC 7638
(https://www.rfc-editor.org/rfc/rfc7638) to a format based on the
SHA256 digest of the key elements.

However, clients may fail if the the `kid` generated by `IdToken` does
not match a key listed in JWKS discovery endpoint, which may be
implemented by the application using RFC 7638-based `kid` values. To
restore the previous behavior, applications have to set a global
setting:

```
JWT.configuration.jwk.kid_generator_type = :rfc7638_thumbprint
```

However, relying on this global setting is not ideal since other keys
may depend on the legacy `kid` values.

In keeping with semantic versioning, restore the `kid` generation to
RFC 7638. Whether this should be customizable can be discussed later.

Closes doorkeeper-gem#193
@stanhu
Copy link
Contributor

stanhu commented Apr 13, 2023

#194 does this.

stanhu added a commit to stanhu/doorkeeper-openid_connect that referenced this issue May 5, 2023
The switch from the `json-jwt` to `jwt` gem in doorkeeper-gem#177 changed the
default `kid` generation from RFC 7638
(https://www.rfc-editor.org/rfc/rfc7638) to a format based on the
SHA256 digest of the key elements.

However, clients may fail if the the `kid` generated by `IdToken` does
not match a key listed in JWKS discovery endpoint, which may be
implemented by the application using RFC 7638-based `kid` values. To
restore the previous behavior, applications have to set a global
setting:

```
JWT.configuration.jwk.kid_generator_type = :rfc7638_thumbprint
```

However, relying on this global setting is not ideal since other keys
may depend on the legacy `kid` values.

In keeping with semantic versioning, restore the `kid` generation to
RFC 7638. Whether this should be customizable can be discussed later.

Closes doorkeeper-gem#193
stanhu added a commit to stanhu/doorkeeper-openid_connect that referenced this issue May 10, 2023
The switch from the `json-jwt` to `jwt` gem in doorkeeper-gem#177 changed the
default `kid` generation from RFC 7638
(https://www.rfc-editor.org/rfc/rfc7638) to a format based on the
SHA256 digest of the key elements.

However, clients may fail if the the `kid` generated by `IdToken` does
not match a key listed in JWKS discovery endpoint, which may be
implemented by the application using RFC 7638-based `kid` values. To
restore the previous behavior, applications have to set a global
setting:

```
JWT.configuration.jwk.kid_generator_type = :rfc7638_thumbprint
```

However, relying on this global setting is not ideal since other keys
may depend on the legacy `kid` values.

In keeping with semantic versioning, restore the `kid` generation to
RFC 7638. Whether this should be customizable can be discussed later.

Closes doorkeeper-gem#193
stanhu added a commit to stanhu/doorkeeper-openid_connect that referenced this issue May 10, 2023
The switch from the `json-jwt` to `jwt` gem in doorkeeper-gem#177 changed the
default `kid` generation from RFC 7638
(https://www.rfc-editor.org/rfc/rfc7638) to a format based on the
SHA256 digest of the key elements.

However, clients may fail if the the `kid` generated by `IdToken` does
not match a key listed in JWKS discovery endpoint, which may be
implemented by the application using RFC 7638-based `kid` values. To
restore the previous behavior, applications have to set a global
setting:

```
JWT.configuration.jwk.kid_generator_type = :rfc7638_thumbprint
```

However, relying on this global setting is not ideal since other keys
may depend on the legacy `kid` values.

In keeping with semantic versioning, restore the `kid` generation to
RFC 7638. Whether this should be customizable can be discussed later.

Closes doorkeeper-gem#193
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

Successfully merging a pull request may close this issue.

2 participants