1package dev.rafex.ether.jwt.internal;
29import com.fasterxml.jackson.core.type.TypeReference;
30import com.fasterxml.jackson.databind.JsonNode;
31import com.fasterxml.jackson.databind.ObjectMapper;
32import com.fasterxml.jackson.databind.node.ArrayNode;
33import com.fasterxml.jackson.databind.node.ObjectNode;
34import dev.rafex.ether.jwt.TokenClaims;
35import dev.rafex.ether.jwt.TokenType;
37import java.time.Instant;
38import java.util.ArrayList;
39import java.util.LinkedHashMap;
43public final class ClaimsMapper {
45 private static final ObjectMapper MAPPER =
new ObjectMapper();
47 private ClaimsMapper() {
51 final ObjectNode node = MAPPER.createObjectNode();
53 putString(node,
"sub", claims.subject());
54 putString(node,
"iss", claims.issuer());
55 putArray(node,
"aud", claims.audience());
56 putInstant(node,
"iat", claims.issuedAt());
57 putInstant(node,
"exp", claims.expiresAt());
58 putInstant(node,
"nbf", claims.notBefore());
59 putString(node,
"jti", claims.jwtId());
61 putArray(node,
"roles", claims.roles());
62 if (claims.tokenType() !=
null) {
63 node.put(
"token_type", claims.tokenType().claimValue());
65 putString(node,
"client_id", claims.clientId());
67 for (
final Map.Entry<String, Object> entry : claims.extras().entrySet()) {
68 node.set(entry.getKey(), MAPPER.valueToTree(entry.getValue()));
74 final Map<String, Object> allClaims = MAPPER.convertValue(payload,
new TypeReference<>() {
76 final Map<String, Object> extras =
new LinkedHashMap<>(allClaims);
77 extras.keySet().removeIf(ClaimsMapper::isKnownClaim);
79 final String
tokenTypeRaw = asText(payload.get(
"token_type"));
81 .subject(asText(payload.get(
"sub")))
82 .issuer(asText(payload.get(
"iss")))
83 .audience(readStringList(payload.get(
"aud")))
84 .issuedAt(asInstant(payload.get(
"iat")))
85 .expiresAt(asInstant(payload.get(
"exp")))
86 .notBefore(asInstant(payload.get(
"nbf")))
87 .jwtId(asText(payload.get(
"jti")))
88 .roles(readStringList(payload.get(
"roles")))
90 .clientId(asText(payload.get(
"client_id")))
96 return asText(payload.get(
"token_type"));
99 private static boolean isKnownClaim(
final String claim) {
100 return "sub".equals(claim)
101 ||
"iss".equals(claim)
102 ||
"aud".equals(claim)
103 ||
"exp".equals(claim)
104 ||
"iat".equals(claim)
105 ||
"nbf".equals(claim)
106 ||
"jti".equals(claim)
107 ||
"roles".equals(claim)
108 ||
"token_type".equals(claim)
109 ||
"client_id".equals(claim);
112 private static void putString(
final ObjectNode node,
final String name,
final String value) {
113 if (value !=
null && !value.isBlank()) {
114 node.put(name, value);
118 private static void putInstant(
final ObjectNode node,
final String name,
final Instant value) {
120 node.put(name, value.getEpochSecond());
124 private static void putArray(
final ObjectNode node,
final String name,
final List<String> values) {
125 if (values ==
null || values.isEmpty()) {
128 final ArrayNode arrayNode = node.putArray(name);
129 for (
final String value : values) {
130 if (value !=
null && !value.isBlank()) {
131 arrayNode.add(value);
136 private static String asText(
final JsonNode node) {
137 if (node ==
null || node.isNull()) {
140 final String text = node.asText();
141 return text ==
null || text.isBlank() ? null : text;
144 private static Instant asInstant(
final JsonNode node) {
145 if (node ==
null || node.isNull()) {
148 if (!node.isIntegralNumber()) {
149 throw new IllegalArgumentException(
"time claim must be epoch seconds");
151 return Instant.ofEpochSecond(node.asLong());
154 private static List<String> readStringList(
final JsonNode node) {
155 if (node ==
null || node.isNull()) {
158 if (node.isArray()) {
159 final List<String> values =
new ArrayList<>();
160 for (
final JsonNode element : node) {
161 final String text = asText(element);
168 final String singleValue = asText(node);
169 return singleValue ==
null ? List.of() : List.of(singleValue);
Normalized claims extracted from a JWT token.
static String tokenTypeRaw(final JsonNode payload)
static TokenClaims fromPayload(final JsonNode payload)
static ObjectNode toPayload(final TokenClaims claims)
Supported business token types.
static TokenType fromClaimValue(final String claimValue)