diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenCreator.java b/simple-jwt/src/main/java/com/onixbyte/jwt/TokenCreator.java index c05d38f..1e3300c 100644 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenCreator.java +++ b/simple-jwt/src/main/java/com/onixbyte/jwt/TokenCreator.java @@ -18,10 +18,29 @@ package com.onixbyte.jwt; /** + * Interface for creating and signing JSON Web Tokens (JWTs). + *
+ * Defines a contract for implementations that generate signed JWTs from a given payload. The + * resulting token is typically a string in the format "header.payload.signature", where the + * signature is created using a cryptographic algorithm specific to the implementation. * + * @author zihluwang */ public interface TokenCreator { + /** + * Signs a token payload to create a JWT. + *
+ * Takes a {@link TokenPayload} object, serialises its claims, and generates a signed + * JWT string. The specific signing algorithm (e.g., HMAC, RSA, ECDSA) depends on + * the implementation. + * + * @param payload the {@link TokenPayload} containing claims to include in the token + * @return the signed JWT as a string in the format "header.payload.signature" + * @throws IllegalArgumentException if the payload cannot be serialised to JSON due to invalid + * data or structure, or if the signing process fails due to + * configuration issues + */ String sign(TokenPayload payload); } diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenManager.java b/simple-jwt/src/main/java/com/onixbyte/jwt/TokenManager.java index fcb596e..f8e8b52 100644 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenManager.java +++ b/simple-jwt/src/main/java/com/onixbyte/jwt/TokenManager.java @@ -1,7 +1,47 @@ +/* + * Copyright (C) 2024-2025 OnixByte. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.onixbyte.jwt; +/** + * Interface for managing JSON Web Tokens (JWTs) with support for signing, verification, and + * payload extraction. + *
+ * Combines the functionality of {@link TokenCreator} for creating signed JWTs and
+ * {@link TokenResolver} for verifying and parsing them, while adding the ability to extract the
+ * payload as a custom type {@code T}. Implementations are expected to handle both token generation
+ * and resolution, providing a unified interface for JWT operations.
+ *
+ * @param
+ * Retrieves the payload from the token and transforms it into the specified type using an
+ * implementation-specific mechanism, such as an adapter or mapper.
+ *
+ * @param token the JWT string from which to extract the payload
+ * @return the payload converted to an object of type {@code T}
+ * @throws IllegalArgumentException if the token is malformed, the signature is invalid, or the
+ * payload cannot be deserialised or converted to
+ * type {@code T}
+ */
T extract(String token);
-
-}
+}
\ No newline at end of file
diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenPayload.java b/simple-jwt/src/main/java/com/onixbyte/jwt/TokenPayload.java
index 506b715..c18b096 100644
--- a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenPayload.java
+++ b/simple-jwt/src/main/java/com/onixbyte/jwt/TokenPayload.java
@@ -24,57 +24,158 @@ import java.time.ZoneId;
import java.util.*;
/**
+ * A builder-style class for constructing JSON Web Token (JWT) payloads.
+ *
+ * Provides a fluent interface to set standard registered claims (e.g., subject, issuer, audience)
+ * and custom claims for a JWT payload. The class supports chaining method calls to build the
+ * payload incrementally, which can then be retrieved as a map for use in JWT creation. Ensures that
+ * registered claims are set using dedicated methods to prevent misuse.
*
+ * @author zihluwang
*/
public class TokenPayload {
+ /**
+ * Creates a new instance of {@link TokenPayload} with an empty payload.
+ *
+ * Initialises the payload with empty collections for claims and audiences, ready for
+ * configuration via the builder methods.
+ *
+ * @return a new {@link TokenPayload} instance
+ */
public static TokenPayload createPayload() {
return new TokenPayload();
}
+ /**
+ * The map storing custom claims for the JWT payload.
+ */
private final Map
+ * Initialises the internal collections for storing claims and audiences, preventing direct
+ * instantiation outside the class.
*/
private TokenPayload() {
payload = new HashMap<>();
audiences = new ArrayList<>();
}
+ /**
+ * Adds a single audience to the JWT payload.
+ *
+ * Appends the specified audience identifier to the list of audiences, allowing the token to be
+ * validated for multiple recipients.
+ *
+ * @param audience the audience identifier to add
+ * @return this {@link TokenPayload} instance for method chaining
+ */
public TokenPayload withAudience(String audience) {
audiences.add(audience);
return this;
}
+ /**
+ * Adds multiple audiences to the JWT payload.
+ *
+ * Appends all provided audience identifiers to the list of audiences, enabling the token to be
+ * validated for multiple recipients.
+ *
+ * @param audiences the audience identifiers to add
+ * @return this {@link TokenPayload} instance for method chaining
+ */
public TokenPayload withAudiences(String... audiences) {
this.audiences.addAll(Arrays.asList(audiences));
return this;
}
+ /**
+ * Sets the subject of the JWT payload.
+ *
+ * Specifies the principal that is the subject of the token, typically identifying the user or
+ * entity the token represents.
+ *
+ * @param subject the subject identifier
+ * @return this {@link TokenPayload} instance for method chaining
+ */
public TokenPayload withSubject(String subject) {
this.subject = subject;
return this;
}
+ /**
+ * Sets the issuer of the JWT payload.
+ *
+ * Specifies the entity that issued the token, allowing recipients to verify the token's origin.
+ *
+ * @param issuer the issuer identifier
+ * @return this {@link TokenPayload} instance for method chaining
+ */
public TokenPayload withIssuer(String issuer) {
this.issuer = issuer;
return this;
}
+ /**
+ * Sets the unique identifier for the JWT payload.
+ *
+ * Assigns a unique token ID to the JWT, which can be used to prevent token reuse or for
+ * tracking purposes.
+ *
+ * @param tokenId the unique token identifier
+ * @return this {@link TokenPayload} instance for method chaining
+ */
public TokenPayload withTokenId(String tokenId) {
this.tokenId = tokenId;
return this;
}
+ /**
+ * Sets the expiration time for the JWT payload.
+ *
+ * Specifies when the token expires, converted to seconds since the Unix epoch based on the
+ * system's default time zone.
+ *
+ * @param expiresAt the expiration time as a {@link LocalDateTime}
+ * @return this {@link TokenPayload} instance for method chaining
+ */
public TokenPayload withExpiresAt(LocalDateTime expiresAt) {
this.expiresAt = expiresAt.atZone(ZoneId.systemDefault())
.toInstant()
@@ -82,6 +183,15 @@ public class TokenPayload {
return this;
}
+ /**
+ * Sets the time before which the JWT must not be accepted.
+ *
+ * Specifies the "not before" time, converted to seconds since the Unix epoch based on the
+ * system's default time zone.
+ *
+ * @param notBefore the time before which the token is invalid, as a {@link LocalDateTime}
+ * @return this {@link TokenPayload} instance for method chaining
+ */
public TokenPayload withNotBefore(LocalDateTime notBefore) {
this.notBefore = notBefore.atZone(ZoneId.systemDefault())
.toInstant()
@@ -89,6 +199,15 @@ public class TokenPayload {
return this;
}
+ /**
+ * Sets the issuance time for the JWT payload.
+ *
+ * Specifies when the token was issued, converted to seconds since the Unix epoch based on the
+ * system's default time zone.
+ *
+ * @param issuedAt the issuance time as a {@link LocalDateTime}
+ * @return this {@link TokenPayload} instance for method chaining
+ */
public TokenPayload withIssuedAt(LocalDateTime issuedAt) {
this.issuedAt = issuedAt.atZone(ZoneId.systemDefault())
.toInstant()
@@ -96,6 +215,17 @@ public class TokenPayload {
return this;
}
+ /**
+ * Adds a custom claim to the JWT payload.
+ *
+ * Stores a custom key-value pair in the payload, provided the key is not a registered claim.
+ * Registered claims must be set using their dedicated methods to ensure proper handling.
+ *
+ * @param name the name of the custom claim
+ * @param value the value of the custom claim
+ * @return this {@link TokenPayload} instance for method chaining
+ * @throws IllegalStateException if the claim name is a registered claim
+ */
public TokenPayload withClaim(String name, String value) {
if (RegisteredClaims.VALUES.contains(name)) {
throw new IllegalStateException("Please set registered claims with pre-defined methods");
@@ -105,10 +235,26 @@ public class TokenPayload {
return this;
}
+ /**
+ * Checks if the JWT payload has a valid issuer.
+ *
+ * Returns {@code true} if the issuer is non-null and not blank, indicating that an issuer has
+ * been set.
+ *
+ * @return {@code true} if an issuer is set, {@code false} otherwise
+ */
public boolean hasIssuer() {
return Objects.nonNull(issuer) && !issuer.isBlank();
}
+ /**
+ * Retrieves the complete JWT payload as a map.
+ *
+ * Constructs a map containing all custom claims, registered claims (if set), and audiences.
+ * Only non-empty or non-blank values are included to ensure a clean payload.
+ *
+ * @return a map containing the JWT payload
+ */
public Map
+ * Defines a contract for implementations that parse, verify, and extract components from JWTs.
+ * Provides methods to validate the token's signature, retrieve its header and payload, and split it
+ * into raw components. Implementations are expected to handle cryptographic verification and JSON
+ * deserialisation specific to their signing algorithm.
*
+ * @author zihluwang
*/
public interface TokenResolver {
/**
- * Verifies the HMAC signature of the provided JWT.
+ * Verifies the signature of the provided JWT.
*
- * Splits the token into its components and uses the configured algorithm and secret to check
- * the signature's validity. If the signature does not match, an exception is thrown by the
- * underlying cryptographic utility.
+ * Splits the token into its components and checks the signature's validity using the
+ * implementation's configured algorithm and key. If the signature does not match, an exception
+ * is thrown.
*
* @param token the JWT string to verify
* @throws IllegalArgumentException if the token is malformed or the signature verification
@@ -68,6 +75,9 @@ public interface TokenResolver {
/**
* Splits a JWT into its raw components: header, payload, and signature.
+ *
+ * Provides a default implementation that separates the token string into its three parts using
+ * dot separators and returns them as a {@link RawTokenComponent}.
*
* @param token the JWT string to split
* @return a {@link RawTokenComponent} containing the header, payload, and signature as strings
diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/Algorithm.java b/simple-jwt/src/main/java/com/onixbyte/jwt/constant/Algorithm.java
index 46c3e07..6a37d57 100644
--- a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/Algorithm.java
+++ b/simple-jwt/src/main/java/com/onixbyte/jwt/constant/Algorithm.java
@@ -18,7 +18,14 @@
package com.onixbyte.jwt.constant;
/**
+ * Enumeration of cryptographic algorithms supported for JSON Web Token (JWT) signing
+ * and verification.
+ *
+ * Defines a set of recognised algorithms including HMAC (HS*), RSA (RS*), and ECDSA (ES*) variants,
+ * each with a specific SHA length (256, 384, or 512 bits). Provides methods to identify the
+ * algorithm type and retrieve its properties.
*
+ * @author zihluwang
*/
public enum Algorithm {
HS256(1, 256, "HmacSHA256"),
@@ -32,32 +39,41 @@ public enum Algorithm {
ES512(3, 512, "SHA512withECDSA");
/**
- *
+ * Bit flag indicating an HMAC-based algorithm.
*/
private static final int HS_FLAG = 1; // 001
/**
- *
+ * Bit flag indicating an RSA-based algorithm.
*/
private static final int RS_FLAG = 2; // 010
/**
- *
+ * Bit flag indicating an ECDSA-based algorithm.
*/
private static final int ES_FLAG = 3; // 011
/**
- *
+ * The type flag identifying the algorithm family (HMAC, RSA, or ECDSA).
*/
private final int typeFlag;
+
+ /**
+ * The length of the SHA hash in bits (256, 384, or 512).
+ */
private final int shaLength;
+
+ /**
+ * The standard name of the algorithm as recognised by the Java Cryptography Architecture (JCA).
+ */
private final String algorithm;
/**
+ * Constructs an algorithm enum constant with the specified type flag, SHA length, and algorithm name.
*
- * @param typeFlag
- * @param shaLength
- * @param algorithm
+ * @param typeFlag the bit flag identifying the algorithm type
+ * @param shaLength the length of the SHA hash in bits
+ * @param algorithm the JCA-compliant algorithm name
*/
Algorithm(int typeFlag, int shaLength, String algorithm) {
this.typeFlag = typeFlag;
@@ -66,48 +82,54 @@ public enum Algorithm {
}
/**
+ * Determines whether this algorithm is HMAC-based.
*
- * @return
+ * @return {@code true} if the algorithm uses HMAC (e.g., HS256, HS384, HS512), {@code false} otherwise
*/
public boolean isHmac() {
return (this.typeFlag & HS_FLAG) != 0;
}
/**
+ * Determines whether this algorithm is RSA-based.
*
- * @return
+ * @return {@code true} if the algorithm uses RSA (e.g., RS256, RS384, RS512), {@code false} otherwise
*/
public boolean isRsa() {
return (this.typeFlag & RS_FLAG) != 0;
}
/**
+ * Determines whether this algorithm is ECDSA-based.
*
- * @return
+ * @return {@code true} if the algorithm uses ECDSA (e.g., ES256, ES384, ES512), {@code false} otherwise
*/
public boolean isEcdsa() {
return (this.typeFlag & ES_FLAG) != 0;
}
/**
+ * Retrieves the SHA length of this algorithm in bits.
*
- * @return
+ * @return the SHA length (256, 384, or 512)
*/
public int getShaLength() {
return shaLength;
}
/**
+ * Retrieves the type flag of this algorithm.
*
- * @return
+ * @return the type flag (1 for HMAC, 2 for RSA, 3 for ECDSA)
*/
public int getTypeFlag() {
return typeFlag;
}
/**
+ * Retrieves the JCA-compliant name of this algorithm.
*
- * @return
+ * @return the algorithm name (e.g., "HmacSHA256", "SHA256withRSA")
*/
public String getAlgorithm() {
return algorithm;
diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/HeaderClaims.java b/simple-jwt/src/main/java/com/onixbyte/jwt/constant/HeaderClaims.java
index 728b2ee..cb681f7 100644
--- a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/HeaderClaims.java
+++ b/simple-jwt/src/main/java/com/onixbyte/jwt/constant/HeaderClaims.java
@@ -17,6 +17,15 @@
package com.onixbyte.jwt.constant;
+/**
+ * Utility class defining standard header claim names for JSON Web Tokens (JWTs).
+ *
+ * Provides constants representing the recognised header claims as specified in the JWT standard.
+ * These claims are used in the header section of a JWT to describe its structure and cryptographic
+ * properties.
+ *
+ * @author zihluwang
+ */
public final class HeaderClaims {
/**
diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/RegisteredClaims.java b/simple-jwt/src/main/java/com/onixbyte/jwt/constant/RegisteredClaims.java
index dcde8a9..6cf27a5 100644
--- a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/RegisteredClaims.java
+++ b/simple-jwt/src/main/java/com/onixbyte/jwt/constant/RegisteredClaims.java
@@ -20,7 +20,14 @@ package com.onixbyte.jwt.constant;
import java.util.List;
/**
+ * Utility class defining standard registered claim names for JSON Web Tokens (JWTs).
+ *
+ * Provides constants representing the registered claims as defined in RFC 7519. These claims are
+ * used in the payload section of a JWT to convey metadata about the token, such as its issuer,
+ * subject, and validity period. All claims are optional but widely recognised in
+ * JWT implementations.
*
+ * @author zihluwang
*/
public final class RegisteredClaims {
@@ -73,6 +80,13 @@ public final class RegisteredClaims {
*/
public static final String TOKEN_ID = "jti";
+ /**
+ * An immutable list of all registered claim names defined in this class.
+ *
+ * Contains the values of {@link #ISSUER}, {@link #SUBJECT}, {@link #AUDIENCE},
+ * {@link #EXPIRES_AT}, {@link #NOT_BEFORE}, {@link #ISSUED_AT}, and {@link #TOKEN_ID} for
+ * convenient iteration or lookup.
+ */
public static final List
+ * Holds the header, payload, and signature of a JWT as strings, typically in their Base64 URL-encoded
+ * form as extracted from a token string. This record is used to facilitate parsing and processing
+ * of JWTs without decoding or validating their contents.
*
- * @param header
- * @param payload
- * @param signature
+ * @param header the Base64 URL-encoded header string of the JWT
+ * @param payload the Base64 URL-encoded payload string of the JWT
+ * @param signature the Base64 URL-encoded signature string of the JWT
* @author zihluwang
*/
public record RawTokenComponent(
diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/holder/ObjectMapperHolder.java b/simple-jwt/src/main/java/com/onixbyte/jwt/holder/ObjectMapperHolder.java
index 653b776..28c5ff2 100644
--- a/simple-jwt/src/main/java/com/onixbyte/jwt/holder/ObjectMapperHolder.java
+++ b/simple-jwt/src/main/java/com/onixbyte/jwt/holder/ObjectMapperHolder.java
@@ -23,6 +23,16 @@ import com.fasterxml.jackson.databind.json.JsonMapper;
import java.util.Objects;
+/**
+ * Singleton holder for a configured {@link ObjectMapper} instance.
+ *
+ * Provides a thread-safe, lazily initialised singleton to manage a single {@link ObjectMapper}
+ * instance, configured to sort JSON properties alphabetically. This class is designed to ensure
+ * consistent JSON serialisation and deserialisation across the application, particularly for
+ * JSON Web Token (JWT) processing.
+ *
+ * @author zihluwang
+ */
public class ObjectMapperHolder {
private static ObjectMapperHolder HOLDER;
@@ -39,9 +49,12 @@ public class ObjectMapperHolder {
}
/**
- * Get singleton instance.
+ * Retrieves the singleton instance of this holder.
+ *
+ * Uses double-checked locking to ensure thread-safe, lazy initialisation of the singleton. If
+ * the instance has not been created, it is initialised in a synchronised block.
*
- * @return the {@code ObjectMapperHolder} instance
+ * @return the singleton {@link ObjectMapperHolder} instance
*/
public static ObjectMapperHolder getInstance() {
if (Objects.isNull(HOLDER)) {
@@ -55,9 +68,9 @@ public class ObjectMapperHolder {
}
/**
- * Get Object
+ * Retrieves the configured {@link ObjectMapper} instance.
*
- * @return
+ * @return the {@link ObjectMapper} configured for alphabetical property sorting
*/
public ObjectMapper getObjectMapper() {
return objectMapper;
diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenManager.java b/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenManager.java
index fa3dd20..b1925ed 100644
--- a/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenManager.java
+++ b/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenManager.java
@@ -1,3 +1,20 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.onixbyte.jwt.impl;
import com.onixbyte.devkit.utils.MapUtil;
@@ -10,39 +27,116 @@ import com.onixbyte.jwt.constant.Algorithm;
import java.util.Map;
+/**
+ * A generic token manager implementation for creating, verifying, and extracting data from
+ * HMAC-signed JSON Web Tokens (JWTs).
+ *
+ * This class integrates an {@link HmacTokenCreator} for signing tokens and an
+ * {@link HmacTokenResolver} for verification and parsing, using a specified HMAC algorithm. It
+ * supports converting token payloads to a custom type {@code T} via an {@link ObjectMapAdapter}.
+ *
+ * @param
+ * Initialises the {@link TokenCreator} and {@link TokenResolver} with the provided HMAC
+ * algorithm and secret key, and associates an adapter for converting token payloads to the
+ * generic type {@code T}.
+ *
+ * @param algorithm the HMAC algorithm to use for signing and verification
+ * @param issuer the issuer identifier to include in the token payload if not already present
+ * @param secret the secret key as a string, used to sign and verify the HMAC signature
+ * @param adapter the {@link ObjectMapAdapter} for converting payload maps to objects of
+ * type {@code T}
+ * @throws IllegalArgumentException if the secret key is too short for the specified algorithm
+ */
public HmacTokenManager(Algorithm algorithm, String issuer, String secret, ObjectMapAdapter
+ * Retrieves the payload as a map and uses the configured {@link ObjectMapAdapter} to transform
+ * it into the desired type.
+ *
+ * @param token the JWT string from which to extract the payload
+ * @return the payload converted to an object of type {@code T}
+ * @throws IllegalArgumentException if the token is malformed, the signature is invalid, or the
+ * payload cannot be deserialised
+ */
@Override
public T extract(String token) {
var payloadMap = getPayload(token);
return MapUtil.mapToObject(payloadMap, adapter);
}
+ /**
+ * Signs a token payload to create a JWT.
+ *
+ * Delegates to the {@link TokenCreator} to generate a signed JWT string from the
+ * provided payload.
+ *
+ * @param payload the {@link TokenPayload} containing claims to include in the token
+ * @return the signed JWT as a string in the format "header.payload.signature"
+ * @throws IllegalArgumentException if the payload cannot be serialised to JSON due to invalid
+ * data or structure
+ */
@Override
public String sign(TokenPayload payload) {
return tokenCreator.sign(payload);
}
+ /**
+ * Verifies the validity of a JWT.
+ *
+ * Delegates to the {@link TokenResolver} to check the token's signature and structure.
+ *
+ * @param token the JWT string to verify
+ * @throws IllegalArgumentException if the token is malformed or the signature
+ * verification fails
+ */
@Override
public void verify(String token) {
tokenResolver.verify(token);
}
+ /**
+ * Retrieves the header claims from the provided JWT.
+ *
+ * Decodes the Base64-encoded header and deserialises it into a map of strings.
+ *
+ * @param token the JWT string from which to extract the header
+ * @return a map containing the header claims as key-value pairs
+ * @throws IllegalArgumentException if the token is malformed or the header cannot be
+ * deserialised due to invalid JSON format
+ */
@Override
public Map
+ * Delegates to the {@link TokenResolver} to extract and deserialise the payload into a map,
+ * excluding registered claims.
+ *
+ * @param token the JWT string from which to extract the payload
+ * @return a map containing the custom payload claims as key-value pairs
+ * @throws IllegalArgumentException if the token is malformed or the payload cannot
+ * be deserialised
+ */
@Override
public Map