JWT Revocation
JWT Revocation
Summary
Right now there's no way to revoke JWTs. In cases like oauth2, where the sessions are stored in cookies as JWTs, this means that these stateless sessions are not able to be terminated. This is important for cases like OIDC back channel logout.
The proposal is essentially
jwt_authnfilter will use ECDS to receive a stream of revocation objects- Each revocation object will have information that will allow Envoy to determine whether a given JWT is revoked (see below).
- Revocations will persist indefinitely until removed from the control plane
Revocation list
I was debating whether this should be sent via SDS or ECDS. SDS data is opaque, which is not sufficient for this use-case. Since we want a more complex data type, the only option is ECDS.
A revocation entry will look something like
message JwtClaimMatch {
string claim_name = 1;
type.matcher.v3.StringMatcher values = 2;
}
message JwtClaimMatchList {
// All of the listed claims must match (logical AND).
repeated JwtClaimMatch claims = 1;
}
message JwtRevocation {
// Exactly one way of describing the revocation criteria.
oneof criteria {
JwtClaimMatchList claim_matches = 1; // Simple list form
xds.type.matcher.v3.Matcher matcher = 2; // Fully-featured matcher... might be overkill?
}
}
Revocation expiration
Envoy does not know how long the sessions are valid. Therefore, it is the responsibility of the control plane to send an update (either via SOTW or DeltaXDS) when a revocation is no longer required.
We could later extend this to send a TTL along with the JwtRevocation, but it will not be included in the initial version.
oauth2 filter integration
For OIDC Back-channel logout, revocations may be common. However, we likely do not want to outright deny the request. For this reason, the jwt_authn filter will add dynamic metadata indicating the rejection. The oauth2 filter will read this in the encodeHeaders function and trigger a redirect. We can also extend this to other types of JWT validation failures if need be.
[optional Relevant Links:] This is related to back-channel logout, but would take place on the control plane https://github.com/envoyproxy/envoy/issues/35225
CC: @zhaohuabing since this is related to OIDC back-channel logout
@bplotnick thanks for bringing up this idea. Having a revocation list managed by the control plane for OIDC logout seems a bit complex, since it introduces another component into the logout process. How would we retrieve or mantian this revocation list in the control plane?
@bplotnick thanks for bringing up this idea. Having a revocation list managed by the control plane for OIDC logout seems a bit complex, since it introduces another component into the logout process. How would we retrieve or mantian this revocation list in the control plane?
The idea here is that the control plane will expose an endpoint that the user would register as the backchannel_logout_uri. It will receive and validate logout tokens. The logout token is required to contain an aud claim, which must match the client_id. Since the control plane likely knows the client ids, it is able to determine which ECDS streams to send the revocations to.
The storage of these revocations in the control plane is left up to the implementation. As far as i know, there is spec for an endpoint at the OP to proactively retrieve logouts. If this doesn't exist, it might be something that could be proposed, though the details of how that endpoint would be authenticated might be tricky.
From my perspective, this is far simpler than having the data plane itself distribute the revocations amongst a cluster. Consider the fact that if i have a cluster of gateways with the same client id, they all are considered a single RP, so where is the OP going to send the logout tokens?
cc @adisuissa : RE thoughts on using ECDS versus other xDS.
Not sure I fully understand, but ECDS is currently at the entire filter level, and I'm assuming this is not the intention here. If what is needed can be achieved by replacing the JWT filter, then ECDS can be used.
Otherwise, I guess that if xDS is to be used as described, then there will be need to support collection updates of generic objects.
Not sure I fully understand, but ECDS is currently at the entire filter level, and I'm assuming this is not the intention here. If what is needed can be achieved by replacing the JWT filter, then ECDS can be used.
hmm. that probably is overkill. so is the alternative to create a new service type? or could we extend SDS to support typed secrets
Otherwise, I guess that if xDS is to be used as described, then there will be need to support collection updates of generic objects.
What would be your suggestion here? naively, we could resend the entire revocation list, but i would be concerned about how this would scale. maybe we could support wildcard subscriptions and send revocations as individual resources?
There was a design back at the day to enable such xDS use-cases, but this work has been deprioritized. Note that this will require the xDS server to support delta-xDS (to update individual entries), so I'm not sure if it's applicable to your use-case.
Can you remind me what is the model for adding and removing JWT tokens? Specifically given a jwt_authn filter what is the lifecycle of the jwt-tokens it holds. I have a vague recollection of cert invalidations (not in Envoy) that this is not an easy problem to address, but that was over 20 years ago, and things may have changed since.
There was a design back at the day to enable such xDS use-cases, but this work has been deprioritized.
If there's a doc out there, i would be interested in checking it out
Note that this will require the xDS server to support delta-xDS (to update individual entries), so I'm not sure if it's applicable to your use-case.
That's fine, we support delta-xDS.
Can you remind me what is the model for adding and removing JWT tokens? Specifically given a jwt_authn filter what is the lifecycle of the jwt-tokens it holds. I have a vague recollection of cert invalidations (not in Envoy) that this is not an easy problem to address, but that was over 20 years ago, and things may have changed since.
The jwt tokens will be received in the filter either via cookies read from the oauth filter or via the Authorization header. We need to know whether those tokens were revoked. The revocation list will be held in memory and when a token is received, it will be checked against this list. for example, a list of session ids (sid claims) so that when a token matches one of those sid claims in the revocation list, it is rejected. delta XDS will send updates to add and remove revocation entries. it will be up to the control plane to keep track of these and ensure that token revocations are added and removed appropriately. How those are distributed to the worker threads is a different question. I suppose each worker thread could hold the entire revocation list or as an optimization, it could be a singleton/
In some ways it's easier than CRLs since CRLs are distributed as a complete signed PEM bundle. When the CRL is updated, the entire thing needs to be reloaded. You can imagine us extending this to CRLs where the control plane reads CRLs and does the incremental update, which would be more efficient.
ECDS was not built to support fine-grained policy within the filter. You could emulate it with embedding the entire list of revoked tokens and then computing the diff internally, but it's not ideal. I think you need YetAnotherDS for fine-grained elements of a filter config, or use an alternate distribution channel (non-xDS, pubsub, files). Ideally, there is an existing third party format for revocations that we natively support in Envoy.
This issue has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in the next 7 days unless it is tagged "help wanted" or "no stalebot" or other activity occurs. Thank you for your contributions.
This issue has been automatically closed because it has not had activity in the last 37 days. If this issue is still valid, please ping a maintainer and ask them to label it as "help wanted" or "no stalebot". Thank you for your contributions.