Skip to content

Commit 3b61def

Browse files
committed
Normalizing temporal claims to avoid parsing issues.
1 parent d14ae86 commit 3b61def

2 files changed

Lines changed: 36 additions & 2 deletions

File tree

libs/texas/src/main/java/no/nav/dolly/libs/texas/TexasTokenIntrospector.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
import tools.jackson.databind.JsonNode;
1616
import tools.jackson.databind.ObjectMapper;
1717

18+
import java.time.Instant;
1819
import java.util.Arrays;
1920
import java.util.Collections;
2021
import java.util.HashMap;
22+
import java.util.Map;
2123
import java.util.Optional;
2224
import java.util.stream.Stream;
2325

@@ -73,8 +75,9 @@ private Mono<OAuth2AuthenticatedPrincipal> parseAndValidate(String introspection
7375

7476
// Extract all claims from the introspection result.
7577
var attributes = objectMapper.convertValue(root, new TypeReference<HashMap<String, Object>>() {});
78+
normalizeTemporalClaims(attributes);
7679

77-
// Build "scope" or "roles" claim in second argument, for role based authorization? Check token.
80+
// Authorities are intentionally empty; access is enforced by authenticated token validation.
7881
return Mono.just(new OAuth2IntrospectionAuthenticatedPrincipal(attributes, Collections.emptyList()));
7982

8083
} catch (Exception e) {
@@ -84,6 +87,30 @@ private Mono<OAuth2AuthenticatedPrincipal> parseAndValidate(String introspection
8487

8588
}
8689

90+
private void normalizeTemporalClaims(Map<String, Object> attributes) {
91+
normalizeTemporalClaim(attributes, "exp");
92+
normalizeTemporalClaim(attributes, "iat");
93+
normalizeTemporalClaim(attributes, "nbf");
94+
}
95+
96+
private void normalizeTemporalClaim(Map<String, Object> attributes, String claimName) {
97+
Optional
98+
.ofNullable(attributes.get(claimName))
99+
.ifPresent(value -> {
100+
if (value instanceof Number numberValue) {
101+
attributes.put(claimName, Instant.ofEpochSecond(numberValue.longValue()));
102+
return;
103+
}
104+
if (value instanceof String stringValue) {
105+
try {
106+
attributes.put(claimName, Instant.ofEpochSecond(Long.parseLong(stringValue)));
107+
} catch (NumberFormatException ignored) {
108+
log.warn("Could not parse claim '{}' as epoch seconds", claimName);
109+
}
110+
}
111+
});
112+
}
113+
87114

88115
/**
89116
* Applies opaque token-based resource server security to the given {@link ServerHttpSecurity} builder.

libs/texas/src/test/java/no/nav/dolly/libs/texas/TexasTokenIntrospectorTest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import reactor.core.publisher.Mono;
66
import tools.jackson.databind.ObjectMapper;
77

8+
import java.time.Instant;
89
import java.util.List;
910

1011
import static org.assertj.core.api.Assertions.assertThat;
@@ -27,6 +28,8 @@ void shouldParseIntrospectionResultIntoPlainAttributeTypes() {
2728
"active": true,
2829
"preferred_username": "user@nav.no",
2930
"exp": 1779285182,
31+
"iat": 1779280200,
32+
"nbf": 1779280200,
3033
"groups": ["group-1", "group-2"]
3134
}
3235
"""));
@@ -40,7 +43,11 @@ void shouldParseIntrospectionResultIntoPlainAttributeTypes() {
4043
assertThat(principal.<String>getAttribute("preferred_username"))
4144
.isEqualTo("user@nav.no");
4245
assertThat(principal.<Object>getAttribute("exp"))
43-
.isInstanceOf(Number.class);
46+
.isEqualTo(Instant.ofEpochSecond(1779285182));
47+
assertThat(principal.<Object>getAttribute("iat"))
48+
.isEqualTo(Instant.ofEpochSecond(1779280200));
49+
assertThat(principal.<Object>getAttribute("nbf"))
50+
.isEqualTo(Instant.ofEpochSecond(1779280200));
4451
assertThat(principal.<List<String>>getAttribute("groups"))
4552
.isEqualTo(List.of("group-1", "group-2"));
4653

0 commit comments

Comments
 (0)