Skip to content

Commit 8c1201a

Browse files
Fabio Guencijzheaux
Fabio Guenci
authored andcommitted
Preserve Null Claim Values
Prior to this commit ClaimTypeConverter returned the claims with the original value for all the claims with a null converted value. The changes allows ClaimTypeConverter to overwrite and return claims with converted value of null. Closes gh-10135
1 parent fefe985 commit 8c1201a

File tree

3 files changed

+31
-29
lines changed

3 files changed

+31
-29
lines changed

oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/converter/ClaimTypeConverter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverter.java

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,18 +52,19 @@ public final class MappedJwtClaimSetConverter implements Converter<Map<String, O
5252

5353
private final Map<String, Converter<Object, ?>> claimTypeConverters;
5454

55-
private final Converter<Map<String, Object>, Map<String, Object>> delegate;
56-
5755
/**
5856
* Constructs a {@link MappedJwtClaimSetConverter} with the provided arguments
5957
*
6058
* This will completely replace any set of default converters.
59+
*
60+
* A converter that returns {@code null} removes the claim from the claim set. A
61+
* converter that returns a non-{@code null} value adds or replaces that claim in the
62+
* claim set.
6163
* @param claimTypeConverters The {@link Map} of converters to use
6264
*/
6365
public MappedJwtClaimSetConverter(Map<String, Converter<Object, ?>> claimTypeConverters) {
6466
Assert.notNull(claimTypeConverters, "claimTypeConverters cannot be null");
6567
this.claimTypeConverters = claimTypeConverters;
66-
this.delegate = new ClaimTypeConverter(claimTypeConverters);
6768
}
6869

6970
/**
@@ -87,6 +88,10 @@ public MappedJwtClaimSetConverter(Map<String, Converter<Object, ?>> claimTypeCon
8788
*
8889
* To completely replace the underlying {@link Map} of converters, see
8990
* {@link MappedJwtClaimSetConverter#MappedJwtClaimSetConverter(Map)}.
91+
*
92+
* A converter that returns {@code null} removes the claim from the claim set. A
93+
* converter that returns a non-{@code null} value adds or replaces that claim in the
94+
* claim set.
9095
* @param claimTypeConverters
9196
* @return An instance of {@link MappedJwtClaimSetConverter} that contains the
9297
* converters provided, plus any defaults that were not overridden.
@@ -143,9 +148,16 @@ private static String convertIssuer(Object source) {
143148
@Override
144149
public Map<String, Object> convert(Map<String, Object> claims) {
145150
Assert.notNull(claims, "claims cannot be null");
146-
Map<String, Object> mappedClaims = this.delegate.convert(claims);
147-
mappedClaims = removeClaims(mappedClaims);
148-
mappedClaims = addClaims(mappedClaims);
151+
Map<String, Object> mappedClaims = new HashMap<>(claims);
152+
for (Map.Entry<String, Converter<Object, ?>> entry : this.claimTypeConverters.entrySet()) {
153+
String claimName = entry.getKey();
154+
Converter<Object, ?> converter = entry.getValue();
155+
if (converter != null) {
156+
Object claim = claims.get(claimName);
157+
Object mappedClaim = converter.convert(claim);
158+
mappedClaims.compute(claimName, (key, value) -> mappedClaim);
159+
}
160+
}
149161
Instant issuedAt = (Instant) mappedClaims.get(JwtClaimNames.IAT);
150162
Instant expiresAt = (Instant) mappedClaims.get(JwtClaimNames.EXP);
151163
if (issuedAt == null && expiresAt != null) {
@@ -154,24 +166,4 @@ public Map<String, Object> convert(Map<String, Object> claims) {
154166
return mappedClaims;
155167
}
156168

157-
private Map<String, Object> removeClaims(Map<String, Object> claims) {
158-
Map<String, Object> result = new HashMap<>();
159-
for (Map.Entry<String, Object> entry : claims.entrySet()) {
160-
if (entry.getValue() != null) {
161-
result.put(entry.getKey(), entry.getValue());
162-
}
163-
}
164-
return result;
165-
}
166-
167-
private Map<String, Object> addClaims(Map<String, Object> claims) {
168-
Map<String, Object> result = new HashMap<>(claims);
169-
for (Map.Entry<String, Converter<Object, ?>> entry : this.claimTypeConverters.entrySet()) {
170-
if (!claims.containsKey(entry.getKey()) && entry.getValue().convert(null) != null) {
171-
result.put(entry.getKey(), entry.getValue().convert(null));
172-
}
173-
}
174-
return result;
175-
}
176-
177169
}

oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverterTests.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -123,8 +123,18 @@ public void convertWhenUsingCustomConverterThenAllOtherDefaultsAreStillUsed() {
123123
assertThat(target.get(JwtClaimNames.SUB)).isEqualTo("1234");
124124
}
125125

126+
// gh-10135
126127
@Test
127128
public void convertWhenConverterReturnsNullThenClaimIsRemoved() {
129+
MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter
130+
.withDefaults(Collections.singletonMap(JwtClaimNames.NBF, (nbfClaimValue) -> null));
131+
Map<String, Object> source = Collections.singletonMap(JwtClaimNames.NBF, Instant.now());
132+
Map<String, Object> target = converter.convert(source);
133+
assertThat(target).doesNotContainKey(JwtClaimNames.NBF);
134+
}
135+
136+
@Test
137+
public void convertWhenClaimValueIsNullThenClaimIsRemoved() {
128138
MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());
129139
Map<String, Object> source = Collections.singletonMap(JwtClaimNames.ISS, null);
130140
Map<String, Object> target = converter.convert(source);

0 commit comments

Comments
 (0)