Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The recommended entry points are the `Signers` and `Verifiers` factories. They t

JWTs are built with an immutable fluent builder and serialized by a `JWTEncoder`. Decoding is done by a `JWTDecoder`, which takes a `VerifierResolver` — use `VerifierResolver.byKid(Map<String, Verifier>)` for a `kid`-indexed keyring, or `VerifierResolver.of(verifier)` to wrap a single verifier.

For the happy path, `JWT.decode(encodedJWT, verifier)` (and an `(encodedJWT, verifier, validator)` overload) routes through a shared default `JWTDecoder` so you don't have to construct one. The same overloads accept a `VerifierResolver` when you have a keyring. Build your own `JWTDecoder` with `JWTDecoder.builder()` when you need non-default settings — a custom `JSONProcessor`, `clockSkew`, allowed algorithms, `fixedTime`, etc. — and pass it to the `JWT.decode(encodedJWT, decoder, verifier)` overload (or call `decoder.decode(...)` directly).
For the happy path, `JWT.decode(encodedJWT, verifier)` (and an `(encodedJWT, verifier, validator)` overload) routes through a shared default `JWTDecoder` so you don't have to construct one. The same overloads accept a `VerifierResolver` when you have a keyring. Build your own `JWTDecoder` with `JWTDecoder.builder()` when you need non-default settings — a custom `JSONProcessor`, `clockSkew`, allowed algorithms, a custom `Clock`, etc. — and pass it to the `JWT.decode(encodedJWT, decoder, verifier)` overload (or call `decoder.decode(...)` directly).

