-
Notifications
You must be signed in to change notification settings - Fork 6k
JWT encoding support for OAuth 2.0 #8583
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
JWT encoding support for OAuth 2.0 #8583
Conversation
@krajcsovszkig-ms Please sign the Contributor License Agreement! Click here to manually synchronize the status of this Pull Request. See the FAQ for frequently asked questions. |
I thought the I figured an implementation of the |
@krajcsovszkig-ms Thank you for signing the Contributor License Agreement! |
* @throws JwtException if an error occurs while attempting to encode the JWT | ||
*/ | ||
String encode(Jwt token) throws JwtException; | ||
Jwt encode(Map<String, Object> claims) throws JwtException; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might give a more type-safe experience to do:
String encode(Consumer<Jwt.Builder> jwtBuilderConsumer) throws JwtException
The other nice thing about this is that it makes it easier for the caller to override whatever defaults the JwtEncoder
implementation sets since the implementation would apply the Consumer
just before signing.
cc @jgrandja
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jzheaux I don't think the JwtEncoder
should set any default claims, those are going to come from the ClientRegistration
, so a Consumer
doesn't really feel intuitive to me here. For type safety we could create a JwtClaimsSet
class, which could be used for the overrides as well in the ClientRegistration
.
@krajcsovszkig-ms @jzheaux We are also needing a /cc @anoopgarlapati I think the design proposed by @jzheaux might work. Given the following:
An application would configure the builder
.headers(headers -> headers.put("custom-header", "header-value"))
.claims(claims -> claims.put("custom-claim", "claim-value")); We already have the capability to set a The application would configure the public final class NimbusJwtEncoder implements JwtEncoder {
public void setJwtCustomizer(Consumer<Jwt.Builder> jwtCustomizer) {
} Another option to consider is possibly using a An alternative option for
NOTE: I'm not convinced on the Thoughts? |
Reusing this stuff in the AS project makes sense. I think mimicking the design of the existing I don't think overriding the headers would be necessary very often (correct me if my assumption is wrong), so providing that via some other means (e.g. the builder for the implementation) would be good. Alternatively instead of a If it's deemed necessary to have a WDYT? |
I'm 99% sure there will be a use case where the header(s) would need to be modified so we need to account for this. Given that a |
Sure, it'll be necessary occasionally, but perhaps not often enough to require it being a parameter to the method. However it also feels natural to make it a parameter, so I'm fine either way. What would you prefer, separate parameters for the headers and the claims, or a new type that has both? I'm OK with returning |
I agree that using However, I agree with @krajcsovszkig-ms that Also, instead of generic |
I've been re-thinking the
Although this may work, it doesn't feel right that the
I think this makes a lot of sense. And @anoopgarlapati is in agreement as well
The JWT RFC defines the term Unsecured JWTs so I think it makes sense to provide a new class called public class UnsecuredJwt {
private JoseHeaders headers;
private JwtClaimsSet claimsSet;
}
public class JoseHeaders {
}
public class JwtClaimsSet implements JwtClaimAccessor {
} And the public interface JwtEncoder {
Jwt encode(UnsecuredJwt unsecuredJwt);
} And we would provide a way for applications to customize the claims and/or headers via: public final class NimbusJwtEncoder implements JwtEncoder {
public final void setJwtCustomizer(Consumer<Jwt.Builder> jwtCustomizer) {
} Thoughts? @krajcsovszkig-ms @anoopgarlapati Given that both of you are working on this on different ends, I want to make sure we are not duplicating efforts. So before implementation begins let's figure out how to get this done without duplicating code. We can figure this out after we align on API design. |
@jgrandja sounds good. |
Just to be clear, this isn't what's being proposed, but instead a
This is actually the case already with public final void setJwtCustomizer(Consumer<Jwt.Builder> jwtCustomizer) {
} The concern I have with (Note that this has recently come up a great deal with the SAML support - and while OAuth != SAML, that's just what's making me think of this.) Regarding some of the ideas around For those reasons, I'd still recommend returning a |
If we return a What's wrong with providing an Also, why would something called an "encoder" even generate stuff to encode? Where would an implementation get the data to use for generating the JWT contents, if not the If the user needs a One other problem I have with the |
If the While I agree that there may be value that design constraint, I see a String encoded = this.jwtEncoder.encode(jwt -> jwt.subject("subject")); And not have to worry about the rest. Or, in the circumstance where I'd like to have a globally configured strategy that Instant myOverride = ...;
String encoded = this.jwtEncoder.encode(jwt -> jwt
.subject("subject")
.expiresAt(myOverride));
Fair question. For me, I prefer to leave that to the implementer. Stating via the contract that an implementation
Good point. I wonder if @jgrandja's idea regarding |
As a side note, I wonder if we should consider merging this into Spring Authorization Server instead for now so that folks can start playing with it. That would probably be helpful feedback in evaluating different contracts. Trying to merge this into Spring Security before Spring Authorization Server can use it I think really raises the bar for consensus. |
I'm suggesting that However I'm completely unfamiliar with the plans for the authorization server, so I don't know if something there would make this approach awkward. |
I see my original comment wasn't clear. Yes agreed that the Let me restate my main points:
The original design intent of |
@krajcsovszkig-ms We're going to hold off with implementing this on the client side for now. @jzheaux makes a great point as far as driving out the design and implementation on the authorization server side first as it will be more involved on that end. After we're comfortable with the design and implementation, we could proceed on the client side. So for now, let's hold off on this PR. Please track the development in spring-authorization-server#81 and see if what we are designing there will suit client as well. Thank you so much for all your feedback. |
How long do you think that's going to take? Unfortunately we can't really afford to wait too long with this feature. Do you see any reason why a |
@anoopgarlapati Is working on this task now. I'm thinking we'll have the initial implementation merged in a couple of weeks.
Understood. At the same time, we cannot afford to not get this feature right the first time around. We need to ensure the API is flexible for any use case. Design takes time and we need to ensure we give it the time. The one thing I will say is that we are still targeting to get this into 5.4. The plan now is to flush out the design and implementation on AS side. After we feel comfortable with the implementation on AS, we will merge it into Spring Security. When it's merged into Spring Security, then we can continue with the JWT client auth. If you want, you could use the |
@krajcsovszkig-ms I'm going to close this PR given the work is being done in AS side. After we complete the work there and merge into Spring Security then you can submit a new PR based on the new |
OK. @anoopgarlapati @jgrandja please let me know if there's any way I can help, and keep us updated on how the design evolves. |
@krajcsovszkig-ms For sure we'll keep you in the loop. |
This PR is to satisfy the JWT-signing and -encoding requirements of #6881
See also PR #8445
This mainly concerns the
spring-security-oauth2-jose
project.The missing pieces we aim to add here are:
Encoder
counterparts to the existingDecoder
s inorg.springframework.security.oauth2.jwt
Jwt
that can store a full or partial set of JWT claims and JOSE headersEC
(and potentially further) signing algorithms toNimbusJwtDecoder