From 07f697c0caa6ae046103255e21938a7a59040311 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Sat, 26 Apr 2025 19:03:18 +0800 Subject: [PATCH] refactor: remove simple-jwt from develop branch --- simple-jwt/build.gradle.kts | 51 --- .../java/com/onixbyte/jwt/TokenCreator.java | 46 --- .../java/com/onixbyte/jwt/TokenManager.java | 47 --- .../java/com/onixbyte/jwt/TokenPayload.java | 380 ------------------ .../java/com/onixbyte/jwt/TokenResolver.java | 98 ----- .../com/onixbyte/jwt/constant/Algorithm.java | 137 ------- .../onixbyte/jwt/constant/HeaderClaims.java | 57 --- .../jwt/constant/RegisteredClaims.java | 92 ----- .../onixbyte/jwt/data/RawTokenComponent.java | 37 -- .../jwt/holder/ObjectMapperHolder.java | 78 ---- .../onixbyte/jwt/impl/HmacTokenCreator.java | 121 ------ .../onixbyte/jwt/impl/HmacTokenManager.java | 144 ------- .../onixbyte/jwt/impl/HmacTokenResolver.java | 158 -------- .../com/onixbyte/jwt/util/CryptoUtil.java | 133 ------ simple-jwt/src/main/resources/logback.xml | 32 -- 15 files changed, 1611 deletions(-) delete mode 100644 simple-jwt/build.gradle.kts delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/TokenCreator.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/TokenManager.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/TokenPayload.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/TokenResolver.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/constant/Algorithm.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/constant/HeaderClaims.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/constant/RegisteredClaims.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/data/RawTokenComponent.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/holder/ObjectMapperHolder.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenCreator.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenManager.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenResolver.java delete mode 100644 simple-jwt/src/main/java/com/onixbyte/jwt/util/CryptoUtil.java delete mode 100644 simple-jwt/src/main/resources/logback.xml diff --git a/simple-jwt/build.gradle.kts b/simple-jwt/build.gradle.kts deleted file mode 100644 index 03ee131..0000000 --- a/simple-jwt/build.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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. - */ - -plugins { - id("java") - id("java-library") - id("maven-publish") - id("signing") -} - -val artefactVersion: String by project -val projectUrl: String by project -val projectGithubUrl: String by project -val licenseName: String by project -val licenseUrl: String by project - -group = "com.onixbyte" -version = artefactVersion - -repositories { - mavenCentral() -} - -dependencies { - compileOnly(libs.slf4j) - implementation(libs.logback) - api(project(":devkit-core")) - api(project(":devkit-utils")) - implementation(libs.jackson.core) - implementation(libs.jackson.databind) - testImplementation(platform(libs.junit.bom)) - testImplementation(libs.junit.jupiter) -} - -tasks.test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenCreator.java b/simple-jwt/src/main/java/com/onixbyte/jwt/TokenCreator.java deleted file mode 100644 index 1e3300c..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenCreator.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 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 deleted file mode 100644 index f8e8b52..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenManager.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 the type of object to which the token payload will be converted - * @author zihluwang - */ -public interface TokenManager extends TokenCreator, TokenResolver { - - /** - * Extracts the payload from a JWT and converts it to an object of type {@code T}. - *

- * 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 deleted file mode 100644 index 3a0d25d..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenPayload.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * 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; - -import com.onixbyte.jwt.constant.RegisteredClaims; - -import java.time.LocalDateTime; -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 payload; - - /** - * The list of audience identifiers for the JWT. - */ - private final List audiences; - - /** - * The subject of the JWT, identifying the principal. - */ - private String subject; - - /** - * The issuer of the JWT, identifying the entity that issued the token. - */ - private String issuer; - - /** - * The unique identifier for the JWT. - */ - private String tokenId; - - /** - * The expiration time of the JWT, as seconds since the Unix epoch. - */ - private Long expiresAt; - - /** - * The time before which the JWT must not be accepted, as seconds since the Unix epoch. - */ - private Long notBefore; - - /** - * The issuance time of the JWT, as seconds since the Unix epoch. - */ - private Long issuedAt; - - /** - * Private constructor to enforce use of the factory method. - *

- * 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() - .getEpochSecond(); - 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() - .getEpochSecond(); - 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() - .getEpochSecond(); - 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) { - checkClaimName(name); - - this.payload.put(name, value); - 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, Long value) { - checkClaimName(name); - - this.payload.put(name, value); - 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, Double value) { - checkClaimName(name); - - this.payload.put(name, value); - 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, Boolean value) { - checkClaimName(name); - - this.payload.put(name, value); - 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, LocalDateTime value) { - checkClaimName(name); - - this.payload.put(name, value); - return this; - } - - /** - * Adds a custom claim with null value 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 - * @return this {@link TokenPayload} instance for method chaining - * @throws IllegalStateException if the claim name is a registered claim - */ - public TokenPayload withNullClaim(String name) { - checkClaimName(name); - - this.payload.put(name, null); - 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 getPayload() { - var _payload = new HashMap<>(payload); - - Optional.of(audiences) - .filter((aud) -> !aud.isEmpty()) - .ifPresent((aud) -> _payload.put(RegisteredClaims.AUDIENCE, aud)); - Optional.ofNullable(subject) - .filter((sub) -> !sub.isBlank()) - .ifPresent((sub) -> _payload.put(RegisteredClaims.SUBJECT, subject)); - Optional.ofNullable(expiresAt) - .ifPresent((exp) -> _payload.put(RegisteredClaims.EXPIRES_AT, exp)); - Optional.ofNullable(tokenId) - .filter((jti) -> !jti.isBlank()) - .ifPresent((jti) -> _payload.put(RegisteredClaims.TOKEN_ID, jti)); - Optional.ofNullable(issuer) - .filter((iss) -> !iss.isBlank()) - .ifPresent((iss) -> _payload.put(RegisteredClaims.ISSUER, iss)); - Optional.ofNullable(issuedAt) - .ifPresent((iat) -> _payload.put(RegisteredClaims.ISSUED_AT, iat)); - Optional.ofNullable(notBefore) - .ifPresent((nbf) -> _payload.put(RegisteredClaims.NOT_BEFORE, nbf)); - - return _payload; - } - - /** - * Check the given claim name, make sure the name will not be one of the registered claim name. - * - * @param name a claim name - */ - private void checkClaimName(String name) { - if (RegisteredClaims.VALUES.contains(name)) { - throw new IllegalStateException("Please set registered claims with pre-defined methods"); - } - } -} diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenResolver.java b/simple-jwt/src/main/java/com/onixbyte/jwt/TokenResolver.java deleted file mode 100644 index 1f60c03..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/TokenResolver.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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; - -import com.onixbyte.jwt.constant.RegisteredClaims; -import com.onixbyte.jwt.data.RawTokenComponent; - -import java.util.Map; - -/** - * Interface for resolving and verifying JSON Web Tokens (JWTs). - *

- * 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 signature of the provided JWT. - *

- * 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 - * fails due to an invalid algorithm, key, or - * mismatched signature - */ - void verify(String 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 - */ - Map getHeader(String token); - - /** - * Retrieves the payload claims from the provided JWT, excluding registered claims. - *

- * Decodes the Base64-encoded payload, deserialises it into a map, and removes any registered - * claims as defined in {@link RegisteredClaims}. - * - * @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 due to invalid JSON format - */ - Map getPayload(String token); - - /** - * 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 - * @throws IllegalArgumentException if the token does not consist of exactly three parts - * separated by dots - */ - default RawTokenComponent splitToken(String token) { - var tokenTuple = token.split("\\."); - - if (tokenTuple.length != 3) { - throw new IllegalArgumentException( - "The provided JWT is invalid: it must consist of exactly three parts separated by dots."); - } - - return new RawTokenComponent(tokenTuple[0], tokenTuple[1], tokenTuple[2]); - } - -} 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 deleted file mode 100644 index 6a37d57..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/Algorithm.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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.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"), - HS384(1, 384, "HmacSHA384"), - HS512(1, 512, "HmacSHA512"), - RS256(2, 256, "SHA256withRSA"), - RS384(2, 384, "SHA384withRSA"), - RS512(2, 512, "SHA512withRSA"), - ES256(3, 256, "SHA256withECDSA"), - ES384(3, 384, "SHA384withECDSA"), - 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 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; - this.shaLength = shaLength; - this.algorithm = algorithm; - } - - /** - * Determines whether this algorithm is HMAC-based. - * - * @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 {@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 {@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 the SHA length (256, 384, or 512) - */ - public int getShaLength() { - return shaLength; - } - - /** - * Retrieves the type flag of this algorithm. - * - * @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 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 deleted file mode 100644 index cb681f7..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/HeaderClaims.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.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 { - - /** - * Private constructor to prevent instantiation of this utility class. - */ - private HeaderClaims() { - } - - /** - * The algorithm used to sign a JWT. - */ - public static final String ALGORITHM = "alg"; - - /** - * The content type of the JWT. - */ - public static final String CONTENT_TYPE = "cty"; - - /** - * The media type of the JWT. - */ - public static final String TYPE = "typ"; - - /** - * The key ID of a JWT used to specify the key for signature validation. - */ - public static final String KEY_ID = "kid"; - -} 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 deleted file mode 100644 index 6cf27a5..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/constant/RegisteredClaims.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.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 { - - /** - * Private constructor to prevent instantiation of this utility class. - */ - private RegisteredClaims() { - } - - /** - * The "iss" (issuer) claim identifies the principal that issued the JWT. - * Refer RFC 7529 Section 4.1.1 - */ - public static final String ISSUER = "iss"; - - /** - * The "sub" (subject) claim identifies the principal that is the subject of the JWT. - * Refer RFC 7529 Section 4.1.2 - */ - public static final String SUBJECT = "sub"; - - /** - * The "aud" (audience) claim identifies the recipients that the JWT is intended for. - * Refer RFC 7529 Section 4.1.3 - */ - public static final String AUDIENCE = "aud"; - - /** - * The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be - * accepted for processing. - * Refer RFC 7529 Section 4.1.4 - */ - public static final String EXPIRES_AT = "exp"; - - /** - * The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. - * Refer RFC 7529 Section 4.1.5 - */ - public static final String NOT_BEFORE = "nbf"; - - /** - * The "iat" (issued at) claim identifies the time at which the JWT was issued. - * Refer RFC 7529 Section 4.1.6 - */ - public static final String ISSUED_AT = "iat"; - - /** - * The "jti" (JWT ID) claim provides a unique identifier for the JWT. - * Refer RFC 7529 Section 4.1.7 - */ - 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 VALUES = List.of(ISSUER, SUBJECT, AUDIENCE, EXPIRES_AT, NOT_BEFORE, ISSUED_AT, TOKEN_ID); - -} diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/data/RawTokenComponent.java b/simple-jwt/src/main/java/com/onixbyte/jwt/data/RawTokenComponent.java deleted file mode 100644 index 8125230..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/data/RawTokenComponent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.data; - -/** - * A record representing the raw components of a JSON Web Token (JWT). - *

- * 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 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( - String header, - String payload, - String signature -) { -} 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 deleted file mode 100644 index 28c5ff2..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/holder/ObjectMapperHolder.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.holder; - -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -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; - - private final ObjectMapper objectMapper; - - /** - * Private constructor to prevent instantiation of this utility class. - */ - private ObjectMapperHolder() { - this.objectMapper = JsonMapper.builder() - .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) - .build(); - } - - /** - * 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 singleton {@link ObjectMapperHolder} instance - */ - public static ObjectMapperHolder getInstance() { - if (Objects.isNull(HOLDER)) { - synchronized (ObjectMapperHolder.class) { - if (Objects.isNull(HOLDER)) { - HOLDER = new ObjectMapperHolder(); - } - } - } - return HOLDER; - } - - /** - * Retrieves the configured {@link ObjectMapper} instance. - * - * @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/HmacTokenCreator.java b/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenCreator.java deleted file mode 100644 index f31e193..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenCreator.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.onixbyte.jwt.TokenCreator; -import com.onixbyte.jwt.TokenPayload; -import com.onixbyte.jwt.constant.Algorithm; -import com.onixbyte.jwt.constant.HeaderClaims; -import com.onixbyte.jwt.holder.ObjectMapperHolder; -import com.onixbyte.jwt.util.CryptoUtil; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.HashMap; - -/** - * Implementation of {@link TokenCreator} that generates HMAC-signed JSON Web Tokens (JWTs). - *

- * This class uses a specified HMAC algorithm to create signed tokens, incorporating a header, - * payload, and signature. It ensures the secret key meets the minimum length requirement for - * the chosen algorithm and handles JSON serialisation of the token components. - * - * @author zihluwang - */ -public class HmacTokenCreator implements TokenCreator { - - private final Algorithm algorithm; - private final String issuer; - private final byte[] secret; - - private final ObjectMapper objectMapper; - - /** - * Constructs an HMAC token creator with the specified algorithm, issuer, and secret key. - *

- * Validates that the secret key length meets the minimum requirement for the chosen algorithm. - * - * @param algorithm the HMAC algorithm to use for signing (e.g., HS256, HS384, HS512) - * @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 generate the HMAC signature - * @throws IllegalArgumentException if the secret key is shorter than the minimum required - * length for the specified algorithm - */ - public HmacTokenCreator(Algorithm algorithm, String issuer, String secret) { - var _minSecretLength = algorithm.getShaLength() >> 3; - var secretBytesLength = secret.getBytes(StandardCharsets.UTF_8).length; - if (secretBytesLength < _minSecretLength) { - throw new IllegalArgumentException("Secret key too short for HS%d: minimum %d bytes required, got %d." - .formatted(algorithm.getShaLength(), _minSecretLength, secretBytesLength) - ); - } - - this.algorithm = algorithm; - this.issuer = issuer; - this.secret = secret.getBytes(StandardCharsets.UTF_8); - this.objectMapper = ObjectMapperHolder.getInstance().getObjectMapper(); - } - - /** - * Creates and signs a JWT using the HMAC algorithm. - *

- * Generates a token by encoding the header and payload as Base64 URL-safe strings, - * creating an HMAC signature, and concatenating them with dots. If the payload does not - * include an issuer, the configured issuer is added. - * - * @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 - * @throws RuntimeException if an unexpected error occurs during JSON processing - */ - @Override - public String sign(TokenPayload payload) { - var header = new HashMap(); - - header.put(HeaderClaims.ALGORITHM, algorithm.name()); - if (!header.containsKey(HeaderClaims.TYPE)) { - header.put(HeaderClaims.TYPE, "JWT"); - } - - if (!payload.hasIssuer()) { - payload.withIssuer(issuer); - } - - try { - var encodedHeader = Base64.getUrlEncoder().withoutPadding() - .encodeToString(objectMapper.writeValueAsBytes(header)); - var encodedPayload = Base64.getUrlEncoder().withoutPadding() - .encodeToString(objectMapper.writeValueAsBytes(payload.getPayload())); - - var signatureBytes = CryptoUtil.createSignatureFor(algorithm, - secret, - encodedHeader.getBytes(StandardCharsets.UTF_8), - encodedPayload.getBytes(StandardCharsets.UTF_8)); - var signature = Base64.getUrlEncoder() - .withoutPadding() - .encodeToString((signatureBytes)); - - return "%s.%s.%s".formatted(encodedHeader, encodedPayload, signature); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException("Failed to serialise token header or payload to JSON.", e); - } - } -} 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 deleted file mode 100644 index b1925ed..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenManager.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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; -import com.onixbyte.devkit.utils.ObjectMapAdapter; -import com.onixbyte.jwt.TokenCreator; -import com.onixbyte.jwt.TokenManager; -import com.onixbyte.jwt.TokenPayload; -import com.onixbyte.jwt.TokenResolver; -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 the type of object to which the token payload will be converted - * @author zihluwang - */ -public class HmacTokenManager implements TokenManager { - - private final TokenCreator tokenCreator; - private final TokenResolver tokenResolver; - private final ObjectMapAdapter adapter; - - /** - * Constructs an HMAC token manager with the specified algorithm, issuer, secret, and adapter. - *

- * 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 adapter) { - this.tokenCreator = new HmacTokenCreator(algorithm, issuer, secret); - this.tokenResolver = new HmacTokenResolver(algorithm, secret); - this.adapter = adapter; - } - - /** - * Extracts the payload from a JWT and converts it to an object of type {@code T}. - *

- * 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 getHeader(String token) { - return tokenResolver.getHeader(token); - } - - /** - * Retrieves the payload claims from a JWT. - *

- * 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 getPayload(String token) { - return tokenResolver.getPayload(token); - } -} diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenResolver.java b/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenResolver.java deleted file mode 100644 index 4c4c54d..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/impl/HmacTokenResolver.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.onixbyte.jwt.TokenResolver; -import com.onixbyte.jwt.constant.Algorithm; -import com.onixbyte.jwt.constant.RegisteredClaims; -import com.onixbyte.jwt.holder.ObjectMapperHolder; -import com.onixbyte.jwt.util.CryptoUtil; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.Map; - -/** - * Implementation of {@link TokenResolver} that resolves and verifies HMAC-signed JSON Web - * Tokens (JWTs). - *

- * This class splits a JWT into its components, verifies its signature using an HMAC algorithm, and - * deserialises the header and payload into usable data structures. It ensures the secret key meets - * the minimum length requirement for the specified algorithm. - * - * @author zihluwang - */ -public class HmacTokenResolver implements TokenResolver { - - private final Algorithm algorithm; - private final byte[] secret; - - private final ObjectMapper objectMapper; - - /** - * Constructs an HMAC token resolver with the specified algorithm and secret key. - *

- * Validates that the secret key length meets the minimum requirement for the chosen algorithm. - * - * @param algorithm the HMAC algorithm used for signature verification (e.g., HS256, - * HS384, HS512) - * @param secret the secret key as a string, used to verify the HMAC signature - * @throws IllegalArgumentException if the secret key is shorter than the minimum required - * length for the specified algorithm - */ - public HmacTokenResolver(Algorithm algorithm, String secret) { - var _minSecretLength = algorithm.getShaLength() >> 3; - var secretBytesLength = secret.getBytes(StandardCharsets.UTF_8).length; - if (secretBytesLength < _minSecretLength) { - throw new IllegalArgumentException("Secret key too short for HS%d: minimum %d bytes required, got %d" - .formatted(algorithm.getShaLength(), _minSecretLength, secretBytesLength) - ); - } - - this.algorithm = algorithm; - this.secret = secret.getBytes(StandardCharsets.UTF_8); - this.objectMapper = ObjectMapperHolder.getInstance().getObjectMapper(); - } - - /** - * Verifies the HMAC 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. - * - * @param token the JWT string to verify - * @throws IllegalArgumentException if the token is malformed or the signature verification - * fails due to an invalid algorithm, key, or - * mismatched signature - */ - @Override - public void verify(String token) { - var _token = splitToken(token); - - var isValid = CryptoUtil.verifySignatureFor(algorithm, - secret, - _token.header(), - _token.payload(), - _token.signature().getBytes(StandardCharsets.UTF_8) - ); - if (!isValid) throw new IllegalArgumentException( - "JWT signature verification failed: the token may be tampered with or invalid."); - } - - /** - * 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 getHeader(String token) { - var _token = splitToken(token); - - var headerBytes = Base64.getDecoder().decode(_token.header()); - var headerJson = new String(headerBytes); - - try { - return objectMapper.readValue(headerJson, new TypeReference<>() { - }); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException( - "Failed to deserialise JWT header: the header JSON is invalid or malformed.", e - ); - } - } - - /** - * Retrieves the payload claims from the provided JWT, excluding registered claims. - *

- * Decodes the Base64-encoded payload, deserialises it into a map, and removes any registered - * claims as defined in {@link RegisteredClaims}. - * - * @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 due to invalid JSON format - */ - @Override - public Map getPayload(String token) { - var _token = splitToken(token); - - var payloadBytes = Base64.getDecoder().decode(_token.payload()); - var payloadJson = new String(payloadBytes); - - try { - var payloadMap = objectMapper.readValue(payloadJson, new TypeReference>() { - }); - - payloadMap.keySet().removeIf(RegisteredClaims.VALUES::contains); - return payloadMap; - } catch (JsonProcessingException e) { - throw new IllegalArgumentException( - "Failed to deserialise JWT payload: the payload JSON is invalid or malformed.", e - ); - } - } -} diff --git a/simple-jwt/src/main/java/com/onixbyte/jwt/util/CryptoUtil.java b/simple-jwt/src/main/java/com/onixbyte/jwt/util/CryptoUtil.java deleted file mode 100644 index 33a067b..0000000 --- a/simple-jwt/src/main/java/com/onixbyte/jwt/util/CryptoUtil.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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.util; - -import com.onixbyte.jwt.constant.Algorithm; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -/** - * Utility class for cryptographic operations related to JWT processing. - *

- * Provides methods for creating and verifying signatures using specified algorithms, primarily for - * JSON Web Token (JWT) authentication purposes. - * - * @author zihluwang - */ -public final class CryptoUtil { - - /** - * Private constructor to prevent instantiation of this utility class. - */ - private CryptoUtil() { - } - - private static final byte JWT_PART_SEPARATOR = (byte) 46; - - /** - * Creates a signature for the given header and payload using the specified algorithm - * and secret. - * - * @param algorithm the cryptographic algorithm to use (e.g., HMAC-SHA256) - * @param secret the secret key bytes used for signing - * @param header the header bytes to include in the signature - * @param payload the payload bytes to include in the signature - * @return the generated signature bytes - * @throws IllegalArgumentException if the algorithm is not supported or the key is invalid - */ - public static byte[] createSignatureFor( - Algorithm algorithm, - byte[] secret, - byte[] header, - byte[] payload) { - try { - final var mac = Mac.getInstance(algorithm.getAlgorithm()); - mac.init(new SecretKeySpec(secret, algorithm.getAlgorithm())); - mac.update(header); - mac.update(JWT_PART_SEPARATOR); - return mac.doFinal(payload); - } catch (InvalidKeyException e) { - throw new IllegalArgumentException("The provided secret key is invalid for the algorithm '%s'." - .formatted(algorithm.getAlgorithm()), e); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException("The specified algorithm '%s' is not supported." - .formatted(algorithm.getAlgorithm()), e); - } - } - - /** - * Verifies the signature for the given header and payload using the specified algorithm - * and secret. - *

- * This method converts the header and payload strings to UTF-8 bytes before verification. - * - * @param algorithm the cryptographic algorithm used for signing - * @param secretBytes the secret key bytes used for signing - * @param header the header string to verify - * @param payload the payload string to verify - * @param signatureBytes the signature bytes to check against - * @return {@code true} if the signature is valid, {@code false} otherwise - * @throws IllegalArgumentException if the algorithm is not supported or the key is invalid - */ - public static boolean verifySignatureFor( - Algorithm algorithm, - byte[] secretBytes, - String header, - String payload, - byte[] signatureBytes) { - return verifySignatureFor( - algorithm, - secretBytes, - header.getBytes(StandardCharsets.UTF_8), - payload.getBytes(StandardCharsets.UTF_8), - signatureBytes); - } - - /** - * Verifies the signature for the given header and payload bytes using the specified algorithm - * and secret. - * - * @param algorithm the cryptographic algorithm used for signing - * @param secretBytes the secret key bytes used for signing - * @param headerBytes the header bytes to verify - * @param payloadBytes the payload bytes to verify - * @param signatureBytes the signature bytes to check against - * @return {@code true} if the signature matches, {@code false} otherwise - * @throws IllegalArgumentException if the algorithm is not supported or the key is invalid - */ - public static boolean verifySignatureFor( - Algorithm algorithm, - byte[] secretBytes, - byte[] headerBytes, - byte[] payloadBytes, - byte[] signatureBytes) { - return MessageDigest.isEqual( - createSignatureFor( - algorithm, - secretBytes, - headerBytes, - payloadBytes), - signatureBytes); - } - -} diff --git a/simple-jwt/src/main/resources/logback.xml b/simple-jwt/src/main/resources/logback.xml deleted file mode 100644 index fd31eac..0000000 --- a/simple-jwt/src/main/resources/logback.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - ${COLOURFUL_OUTPUT} - - - - - - \ No newline at end of file