RepoPilotOpen in app →

jwtk/jjwt

Java JWT: JSON Web Token for Java and Android

Healthy

Healthy across the board

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1w ago
  • 16 active contributors
  • Apache-2.0 licensed
Show all 6 evidence items →
  • CI configured
  • Tests present
  • Concentrated ownership — top contributor handles 79% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/jwtk/jjwt)](https://repopilot.app/r/jwtk/jjwt)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/jwtk/jjwt on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: jwtk/jjwt

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/jwtk/jjwt shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 1w ago
  • 16 active contributors
  • Apache-2.0 licensed
  • CI configured
  • Tests present
  • ⚠ Concentrated ownership — top contributor handles 79% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live jwtk/jjwt repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/jwtk/jjwt.

What it runs against: a local clone of jwtk/jjwt — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in jwtk/jjwt | Confirms the artifact applies here, not a fork | | 2 | License is still Apache-2.0 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 37 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>jwtk/jjwt</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of jwtk/jjwt. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/jwtk/jjwt.git
#   cd jjwt
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of jwtk/jjwt and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "jwtk/jjwt(\\.git)?\\b" \\
  && ok "origin remote is jwtk/jjwt" \\
  || miss "origin remote is not jwtk/jjwt (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
  && ok "license is Apache-2.0" \\
  || miss "license drift — was Apache-2.0 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "api/src/main/java/io/jsonwebtoken/Jwts.java" \\
  && ok "api/src/main/java/io/jsonwebtoken/Jwts.java" \\
  || miss "missing critical file: api/src/main/java/io/jsonwebtoken/Jwts.java"
test -f "api/src/main/java/io/jsonwebtoken/JwtBuilder.java" \\
  && ok "api/src/main/java/io/jsonwebtoken/JwtBuilder.java" \\
  || miss "missing critical file: api/src/main/java/io/jsonwebtoken/JwtBuilder.java"
test -f "api/src/main/java/io/jsonwebtoken/JwtParser.java" \\
  && ok "api/src/main/java/io/jsonwebtoken/JwtParser.java" \\
  || miss "missing critical file: api/src/main/java/io/jsonwebtoken/JwtParser.java"
test -f "api/src/main/java/io/jsonwebtoken/Claims.java" \\
  && ok "api/src/main/java/io/jsonwebtoken/Claims.java" \\
  || miss "missing critical file: api/src/main/java/io/jsonwebtoken/Claims.java"
test -f "api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java" \\
  && ok "api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java" \\
  || miss "missing critical file: api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 37 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~7d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/jwtk/jjwt"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

JJWT is a pure Java implementation for creating and verifying JSON Web Tokens (JWTs), JSON Web Signatures (JWS), and JSON Web Encryption (JWE) fully compliant with JOSE RFC specifications (7515-7519, 7638, 8037, 9278). It provides fluent Java APIs for signing, encrypting, and parsing JWTs on JVM and Android platforms without external cryptographic library dependencies. Multi-module Maven project with api/ containing core abstractions (Jwts, JwtBuilder, JwtParser interfaces in io/jsonwebtoken/), separate implementation modules (inferred from pom.xml structure), and Groovy-based test suites. The api/ module defines fluent builder patterns: JwtBuilder for creation, JwtParserBuilder for parsing, with visitor patterns (JwtHandler, JwtVisitor) for token traversal.

👥Who it's for

Java and Android developers building authentication and authorization systems who need a standards-compliant JWT library with minimal boilerplate; backend engineers implementing OAuth2/OIDC flows; security-conscious teams requiring automatic best-practice enforcement and comprehensive algorithm support.

🌱Maturity & risk

Production-ready. The project has comprehensive CI/CD via GitHub Actions (.github/workflows/ci.yml), active maintenance at version 0.14.0-SNAPSHOT with coverage tracking via Coveralls, security scanning via Snyk, and a detailed CHANGELOG.md documenting evolution from early versions. Community-maintained with multiple contributors and clear documentation in README.adoc.

Low risk for core JWT use cases. The codebase is well-established (1.7M+ Java LOC) with no single-maintainer dependency noted. Primary risks: Java version constraints (some algorithms require Java 11+ or BouncyCastle provider per README macros), and the large API surface area in api/src/main/java/io/jsonwebtoken/ requires careful backward-compatibility management (evidenced by japicmp-maven-plugin usage).

Active areas of work

Currently in 0.14.0-SNAPSHOT development. Recent work appears focused on JWT/JWE feature completeness (JWE classes present alongside JWS), algorithm support expansion (RFC 8037 Edwards curves, RFC 9278 JWK Thumbprint URI), and likely refinements to the fluent API surface (ClaimsBuilder, HeaderMutator classes added). CI is actively running on commits to master branch.

🚀Get running

git clone https://github.com/jwtk/jjwt.git
cd jjwt
./mvnw clean install

The repo includes Maven wrapper (.mvn/wrapper/), so no Maven installation required. Tests run as part of the standard Maven build lifecycle.

Daily commands: This is a library, not an application. Build with ./mvnw clean install to compile and run tests. To use in a project, depend on io.jsonwebtoken:jjwt-api and appropriate impl modules. See api/pom.xml for module structure. Tests are Groovy-based (inferred from .groovy LOC in file list) and run automatically during Maven build.

🗺️Map of the codebase

  • api/src/main/java/io/jsonwebtoken/Jwts.java — Main public API entry point; all JWT creation and parsing flows start here via static factory methods
  • api/src/main/java/io/jsonwebtoken/JwtBuilder.java — Core abstraction for building and signing JWTs; every token creation workflow uses this interface
  • api/src/main/java/io/jsonwebtoken/JwtParser.java — Core abstraction for parsing and validating JWTs; handles token deserialization and claim verification
  • api/src/main/java/io/jsonwebtoken/Claims.java — Represents JWT claims payload; all claim access patterns depend on this interface
  • api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java — Base contract for authenticated encryption (JWE); critical for encrypted token support
  • api/src/main/java/io/jsonwebtoken/io/Serializer.java — Core serialization contract; pluggable JSON serialization implementation affects all token creation
  • api/src/main/java/io/jsonwebtoken/io/Deserializer.java — Core deserialization contract; pluggable JSON deserialization is required for all token parsing

🛠️How to make changes

Add Support for a Custom Signature Algorithm

  1. Create a new algorithm implementation class that extends the appropriate algorithm base in the impl module (not listed, but would be in impl/src/main/java) (api/src/main/java/io/jsonwebtoken/SignatureAlgorithm.java)
  2. Register the algorithm in the signature algorithm registry so Jwts.builder() can discover it by name (api/src/main/java/io/jsonwebtoken/Jwts.java)
  3. Define the key spec and JCA algorithm name required by your SigningKey instance (api/src/main/java/io/jsonwebtoken/security/AsymmetricJwk.java)

Add Custom Claim Validation on Token Parse

  1. Create a custom exception by extending InvalidClaimException or a specific subclass (api/src/main/java/io/jsonwebtoken/InvalidClaimException.java)
  2. Use JwtParserBuilder to call requireClaim() or requireSubject() etc., or override requireXxx() methods for custom logic (api/src/main/java/io/jsonwebtoken/JwtParserBuilder.java)
  3. Access parsed Claims and throw InvalidClaimException subclass if validation fails (api/src/main/java/io/jsonwebtoken/Claims.java)

Implement Custom JSON Serialization/Deserialization

  1. Create a class implementing Serializer<Map<String,?>> to convert claims/headers to JSON bytes (api/src/main/java/io/jsonwebtoken/io/Serializer.java)
  2. Create a class implementing Deserializer<Map<String,?>> to parse JSON bytes back to claim maps (api/src/main/java/io/jsonwebtoken/io/Deserializer.java)
  3. Pass instances to JwtParserBuilder.deserializer() and JwtBuilder.serializeToJsonWith() to override default JSON provider (api/src/main/java/io/jsonwebtoken/JwtParserBuilder.java)

Implement Key Resolution Strategy for Multi-Tenant Signing

  1. Create a class implementing SigningKeyResolver with resolveSigningKey(JwsHeader, Claims) method (api/src/main/java/io/jsonwebtoken/SigningKeyResolver.java)
  2. Extract kid or other header claims to look up the correct signing key for verification (api/src/main/java/io/jsonwebtoken/JwsHeader.java)
  3. Pass resolver to JwtParserBuilder.setSigningKeyResolver() to enable dynamic key lookup during parse (api/src/main/java/io/jsonwebtoken/JwtParserBuilder.java)

🪤Traps & gotchas

  1. Java version constraints: some algorithms (EdDSA per RFC 8037) require Java 11+ or third-party JCA provider (BouncyCastle) — check README macros (:fn-require-java11-plus:, :fn-require-java15-plus:). 2. Builder pattern requires complete chain before build() — incomplete chains may throw NullPointerException. 3. Clock interface (Clock.java) is injectable for testing but defaults to system time; tests must mock or set custom Clock to avoid flaky expiry assertions. 4. JwtException hierarchy is checked/unchecked mixed; MalformedJwtException is checked, so parsing requires try-catch. 5. Multi-module Maven setup means changes to api/ require rebuilding impl modules (not visible in file list) to propagate.

🏗️Architecture

💡Concepts to learn

  • JSON Web Token (JWT) Structure — JWTs are three Base64url-encoded segments (header.payload.signature); understanding this structure is essential to using JwtBuilder output and JwtParser input correctly
  • [JWS (JSON Web Signature) vs JWE (JSON Web Encryption)](https://tools.ietf.org/html/rfc7515 (JWS) and https://tools.ietf.org/html/rfc7516 (JWE)) — JJWT supports both; JWS signs but doesn't encrypt (Jws.java), JWE encrypts payload (Jwe.java). Choosing the right type depends on whether confidentiality is required
  • Fluent Builder Pattern — Core API design in JwtBuilder and JwtParserBuilder; chainable method calls enable readable code and IDE autocomplete, reducing boilerplate
  • HMAC vs Asymmetric Signing Algorithms — JJWT supports both HS256 (HMAC with shared secret) and RS256/ES256 (RSA/ECDSA with key pairs); choice affects key management, trust model, and JwtParserBuilder.verifyWith() setup
  • Claim Validation and Custom Claims — Claims.java defines standard claims (exp, nbf, iat); JwtParserBuilder enforces expiry, not-before, and custom claim assertions via requireClaim(). Misunderstanding validation scope causes security bugs
  • Key Management and JWK (JSON Web Key) — RFC 7517 (and RFC 7638 thumbprinting, RFC 9278 URI) are referenced in README; JJWT abstracts key handling but understanding JWK format and key rotation is critical for production deployments
  • Compression Codecs for JWT Payload — CompressionCodec.java and CompressionCodecResolver.java allow DEFLATE/GZIP compression of large payloads; understanding when to enable (RFC 7516 mentions this) prevents unexpected serialization overhead
  • auth0/java-jwt — Direct alternative JWT library for Java with simpler API but less RFC coverage; commonly compared in library selection
  • micrometer-metrics/micrometer — Used alongside JJWT in Spring Boot for instrumenting token creation/validation latency and error metrics
  • spring-projects/spring-security — Integrates JJWT for OAuth2/OIDC in Spring applications; core consumer of this library in enterprise Java ecosystem
  • googleapis/google-auth-library-java — Google Cloud authentication uses similar JWT patterns; learning resource for enterprise JWT usage and key management strategies
  • keycloak/keycloak — Reference IAM platform implementation heavily using JWT; demonstrates real-world JJWT usage in production identity systems

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for io/Base64 codec classes

The api/src/main/java/io/jsonwebtoken/io directory contains Base64, Base64Decoder, Base64Encoder, Base64UrlDecoder, and Base64UrlEncoder classes that are critical for JWT encoding/decoding. A thorough test suite covering edge cases (empty input, malformed base64, padding variations, URL-safe encoding differences) would improve reliability and prevent regressions in core functionality.

  • [ ] Examine existing test files in api/src/test/java/io/jsonwebtoken/io/ to identify test gaps
  • [ ] Create comprehensive test classes for Base64Support, Base64Encoder/Decoder, and Base64UrlEncoder/Decoder
  • [ ] Add tests for boundary conditions: empty byte arrays, very large payloads, special characters, padding edge cases
  • [ ] Add tests verifying URL-safe vs standard Base64 encoding differences
  • [ ] Run tests against the japicmp-maven-plugin configuration to ensure no API breaks

Add integration tests for compression codec resolver chain

The api/src/main/java/io/jsonwebtoken/ contains CompressionCodec, CompressionCodecResolver, and CompressionCodecs classes that handle JWT payload compression. Currently, there appear to be no specific integration tests validating the resolver's ability to handle multiple codecs, fallback behavior, and codec selection based on algorithm identifiers.

  • [ ] Review api/src/test/java/io/jsonwebtoken/ for existing compression tests
  • [ ] Create integration test class testing CompressionCodecResolver with multiple codec registrations
  • [ ] Add tests for codec resolution by algorithm ID, missing codec handling, and CompressionException scenarios
  • [ ] Add tests verifying round-trip compression/decompression with JwtBuilder and JwtParser
  • [ ] Test interoperability between different compression algorithms in a single JWT workflow

Add validation tests for all JWT exception hierarchy classes

The api/src/main/java/io/jsonwebtoken/ contains multiple exception classes (ExpiredJwtException, MalformedJwtException, SignatureException, IncorrectClaimException, etc.) that inherit from JwtException. These exceptions lack comprehensive tests validating proper initialization, message formatting, cause chaining, and parsing context preservation (e.g., that ExpiredJwtException properly captures claim data).

  • [ ] Audit api/src/test/java/io/jsonwebtoken/ to identify which exception classes lack dedicated tests
  • [ ] Create test class covering all exception constructors and inheritance chain for ClaimJwtException, InvalidClaimException, IncorrectClaimException, MissingClaimException, PrematureJwtException, and RequiredTypeException
  • [ ] Add tests verifying exception messages include relevant claim names and values
  • [ ] Add tests validating getCause() chaining and proper serialization for exception handling frameworks
  • [ ] Verify exceptions properly extend ClaimJwtException or JwtException as appropriate

🌿Good first issues

  • Add comprehensive Javadoc examples to JwtBuilder.java showing common patterns (signing with HS256, RS256, setting exp/nbf claims, encrypting with JWE). Currently api/src/main/java/io/jsonwebtoken/*.java have minimal method-level docs.
  • Extend CompressionCodecs.java with additional codec examples (gzip, deflate) in Javadoc, or add test coverage in Groovy tests demonstrating round-trip compression/decompression with different CompressionCodec implementations.
  • Create integration test under test suite comparing RFC compliance: parse a well-known JWT from jwt.io or auth0 test vectors using JwtParser and verify all claims match spec. Tests/src currently inferred; add RFC 7519 test vector suite.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 299c14e — Fix imports in docs example (#1028) (quapka)
  • 6f6f027 — Release 0.13.0 (#1016) (lhazlewood)
  • b6f8cb8 — Made constructor public to allow users their own objectMapper instance (#972) (kesrishubham2510)
  • 03f088a — Bumping development version to 0.13.0-SNAPSHOT (#1014) (lhazlewood)
  • 3f2697f — Release 0.12.7 (#1012) (lhazlewood)
  • efed1cf — Updated 0.12.7 change list (lhazlewood)
  • ca27b12 — Resolves #1010 (#1011) (lhazlewood)
  • 55c7b9a — Resolves #771 (#1009) (lhazlewood)
  • 6e9c6a5 — Bump org.bouncycastle:bcpkix-jdk18on from 1.78 to 1.78.1 (#1008) (dependabot[bot])
  • 7ec7dd1 — Enable JwtParser empty nested algorithm collections. (#1007) (lhazlewood)

🔒Security observations

JJWT demonstrates generally good security practices as a cryptographic JWT library with proper separation of concerns and organized package structure. No critical vulnerabilities were identified in the provided analysis scope. However, the incomplete SECURITY.md file and limited documentation of secure reporting channels present moderate concerns. The codebase appears well-structured for security-sensitive operations, but the pre-release status (0.14.0-SNAPSHOT) and restrictive security support policy (0.12.x+ only) should be considered by users. Recommendation: Complete security documentation and ensure robust CI/CD security scanning given the library's sensitive nature (JWT cryptography).

  • Medium · Incomplete Security Policy Documentation — SECURITY.md. The SECURITY.md file is truncated and does not provide complete security reporting instructions. The coordinated disclosure contact information is cut off, which may prevent security researchers from reporting vulnerabilities through proper channels. Fix: Complete the SECURITY.md file with full contact information for security vulnerability reporting (e.g., security@jwtk.io or similar secure reporting mechanism).
  • Low · Pre-release Version in Active Development — pom.xml (version: 0.14.0-SNAPSHOT). The codebase is at version 0.14.0-SNAPSHOT, indicating active development on a pre-release version. Security policies only support 0.12.x and newer, but the version numbering suggests rapid development which may introduce untested code. Fix: Maintain rigorous security testing and code review processes for pre-release versions. Consider implementing automated security scanning in CI/CD pipeline (note: ci.yml exists but content not provided for review).
  • Low · Limited Security Support Scope — SECURITY.md - Supported Versions table. The SECURITY.md indicates only the latest minor and point revisions receive security fixes. Users on older versions have no security support, which could leave systems vulnerable if they don't upgrade. Fix: Consider extending security patch support for at least one additional minor version, or provide clear migration documentation for users to upgrade safely.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · jwtk/jjwt — RepoPilot