diff --git a/CHANGELOG.md b/CHANGELOG.md index 0269981..729e5da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,62 @@ The 0.x line is treated as a single pre-stable development series — see 1.0.0 stabilisation cut; for 0.x history consult `git log` against the relevant tags. +## [2.1.0] — 2026-06-23 + +A backward-compatible minor release. The headline is **crypto-agility and +post-quantum readiness for passkey signature algorithms** (ADR 0019): the +two divergent hardcoded COSE algorithm lists are replaced by a single +operator-configurable source of truth, and there is now a way to see which +algorithm each stored credential uses. No public API is removed and the HTTP +`/auth/**` wire contract is unchanged, so hosts upgrade by bumping the version. + +To be precise about scope: **no post-quantum signature algorithm is added** — +PQC for passkeys is gated end-to-end by authenticator hardware, CTAP2/FIDO2, +the WebAuthn/COSE registry, and WebAuthn4J's verifier, none of which yet +standardize one. The goal here is crypto-agility (clean removal of the +migration obstacles) and honest documentation, not new algorithms. + +### Added + +- **Crypto-agility for passkey signature algorithms** (ADR 0019). A new + framework-neutral `CoseAlgorithm` enum, and `CeremonyConfig` now carries two + ordered lists: `offeredAlgorithms` (advertised in registration create-options) + and `acceptedAlgorithms` (enforced on the WebAuthn4J verify path). + `acceptedAlgorithms` is authoritative and `offeredAlgorithms` must be a subset. + Both the create-options ceremony and the verify path derive their lists from + this single config, replacing the two previously-divergent hardcoded lists, so + operators can narrow either without code changes. A new 5-arg `CeremonyConfig` + convenience constructor applies backward-compatible defaults — accepted = + `ES256, EdDSA, RS256, ES384, RS384` (the union of everything previously + accepted, so no already-registered credential can fail verification); offered = + `ES256, EdDSA, RS256` (the historical create-options subset) — so every + existing call site compiles and behaves identically. +- **Per-credential algorithm visibility.** + `CredentialAlgorithms.coseAlgorithm(record)` decodes the COSE algorithm already + embedded in a stored public key (**no schema change**), and + `AdminService.listCredentialsByAlgorithm(actor, target, coseAlgorithm)` reports + which stored credentials use a given algorithm — the read side a future + "re-enroll off algorithm X" campaign drives off. +- **Offered/accepted algorithm configuration wired through every adapter.** The + Spring Boot starter, Dropwizard, and Micronaut adapters (and the matching demo + `application.yml` files) expose the two algorithm lists as host config, so the + create-options and verify lists can be tuned without touching code. + +### Changed + +- **Honest JWT crypto framing** (ADR 0019, documentation only — no behavior + change). The HS256-vs-ES256 choice is re-documented as a trust-topology + decision rather than a dev-vs-prod one: HMAC-SHA256 is *not* broken by Shor and + retains ~128-bit security under Grover with the enforced ≥ 256-bit key, making + it the quantum-conservative default for a single-issuer/single-verifier + deployment; ES256 JWTs exist for untrusted third-party verification and carry + Shor exposure bounded by the short token TTL. See `docs/threat-model.md` and + `docs/operator-guide.md`. +- **Build / CI.** The Playwright end-to-end suites under `examples/` are now + wired into each demo's `check` via an opt-in `e2eTest` task (gated on + `PK_RUN_E2E=1` / `-PrunE2e`, so the default gate stays fast and Chrome-free), + and CI runs all three demos in a dedicated `e2e` matrix job. + ## [2.0.0] — 2026-06-13 The first 2.x release. The bulk of the work is correctness, hardening, and @@ -352,7 +408,9 @@ Security-review follow-ups (hardening; no known exploit in the items below). First stable release. Captures the surface produced by the 0.x development series; see `git log` for the full history. -[Unreleased]: https://github.com/codeheadsystems/pk-auth/compare/v1.3.1...HEAD +[Unreleased]: https://github.com/codeheadsystems/pk-auth/compare/v2.1.0...HEAD +[2.1.0]: https://github.com/codeheadsystems/pk-auth/compare/v2.0.0...v2.1.0 +[2.0.0]: https://github.com/codeheadsystems/pk-auth/compare/v1.3.1...v2.0.0 [1.3.1]: https://github.com/codeheadsystems/pk-auth/compare/v1.3.0...v1.3.1 [1.3.0]: https://github.com/codeheadsystems/pk-auth/compare/v1.2.0...v1.3.0 [1.2.0]: https://github.com/codeheadsystems/pk-auth/compare/v1.1.0...v1.2.0 diff --git a/CLAUDE.md b/CLAUDE.md index f41ce6a..b00cc4c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this is -pk-auth is a **passkeys-first authentication library set for the JVM**, published to Maven Central under `com.codeheadsystems`. All modules share one version (`gradle.properties` → currently `2.0.1-SNAPSHOT`). It is *not* an identity provider: it owns passkeys/credentials, never users — the host maps users via the `UserLookup` SPI. +pk-auth is a **passkeys-first authentication library set for the JVM**, published to Maven Central under `com.codeheadsystems`. All modules share one version (`gradle.properties` → currently `2.1.0-SNAPSHOT`). It is *not* an identity provider: it owns passkeys/credentials, never users — the host maps users via the `UserLookup` SPI. The canonical architecture reference is [`DESIGN.md`](./DESIGN.md); per-decision rationale lives in [`docs/adr/`](./docs/adr/) (Nygard format, numbered). Read the relevant ADR before changing cross-module behavior. diff --git a/README.md b/README.md index 45b0eef..4d168fc 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ tests and Playwright end-to-end suites: ## Status 1.0.0 cut the stable baseline; the current development line is -**2.0.1-SNAPSHOT** (see `gradle.properties` for the authoritative +**2.1.0-SNAPSHOT** (see `gradle.properties` for the authoritative version). The 1.1 line added per-audience JWT TTLs, the `AccessTokenStore` (stateful access tokens), the `UserDeletionService` fan-out, and the `pk-auth-refresh-tokens` diff --git a/RELEASE.md b/RELEASE.md index d73fdff..350271f 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -194,7 +194,7 @@ git tag, because the release is already tagged (`vX.Y.Z`) for Maven Central. Pin the SDK version to that same number so the two stay in lockstep. > The committed `version` in `package.json` is the in-development line -> (e.g. `2.0.0-SNAPSHOT`), mirroring `gradle.properties`. `SNAPSHOT` is a valid +> (e.g. `2.1.0-SNAPSHOT`), mirroring `gradle.properties`. `SNAPSHOT` is a valid > semver prerelease identifier, so it will not be published by accident — step 1 > always pins it to the concrete release version first. After publishing, you may > leave `package.json` at the published version or restore the `-SNAPSHOT` line on diff --git a/SECURITY.md b/SECURITY.md index bca0f46..55b8e18 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -36,7 +36,7 @@ Use one of these private channels instead: Please include, as far as you can: -- The affected module(s) and version (e.g. `pk-auth-core 2.0.0`). +- The affected module(s) and version (e.g. `pk-auth-core 2.1.0`). - A description of the issue and its impact (what an attacker can do). - Steps to reproduce — a minimal proof of concept, failing test, or request sequence is ideal. diff --git a/clients/passkeys-browser/package.json b/clients/passkeys-browser/package.json index df2ccfb..0a201be 100644 --- a/clients/passkeys-browser/package.json +++ b/clients/passkeys-browser/package.json @@ -1,6 +1,6 @@ { "name": "@pk-auth/passkeys-browser", - "version": "2.0.1-SNAPSHOT", + "version": "2.1.0", "description": "pk-auth browser SDK: WebAuthn ceremonies + admin operations against the pk-auth wire contract.", "license": "MIT", "author": "Ned Wolpert", diff --git a/docs/stability.md b/docs/stability.md index 11f4b45..447a07f 100644 --- a/docs/stability.md +++ b/docs/stability.md @@ -3,7 +3,7 @@ ## Current status: 2.x pk-auth is **post-1.0** as of the `v1.0.0` tag. The current development line is -**2.0.1-SNAPSHOT** (`gradle.properties` is authoritative); the 1.1, 1.2, 1.3, and +**2.1.0-SNAPSHOT** (`gradle.properties` is authoritative); the 1.1, 1.2, 1.3, and 2.0 releases have shipped. See [`CHANGELOG.md`](../CHANGELOG.md) for the per-release delta. diff --git a/gradle.properties b/gradle.properties index 6e3d0aa..9df120d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,4 +21,4 @@ org.gradle.jvmargs=-Xmx2g \ # Project identity group=com.codeheadsystems -version=2.0.1-SNAPSHOT +version=2.1.0-SNAPSHOT diff --git a/pk-auth-admin-api/src/main/java/com/codeheadsystems/pkauth/admin/AdminService.java b/pk-auth-admin-api/src/main/java/com/codeheadsystems/pkauth/admin/AdminService.java index 4d918d2..5d427b6 100644 --- a/pk-auth-admin-api/src/main/java/com/codeheadsystems/pkauth/admin/AdminService.java +++ b/pk-auth-admin-api/src/main/java/com/codeheadsystems/pkauth/admin/AdminService.java @@ -27,7 +27,7 @@ public interface AdminService { * Shor-vulnerable algorithm and prompt those users to re-enroll. The algorithm is read from the * already-stored COSE key — no schema change. See ADR 0019. * - * @since 2.0.1 + * @since 2.1.0 */ AdminResult> listCredentialsByAlgorithm( UserHandle actor, UserHandle target, int coseAlgorithm); diff --git a/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/config/CeremonyConfig.java b/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/config/CeremonyConfig.java index 1528893..313d63f 100644 --- a/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/config/CeremonyConfig.java +++ b/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/config/CeremonyConfig.java @@ -41,7 +41,7 @@ public record CeremonyConfig( * PublicKeyCredentialParameters}). The historical offered set: ES256, EdDSA, RS256. Deliberately * a subset of {@link #DEFAULT_ACCEPTED_ALGORITHMS}. * - * @since 2.0.1 + * @since 2.1.0 */ public static final List DEFAULT_OFFERED_ALGORITHMS = List.of(CoseAlgorithm.ES256, CoseAlgorithm.EdDSA, CoseAlgorithm.RS256); @@ -52,7 +52,7 @@ public record CeremonyConfig( * the union of everything previously accepted so no already-registered credential can * fail verification. * - * @since 2.0.1 + * @since 2.1.0 */ public static final List DEFAULT_ACCEPTED_ALGORITHMS = List.of( @@ -173,7 +173,7 @@ public static CeremonyConfig from( * @param offeredAlgorithms algorithms advertised in create-options, or null for the default. * @param acceptedAlgorithms algorithms accepted on verify, or null for the default. * @return the resolved ceremony config. - * @since 2.0.1 + * @since 2.1.0 */ public static CeremonyConfig from( @Nullable Duration challengeTtl, diff --git a/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/config/CoseAlgorithm.java b/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/config/CoseAlgorithm.java index 198d3ab..710e721 100644 --- a/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/config/CoseAlgorithm.java +++ b/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/config/CoseAlgorithm.java @@ -18,7 +18,7 @@ * touching the ceremony or verification code. See {@code docs/threat-model.md} (Post-quantum * readiness) and ADR 0019. * - * @since 2.0.1 + * @since 2.1.0 */ public enum CoseAlgorithm { @@ -47,7 +47,7 @@ public enum CoseAlgorithm { * The IANA COSE algorithm identifier for this algorithm (e.g. {@code -7} for {@link #ES256}). * * @return the signed COSE algorithm identifier. - * @since 2.0.1 + * @since 2.1.0 */ public int coseValue() { return coseValue; diff --git a/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/credential/CredentialAlgorithms.java b/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/credential/CredentialAlgorithms.java index 07c7168..074b030 100644 --- a/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/credential/CredentialAlgorithms.java +++ b/pk-auth-core/src/main/java/com/codeheadsystems/pkauth/credential/CredentialAlgorithms.java @@ -19,7 +19,7 @@ * against the configured algorithm vocabulary directly. See {@code docs/threat-model.md} * (Post-quantum readiness) and ADR 0019. * - * @since 2.0.1 + * @since 2.1.0 */ public final class CredentialAlgorithms { @@ -35,7 +35,7 @@ private CredentialAlgorithms() {} * @param record the stored credential. * @return the IANA COSE algorithm identifier (e.g. {@code -7} for ES256). * @throws IllegalStateException if the stored COSE key cannot be decoded or carries no algorithm. - * @since 2.0.1 + * @since 2.1.0 */ public static int coseAlgorithm(CredentialRecord record) { COSEKey coseKey; @@ -60,7 +60,7 @@ public static int coseAlgorithm(CredentialRecord record) { * @param coseAlgorithm the IANA COSE algorithm identifier to test for (e.g. {@code -7} for * ES256). * @return {@code true} if the credential uses that algorithm. - * @since 2.0.1 + * @since 2.1.0 */ public static boolean usesAlgorithm(CredentialRecord record, int coseAlgorithm) { return coseAlgorithm(record) == coseAlgorithm; diff --git a/site/index.html b/site/index.html index 1e7de66..56f6135 100644 --- a/site/index.html +++ b/site/index.html @@ -1098,7 +1098,7 @@
- v2.0.0 + v2.1.0 JDK 21 MIT WebAuthn4J @@ -1146,9 +1146,9 @@

