Skip to content

Commit 6b5d9f0

Browse files
committed
Polish JwtEncoder APIs
Closes gh-391
1 parent ea7c689 commit 6b5d9f0

File tree

7 files changed

+260
-211
lines changed

7 files changed

+260
-211
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/jwt/JoseHeader.java

Lines changed: 63 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import java.util.function.Consumer;
2525

2626
import org.springframework.security.oauth2.core.converter.ClaimConversionService;
27-
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
27+
import org.springframework.security.oauth2.jose.JwaAlgorithm;
2828
import org.springframework.util.Assert;
2929

3030
/**
@@ -36,9 +36,9 @@
3636
* @author Joe Grandja
3737
* @since 0.0.1
3838
* @see Jwt
39-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519#section-5">JWT JOSE Header</a>
40-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515#section-4">JWS JOSE Header</a>
41-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7516#section-4">JWE JOSE Header</a>
39+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7519#section-5">JWT JOSE Header</a>
40+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7515#section-4">JWS JOSE Header</a>
41+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7516#section-4">JWE JOSE Header</a>
4242
*/
4343
public final class JoseHeader {
4444
private final Map<String, Object> headers;
@@ -48,12 +48,13 @@ private JoseHeader(Map<String, Object> headers) {
4848
}
4949

5050
/**
51-
* Returns the {@link JwsAlgorithm JWS algorithm} used to digitally sign the JWS.
51+
* Returns the {@link JwaAlgorithm JWA algorithm} used to digitally sign the JWS or encrypt the JWE.
5252
*
53-
* @return the JWS algorithm
53+
* @return the {@link JwaAlgorithm}
5454
*/
55-
public JwsAlgorithm getJwsAlgorithm() {
56-
return getHeader(JoseHeaderNames.ALG);
55+
@SuppressWarnings("unchecked")
56+
public <T extends JwaAlgorithm> T getAlgorithm() {
57+
return (T) getHeader(JoseHeaderNames.ALG);
5758
}
5859

5960
/**
@@ -62,7 +63,7 @@ public JwsAlgorithm getJwsAlgorithm() {
6263
*
6364
* @return the JWK Set URL
6465
*/
65-
public URL getJwkSetUri() {
66+
public URL getJwkSetUrl() {
6667
return getHeader(JoseHeaderNames.JKU);
6768
}
6869

@@ -91,13 +92,16 @@ public String getKeyId() {
9192
*
9293
* @return the X.509 URL
9394
*/
94-
public URL getX509Uri() {
95+
public URL getX509Url() {
9596
return getHeader(JoseHeaderNames.X5U);
9697
}
9798

9899
/**
99100
* Returns the X.509 certificate chain that contains the X.509 public key certificate
100-
* or certificate chain corresponding to the key used to digitally sign the JWS or encrypt the JWE.
101+
* or certificate chain corresponding to the key used to digitally sign the JWS or
102+
* encrypt the JWE. The certificate or certificate chain is represented as a
103+
* {@code List} of certificate value {@code String}s. Each {@code String} in the
104+
* {@code List} is a Base64-encoded DER PKIX certificate value.
101105
*
102106
* @return the X.509 certificate chain
103107
*/
@@ -125,16 +129,6 @@ public String getX509SHA256Thumbprint() {
125129
return getHeader(JoseHeaderNames.X5T_S256);
126130
}
127131

128-
/**
129-
* Returns the critical headers that indicates which extensions to the JWS/JWE/JWA specifications
130-
* are being used that MUST be understood and processed.
131-
*
132-
* @return the critical headers
133-
*/
134-
public Set<String> getCritical() {
135-
return getHeader(JoseHeaderNames.CRIT);
136-
}
137-
138132
/**
139133
* Returns the type header that declares the media type of the JWS/JWE.
140134
*
@@ -153,6 +147,16 @@ public String getContentType() {
153147
return getHeader(JoseHeaderNames.CTY);
154148
}
155149

150+
/**
151+
* Returns the critical headers that indicates which extensions to the JWS/JWE/JWA specifications
152+
* are being used that MUST be understood and processed.
153+
*
154+
* @return the critical headers
155+
*/
156+
public Set<String> getCritical() {
157+
return getHeader(JoseHeaderNames.CRIT);
158+
}
159+
156160
/**
157161
* Returns the headers.
158162
*
@@ -185,13 +189,13 @@ public static Builder builder() {
185189
}
186190

187191
/**
188-
* Returns a new {@link Builder}, initialized with the provided {@link JwsAlgorithm}.
192+
* Returns a new {@link Builder}, initialized with the provided {@link JwaAlgorithm}.
189193
*
190-
* @param jwsAlgorithm the {@link JwsAlgorithm}
194+
* @param jwaAlgorithm the {@link JwaAlgorithm}
191195
* @return the {@link Builder}
192196
*/
193-
public static Builder withAlgorithm(JwsAlgorithm jwsAlgorithm) {
194-
return new Builder(jwsAlgorithm);
197+
public static Builder withAlgorithm(JwaAlgorithm jwaAlgorithm) {
198+
return new Builder(jwaAlgorithm);
195199
}
196200

197201
/**
@@ -213,9 +217,8 @@ public static final class Builder {
213217
private Builder() {
214218
}
215219

216-
private Builder(JwsAlgorithm jwsAlgorithm) {
217-
Assert.notNull(jwsAlgorithm, "jwsAlgorithm cannot be null");
218-
header(JoseHeaderNames.ALG, jwsAlgorithm);
220+
private Builder(JwaAlgorithm jwaAlgorithm) {
221+
algorithm(jwaAlgorithm);
219222
}
220223

221224
private Builder(JoseHeader headers) {
@@ -224,24 +227,25 @@ private Builder(JoseHeader headers) {
224227
}
225228

226229
/**
227-
* Sets the {@link JwsAlgorithm JWS algorithm} used to digitally sign the JWS.
230+
* Sets the {@link JwaAlgorithm JWA algorithm} used to digitally sign the JWS or encrypt the JWE.
228231
*
229-
* @param jwsAlgorithm the JWS algorithm
232+
* @param jwaAlgorithm the {@link JwaAlgorithm}
230233
* @return the {@link Builder}
231234
*/
232-
public Builder jwsAlgorithm(JwsAlgorithm jwsAlgorithm) {
233-
return header(JoseHeaderNames.ALG, jwsAlgorithm);
235+
public Builder algorithm(JwaAlgorithm jwaAlgorithm) {
236+
Assert.notNull(jwaAlgorithm, "jwaAlgorithm cannot be null");
237+
return header(JoseHeaderNames.ALG, jwaAlgorithm);
234238
}
235239

236240
/**
237241
* Sets the JWK Set URL that refers to the resource of a set of JSON-encoded public keys,
238242
* one of which corresponds to the key used to digitally sign the JWS or encrypt the JWE.
239243
*
240-
* @param jwkSetUri the JWK Set URL
244+
* @param jwkSetUrl the JWK Set URL
241245
* @return the {@link Builder}
242246
*/
243-
public Builder jwkSetUri(String jwkSetUri) {
244-
return header(JoseHeaderNames.JKU, jwkSetUri);
247+
public Builder jwkSetUrl(String jwkSetUrl) {
248+
return header(JoseHeaderNames.JKU, convertAsURL(JoseHeaderNames.JKU, jwkSetUrl));
245249
}
246250

247251
/**
@@ -269,16 +273,19 @@ public Builder keyId(String keyId) {
269273
* Sets the X.509 URL that refers to the resource for the X.509 public key certificate
270274
* or certificate chain corresponding to the key used to digitally sign the JWS or encrypt the JWE.
271275
*
272-
* @param x509Uri the X.509 URL
276+
* @param x509Url the X.509 URL
273277
* @return the {@link Builder}
274278
*/
275-
public Builder x509Uri(String x509Uri) {
276-
return header(JoseHeaderNames.X5U, x509Uri);
279+
public Builder x509Url(String x509Url) {
280+
return header(JoseHeaderNames.X5U, convertAsURL(JoseHeaderNames.X5U, x509Url));
277281
}
278282

279283
/**
280284
* Sets the X.509 certificate chain that contains the X.509 public key certificate
281-
* or certificate chain corresponding to the key used to digitally sign the JWS or encrypt the JWE.
285+
* or certificate chain corresponding to the key used to digitally sign the JWS or
286+
* encrypt the JWE. The certificate or certificate chain is represented as a
287+
* {@code List} of certificate value {@code String}s. Each {@code String} in the
288+
* {@code List} is a Base64-encoded DER PKIX certificate value.
282289
*
283290
* @param x509CertificateChain the X.509 certificate chain
284291
* @return the {@link Builder}
@@ -309,17 +316,6 @@ public Builder x509SHA256Thumbprint(String x509SHA256Thumbprint) {
309316
return header(JoseHeaderNames.X5T_S256, x509SHA256Thumbprint);
310317
}
311318

312-
/**
313-
* Sets the critical headers that indicates which extensions to the JWS/JWE/JWA specifications
314-
* are being used that MUST be understood and processed.
315-
*
316-
* @param headerNames the critical header names
317-
* @return the {@link Builder}
318-
*/
319-
public Builder critical(Set<String> headerNames) {
320-
return header(JoseHeaderNames.CRIT, headerNames);
321-
}
322-
323319
/**
324320
* Sets the type header that declares the media type of the JWS/JWE.
325321
*
@@ -340,6 +336,17 @@ public Builder contentType(String contentType) {
340336
return header(JoseHeaderNames.CTY, contentType);
341337
}
342338

339+
/**
340+
* Sets the critical headers that indicates which extensions to the JWS/JWE/JWA specifications
341+
* are being used that MUST be understood and processed.
342+
*
343+
* @param headerNames the critical header names
344+
* @return the {@link Builder}
345+
*/
346+
public Builder critical(Set<String> headerNames) {
347+
return header(JoseHeaderNames.CRIT, headerNames);
348+
}
349+
343350
/**
344351
* Sets the header.
345352
*
@@ -373,19 +380,15 @@ public Builder headers(Consumer<Map<String, Object>> headersConsumer) {
373380
*/
374381
public JoseHeader build() {
375382
Assert.notEmpty(this.headers, "headers cannot be empty");
376-
convertAsURL(JoseHeaderNames.JKU);
377-
convertAsURL(JoseHeaderNames.X5U);
378383
return new JoseHeader(this.headers);
379384
}
380385

381-
private void convertAsURL(String header) {
382-
Object value = this.headers.get(header);
383-
if (value != null) {
384-
URL convertedValue = ClaimConversionService.getSharedInstance().convert(value, URL.class);
385-
Assert.isTrue(convertedValue != null,
386-
() -> "Unable to convert header '" + header + "' of type '" + value.getClass() + "' to URL.");
387-
this.headers.put(header, convertedValue);
388-
}
386+
private static URL convertAsURL(String header, String value) {
387+
URL convertedValue = ClaimConversionService.getSharedInstance().convert(value, URL.class);
388+
Assert.isTrue(convertedValue != null,
389+
() -> "Unable to convert header '" + header + "' of type '" + value.getClass() + "' to URL.");
390+
return convertedValue;
389391
}
392+
390393
}
391394
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/jwt/JwtClaimsSet.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
*/
1616
package org.springframework.security.oauth2.jwt;
1717

18+
import java.net.URL;
1819
import java.time.Instant;
1920
import java.util.Collections;
2021
import java.util.HashMap;
2122
import java.util.List;
2223
import java.util.Map;
2324
import java.util.function.Consumer;
2425

26+
import org.springframework.security.oauth2.core.converter.ClaimConversionService;
2527
import org.springframework.util.Assert;
2628

2729
/**
@@ -32,7 +34,7 @@
3234
* @since 0.0.1
3335
* @see Jwt
3436
* @see JwtClaimAccessor
35-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519#section-4">JWT Claims Set</a>
37+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7519#section-4">JWT Claims Set</a>
3638
*/
3739
public final class JwtClaimsSet implements JwtClaimAccessor {
3840
private final Map<String, Object> claims;
@@ -166,10 +168,10 @@ public Builder claim(String name, Object value) {
166168
}
167169

168170
/**
169-
* A {@code Consumer} to be provided access to the claims set
171+
* A {@code Consumer} to be provided access to the claims
170172
* allowing the ability to add, replace, or remove.
171173
*
172-
* @param claimsConsumer a {@code Consumer} of the claims set
174+
* @param claimsConsumer a {@code Consumer} of the claims
173175
*/
174176
public Builder claims(Consumer<Map<String, Object>> claimsConsumer) {
175177
claimsConsumer.accept(this.claims);
@@ -183,6 +185,17 @@ public Builder claims(Consumer<Map<String, Object>> claimsConsumer) {
183185
*/
184186
public JwtClaimsSet build() {
185187
Assert.notEmpty(this.claims, "claims cannot be empty");
188+
189+
// The value of the 'iss' claim is a String or URL (StringOrURI).
190+
// Attempt to convert to URL.
191+
Object issuer = this.claims.get(JwtClaimNames.ISS);
192+
if (issuer != null) {
193+
URL convertedValue = ClaimConversionService.getSharedInstance().convert(issuer, URL.class);
194+
if (convertedValue != null) {
195+
this.claims.put(JwtClaimNames.ISS, convertedValue);
196+
}
197+
}
198+
186199
return new JwtClaimsSet(this.claims);
187200
}
188201
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/jwt/JwtEncoder.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@
3232
* @see JoseHeader
3333
* @see JwtClaimsSet
3434
* @see JwtDecoder
35-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7519">JSON Web Token (JWT)</a>
36-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515">JSON Web Signature (JWS)</a>
37-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7516">JSON Web Encryption (JWE)</a>
38-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7515#section-3.1">JWS Compact Serialization</a>
39-
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7516#section-3.1">JWE Compact Serialization</a>
35+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7519">JSON Web Token (JWT)</a>
36+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7515">JSON Web Signature (JWS)</a>
37+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7516">JSON Web Encryption (JWE)</a>
38+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7515#section-3.1">JWS Compact Serialization</a>
39+
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7516#section-3.1">JWE Compact Serialization</a>
4040
*/
4141
@FunctionalInterface
4242
public interface JwtEncoder {

0 commit comments

Comments
 (0)