1
0
Files
spring-auth-demo/docs/output/pk-counter.txt
Ankur Mhatre f6dd692177 Add passkeys project: WebAuthn ceremonies, a software authenticator and the one-time-token fallback
Fourth Maven project in the repository. Registration and authentication run end to
end with no browser and no hardware key: VirtualAuthenticator emits real CBOR
attestation objects and real ES256 assertion signatures, and tools/PasskeyCeremony.java
drives the live HTTP endpoints with them.

Profiles cover userVerification REQUIRED, DIRECT attestation, a disallowed origin and
JDBC persistence. Eleven doc chapters and twelve captured transcripts under docs/passkeys
and docs/output/pk-*.txt, all regenerated by passkeys/scripts/run-all.sh.
2026-08-25 23:00:27 +05:30

65 lines
2.7 KiB
Plaintext

==============================================================================
Signature counter: stored on every assertion, compared against on none
==============================================================================
=== Signature counter: does the relying party detect a cloned authenticator? ===
POST /login (password) -> HTTP 302, Location: http://localhost:8080/
$ POST /webauthn/register/options
HTTP 200
{"attestation":"none","authenticatorSelection":{"residentKey":"required","userVerification":"preferred"},"challenge":"U-Ag0aFP_XRcaPxY293PTL-bRtb263WeXHdkShFPY58","excludeCredentials":[],"extensions":{"credProps":true},"pubKeyCredParams":[{"alg":-8,"type":"public-key"},{"alg":-7,"type":"public-key"},{"alg":-257,"type":"public-key"}],"rp":{"id":"localhost","name":"ankurm passkeys demo"},"timeout":300000,"user":{"name":"user","id":"YD7ijO_ydB9lpHfkEczKQ-RGqud-lI0XnrvNYxXOF0M","displayName":"user"}}
authenticator produced credentialId vALRaznkn_0DdHi9VgpA2A and a 178-byte CBOR attestation object
$ POST /webauthn/register
HTTP 200
{"success":true}
logged out, cookie jar emptied
--- assertion 1, authenticator signCount = 1
$ POST /webauthn/authenticate/options
HTTP 200
{"allowCredentials":[],"challenge":"cYWUR_Jdr9MZZ-mRwzk7lnDPd9zGjzbaoaJy_Hwybrk","extensions":{},"rpId":"localhost","timeout":300000,"userVerification":"preferred"}
$ POST /login/webauthn
HTTP 200
{"authenticated":true,"redirectUrl":"/"}
POST /login (password) -> HTTP 302, Location: http://localhost:8080/
stored signatureCount now: 1
logged out, cookie jar emptied
--- assertion 2, authenticator signCount = 2
$ POST /webauthn/authenticate/options
HTTP 200
{"allowCredentials":[],"challenge":"HWqOXFziV2H1LG98g2MGr5qvbbrBLgsPi1yOBTw73qg","extensions":{},"rpId":"localhost","timeout":300000,"userVerification":"preferred"}
$ POST /login/webauthn
HTTP 200
{"authenticated":true,"redirectUrl":"/"}
POST /login (password) -> HTTP 302, Location: http://localhost:8080/
stored signatureCount now: 2
logged out, cookie jar emptied
--- assertion 3, authenticator signCount = 3
$ POST /webauthn/authenticate/options
HTTP 200
{"allowCredentials":[],"challenge":"GqB6o-RP_G76sVZm6KVk1MHBGziU61UlVTd62lzerYo","extensions":{},"rpId":"localhost","timeout":300000,"userVerification":"preferred"}
$ POST /login/webauthn
HTTP 200
{"authenticated":true,"redirectUrl":"/"}
POST /login (password) -> HTTP 302, Location: http://localhost:8080/
stored signatureCount now: 3
logged out, cookie jar emptied
=== Replaying a stale counter. A cloned key would look exactly like this ===
--- assertion 4, authenticator signCount = 1 (lower than the stored 3)
$ POST /login/webauthn
HTTP 200
{"authenticated":true,"redirectUrl":"/"}
replayed a counter of 1 after the relying party had stored 3: HTTP 200