#### Sign and encode a JWT using HMAC
```java
Expand Down Expand Up @@ -175,7 +175,7 @@ Alternate: `ECVerifier.newVerifier(Paths.get("public_key.pem"))`.

#### Build your own JWTDecoder

The `JWT.decode(...)` static helpers route through a shared default decoder that uses the bundled zero-dependency `LatteJSONProcessor`. Build your own when you need a different `JSONProcessor` or any other non-default setting — `clockSkew`, `expectedAlgorithms`, `expectedType`, `criticalHeaders`, `maxInputBytes`, `fixedTime`, etc. The clock-skew and time-pinning examples below use the same builder.
The `JWT.decode(...)` static helpers route through a shared default decoder that uses the bundled zero-dependency `LatteJSONProcessor`. Build your own when you need a different `JSONProcessor` or any other non-default setting — `clockSkew`, `expectedAlgorithms`, `expectedType`, `criticalHeaders`, `maxInputBytes`, a custom `Clock`, etc. The clock-skew and time-pinning examples below use the same builder.

`JSONProcessor` is a strategy interface — `serialize(Map)` / `deserialize(byte[])` — and **the library does not bundle a Jackson, Gson, or other third-party adapter**. To use one, write a small class that implements `JSONProcessor` and delegates to your JSON library of choice. Implementations must be stateless and thread-safe; the encoder and decoder may call them concurrently.

Expand Down Expand Up @@ -291,7 +291,7 @@ Verifier verifier = Verifiers.forAsymmetric(Algorithm.ES256,
// Pin the decoder to a specific instant. Use ONLY in tests — never in production.
Instant thePast = Instant.parse("2019-01-01T00:00:00Z");
JWTDecoder timeMachine = JWTDecoder.builder()
.fixedTime(thePast)
.clock(Clock.fixed(thePast, ZoneOffset.UTC))
.build();

JWT jwt = timeMachine.decode(encodedJWT, VerifierResolver.of(verifier));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
*/
package org.lattejava.jwt.benchmarks.lattejwtjackson;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;

import org.lattejava.jwt.Algorithm;
import org.lattejava.jwt.JSONProcessor;
Expand Down Expand Up @@ -55,10 +57,10 @@ public void prepare(Fixtures fixtures) throws Exception {

encoder = new JWTEncoder(json);

Instant fixedNow = Instant.ofEpochSecond(1761408000L + 1800L);
es256Decoder = JWTDecoder.builder().fixedTime(fixedNow).jsonProcessor(json).build();
hs256Decoder = JWTDecoder.builder().fixedTime(fixedNow).jsonProcessor(json).build();
rs256Decoder = JWTDecoder.builder().fixedTime(fixedNow).jsonProcessor(json).build();
Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(1761408000L + 1800L), ZoneOffset.UTC);
es256Decoder = JWTDecoder.builder().clock(fixedClock).jsonProcessor(json).build();
hs256Decoder = JWTDecoder.builder().clock(fixedClock).jsonProcessor(json).build();
rs256Decoder = JWTDecoder.builder().clock(fixedClock).jsonProcessor(json).build();
unsafeDecoder = JWTDecoder.builder().jsonProcessor(json).build();

canonicalJWT = JWT.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
*/
package org.lattejava.jwt.benchmarks.lattejwt;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;

import org.lattejava.jwt.Algorithm;
import org.lattejava.jwt.JWT;
Expand Down Expand Up @@ -46,10 +48,10 @@ public void prepare(Fixtures fixtures) throws Exception {

encoder = new JWTEncoder();

Instant fixedNow = Instant.ofEpochSecond(1761408000L + 1800L);
es256Decoder = JWTDecoder.builder().fixedTime(fixedNow).build();
hs256Decoder = JWTDecoder.builder().fixedTime(fixedNow).build();
rs256Decoder = JWTDecoder.builder().fixedTime(fixedNow).build();
Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(1761408000L + 1800L), ZoneOffset.UTC);
es256Decoder = JWTDecoder.builder().clock(fixedClock).build();
hs256Decoder = JWTDecoder.builder().clock(fixedClock).build();
rs256Decoder = JWTDecoder.builder().clock(fixedClock).build();
unsafeDecoder = JWTDecoder.builder().build();

canonicalJWT = JWT.builder()
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/lattejava/jwt/JWT.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public static Builder builder() {
* be observed until the signature has been validated.
*
* <p>Build your own decoder with {@link JWTDecoder#builder()} when you need non-default settings (custom
* {@link JSONProcessor}, {@code clockSkew}, allowed algorithms, {@code fixedTime}, etc.).</p>
* {@link JSONProcessor}, {@code clockSkew}, allowed algorithms, custom {@link java.time.Clock}, etc.).</p>
*
* @param encodedJWT the compact JWS string; must be non-null
* @param resolver the verifier resolver; must be non-null
Expand Down Expand Up @@ -151,7 +151,7 @@ public static JWT decode(String encodedJWT, JWTDecoder decoder, Verifier verifie
* subclass to reject the token.
*
* <p>Build your own decoder with {@link JWTDecoder#builder()} when you need non-default settings (custom
* {@link JSONProcessor}, {@code clockSkew}, allowed algorithms, {@code fixedTime}, etc.).</p>
* {@link JSONProcessor}, {@code clockSkew}, allowed algorithms, custom {@link java.time.Clock}, etc.).</p>
*
* @param encodedJWT the compact JWS string; must be non-null
* @param resolver the verifier resolver; must be non-null
Expand Down
11 changes: 1 addition & 10 deletions src/main/java/org/lattejava/jwt/JWTDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public static Builder builder() {
/**
* Returns the shared default {@link JWTDecoder} used by {@link JWT#decode(String, VerifierResolver)} and its
* overloads. Build your own with {@link #builder()} when you need non-default settings (custom {@link JSONProcessor},
* {@code clockSkew}, allowed algorithms, {@code fixedTime}, etc.).
* {@code clockSkew}, allowed algorithms, custom {@link Clock}, etc.).
*
* @return the shared default instance; never {@code null}
*/
Expand Down Expand Up @@ -550,15 +550,6 @@ public Builder expectedType(String expectedType) {
return this;
}

/**
* Convenience for {@code clock(Clock.fixed(instant, ZoneOffset.UTC))}.
*/
public Builder fixedTime(Instant instant) {
Objects.requireNonNull(instant, "instant");
this.clock = Clock.fixed(instant, ZoneOffset.UTC);
return this;
}

public Builder jsonProcessor(JSONProcessor jsonProcessor) {
this.jsonProcessor = Objects.requireNonNull(jsonProcessor, "jsonProcessor");
this.jsonProcessorExplicit = true;
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/org/lattejava/jwt/JWTTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,9 @@ public void decode_single_verifier_shortcut_with_validator() {

@Test
public void decode_uses_supplied_decoder() {
// Use case: JWT.decode(encodedJWT, decoder, resolver) routes through the supplied decoder. We confirm by
// pinning fixedTime far in the future and asserting the call would fail without our supplied clock skew --
// proving the supplied decoder (not the default) was consulted.
// Use case: JWT.decode(encodedJWT, decoder, resolver) routes through the supplied decoder. We confirm by pinning
// the clock far in the future and asserting the call would fail without our supplied clock skew -- proving the
// supplied decoder (not the default) was consulted.
JWT jwt = JWT.builder()
.subject("supplied-decoder")
.expiresAt(Instant.parse("2026-04-26T12:00:00Z"))
Expand Down
3 changes: 2 additions & 1 deletion src/test/java/org/lattejava/jwt/RFC7515VectorsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.math.*;
import java.security.*;
import java.security.spec.*;
import java.time.*;
import java.util.*;

import org.lattejava.jwt.algorithm.ec.*;
Expand Down Expand Up @@ -97,7 +98,7 @@ private static PublicKey rsaPublic(BigInteger n, BigInteger e) {
*/
private static JWTDecoder vectorClockDecoder() {
return JWTDecoder.builder()
.fixedTime(java.time.Instant.ofEpochSecond(1300819300L))
.clock(Clock.fixed(Instant.ofEpochSecond(1300819300L), ZoneOffset.UTC))
.build();
}

Expand Down
Loading