Reload JWKs when keys are rotated

819 views
Skip to first unread message

Julian Trischler

unread,
Mar 8, 2018, 2:23:54 AM3/8/18
to ve...@googlegroups.com
Hi there,

I wanted to use the vertx-auth-oauth2 module to authenticate users
against an OpenID provider. On application start I load the JWKs from
the provider using OAuth2AuthProviderImpl.loadJWK(). But now suppose
some time later the provider rotate its signing keys and a client sends
me a legitimate access token which was signed with such a new key I
don't have in the local key store yet, so validation fails.

Looking at the current code I don't see any possibility on how to detect
this exact situation (unknown key ID) to then reload the JWKs from the
provider. JWT.decode(...) just raises a very generic
RuntimeException("Signature verification failed"). But this could also
be thrown in case somebody messed with the token.

Also OAuth2TokenImpl.decodeToken(...), which calls the above method,
simply catches the exception, logs it and returns null, so my
AccessToken.accessToken() will be null.

Do you folks have any idea on how one is supposed to handle that? I
really don't want to reload the JWKs on every failed attempt to decode
the token. This definitely seems a bit much to me.

Any help would be greatly appreciated.

Cheers
Julian

Paulo Lopes

unread,
Mar 8, 2018, 7:22:37 AM3/8/18
to vert.x
Hi Julien,

There is no easy solution for this. Mostly because there is (or there wasn't) any spec regarding key rotation when this was implemented. What you can do is define a periodic task say, every hour or every day (depending on the granularity your application needs) that reloads the keys. This still does not solve the issue entirely as key invalidation can happen at any moment and if the old keys are not preserved for some leeway time you will be failing to authenticate in a the time between the key rotated and the key got reloaded.

I'm not sure you can/should rely on exceptions for this, since a malformed token (either due to new key, or bad intended behavior by an attacker) would now trigger the reload of keys. If these keys are loaded over the network and attacker could use this as an opportunity for DDoS the authorization server.

Julian Trischler

unread,
Mar 12, 2018, 2:24:54 AM3/12/18
to ve...@googlegroups.com
Hi Paulo,

thanks for your reply!

> There is no easy solution for this. Mostly because there is (or there
> wasn't) any spec regarding key rotation when this was implemented.

Hmm, I see.

> What you can do is define a periodic task say, every hour or every day
> (depending on the granularity your application needs) that reloads the
> keys. This still does not solve the issue entirely as key invalidation
> can happen at any moment and if the old keys are not preserved for some
> leeway time you will be failing to authenticate in a the time between
> the key rotated and the key got reloaded.

At the moment the provider gives me the last three recently used keys,
but I don't know the provider's key rotation schedule, so this approach
is not possible in my case. Ignoring the unnecessary network traffic,
even checking every ten seconds would be unacceptable for users in my
opinion.

I probably could figure out the provider's regular schedule, but this
still does not account for out-of-schedule key renewals for whatever
reason (in all likelihood they won't occurr at all or at least
incredibly rarely, but the probability is not zero).

> I'm not sure you can/should rely on exceptions for this, since a
> malformed token (either due to new key, or bad intended behavior by an
> attacker) would now trigger the reload of keys. If these keys are loaded
> over the network and attacker could use this as an opportunity for DDoS
> the authorization server.

Yes, true. I could ensure that a key reload will not happen unless the
last attempt was at least e.g. ten seconds ago. What's the recommended
way of handling this?

So I'm probably best off implementing this myself or quite heavily
refactoring the library.

Cheers
Julian

Paulo Lopes

unread,
Mar 13, 2018, 7:28:52 AM3/13/18
to vert.x

You could have some code that triggers the reload on error, inside that method, you check the last time you updated the keys, if the time difference is less than 10s then you abort.

This does not seem to hard to implement as all you need is to store the last run timestamp and download a json file.

But the devil is on the details right ;)

Julian Trischler

unread,
Mar 14, 2018, 2:53:21 AM3/14/18
to ve...@googlegroups.com
Hi Paulo,

> > > [...] If these keys are loaded over the network and attacker could
> > > use this as an opportunity for DDoS the authorization server.
>
> > Yes, true. I could ensure that a key reload will not happen unless
> > the last attempt was at least e.g. ten seconds ago. What's the
> > recommended way of handling this?
>
> You could have some code that triggers the reload on error, inside
> that method, you check the last time you updated the keys, if the time
> difference is less than 10s then you abort.

yeah, sure. My question was targeted on the general concept of (D)DoS
avoidance in this circumstance. Maybe there's an even simpler or better
way to act on such "new key" events, I didn't think of. :-) But I reckon
the time check is good enough – at least for now.

I just realized that section 10.2.1 "Rotation of Asymmetric Encryption
Keys" of the OpenID Connect 1.0 specification recommends the usage of
the Cache-Control HTTP header, so clients can proactively update their
local key store:
http://openid.net/specs/openid-connect-core-1_0.html#RotateEncKeys

I have to check if my provider implements this.

> But the devil is on the details right ;)

Definitely! ;-)

Thanks and cheers
Julian

Paulo Lopes

unread,
May 10, 2020, 8:40:38 AM5/10/20
to vert.x
Hi Julien,

It's been a while but now that we're updating the API for 4.0.0 so we can now add this feature. Please have a look and comment on:


The behavior is as described on the oidc core final spec:

1. if there's a `Cache-Control` header it shall be used to set a periodic task to reload the keys
2. if a key is missing it shall notify you about the missing key id

In the second case, you as the user have the power to decide, how to proceed. does the key id make sense? is it a DDoS? If you believe it's ok, you can call the refresh of keys but be careful by adding some fences/backoff period to avoid calling the IdP to often?

I know it has been a very long time, but if you have the time all feedback is welcome.
Reply all
Reply to author
Forward
0 new messages