// One adapter, one persistence module, done.
 dependencies {
-  implementation("com.codeheadsystems:pk-auth-spring-boot-starter:2.0.0")
-  implementation("com.codeheadsystems:pk-auth-persistence-jdbi:2.0.0")
-  implementation("com.codeheadsystems:pk-auth-admin-api:2.0.0")
+  implementation("com.codeheadsystems:pk-auth-spring-boot-starter:2.1.0")
+  implementation("com.codeheadsystems:pk-auth-persistence-jdbi:2.1.0")
+  implementation("com.codeheadsystems:pk-auth-admin-api:2.1.0")
 }
@@ -1332,9 +1332,9 @@

Add the adapter, persistence, and admin API.

dependencies {
-  implementation("com.codeheadsystems:pk-auth-spring-boot-starter:2.0.0")
-  implementation("com.codeheadsystems:pk-auth-persistence-jdbi:2.0.0")
-  implementation("com.codeheadsystems:pk-auth-admin-api:2.0.0")
+  implementation("com.codeheadsystems:pk-auth-spring-boot-starter:2.1.0")
+  implementation("com.codeheadsystems:pk-auth-persistence-jdbi:2.1.0")
+  implementation("com.codeheadsystems:pk-auth-admin-api:2.1.0")
 }
@@ -1343,7 +1343,7 @@

Add the adapter, persistence, and admin API.

<dependency>
   <groupId>com.codeheadsystems</groupId>
   <artifactId>pk-auth-spring-boot-starter</artifactId>
-  <version>2.0.0</version>
+  <version>2.1.0</version>
 </dependency>
 <!-- + pk-auth-persistence-jdbi, pk-auth-admin-api -->
@@ -1754,7 +1754,7 @@

pk-auth

A passkey credential layer for the JVM. MIT-licensed. Three adapters, one wire contract, your user table.

-

v2.0.0 · JDK 21 · WebAuthn4J

+

v2.1.0 · JDK 21 · WebAuthn4J