A password and a TOTP code are both shared secrets: the server keeps something that can be stolen from it, and the person keeps something that can be talked out of them. A passkey is neither. The private half never leaves the device, and the signature is bound to the origin that asked for it — so a convincing copy of the sign-in page at another address gets a signature it cannot use. Logging in asks for no email. The device knows which account its key belongs to, which is not only convenience: it means the sign-in page cannot be used to find out who has an account here. A key that verified its owner is both factors at once and completes the login; one that only proved possession still meets the TOTP step, exactly as a password does. The relying party id is derived from VERDANDE_BASE_URL rather than configured separately — a second setting that has to agree with the first is one that will one day disagree, and the failure is a login nobody can complete. An IP address is refused up front: no browser accepts one, and without the check the server issues a challenge happily and the browser throws SecurityError long afterwards. The list endpoint reports whether this deployment can offer them at all, so the interface never shows a button that cannot work. A challenge is stored server-side, answerable exactly once, and taken rather than read — two requests racing the same one would both succeed, which is the shape of a replay. A sign count that has gone backwards means the credential was cloned and is refused rather than logged. The Go tests cover who may reach these endpoints and that challenges are spent. The ceremony itself needs an authenticator, so the smoke test drives Chromium's virtual one over CDP — the only thing that can tell whether the base64url conversions are right, since one wrong byte produces a signature that will not verify and reads as a broken key rather than a broken encoder. That test runs at localhost in its own context while the suite stays on the loopback address, because WebAuthn refuses an IP and resolving localhost is slow enough here to cost the suite minutes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
35 lines
1.2 KiB
AMPL
35 lines
1.2 KiB
AMPL
module github.com/kristianwind/verdande
|
|
|
|
go 1.26.3
|
|
|
|
require (
|
|
github.com/coder/websocket v1.8.15
|
|
github.com/go-chi/chi/v5 v5.3.1
|
|
github.com/go-webauthn/webauthn v0.17.4
|
|
github.com/pquerna/otp v1.5.0
|
|
github.com/teambition/rrule-go v1.8.2
|
|
golang.org/x/crypto v0.55.0
|
|
modernc.org/sqlite v1.56.0
|
|
)
|
|
|
|
require (
|
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
github.com/fxamacker/cbor/v2 v2.9.2 // indirect
|
|
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
|
github.com/go-webauthn/x v0.2.6 // indirect
|
|
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
|
github.com/google/go-tpm v0.9.8 // indirect
|
|
github.com/google/uuid v1.6.0 // indirect
|
|
github.com/mattn/go-isatty v0.0.24 // indirect
|
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
|
github.com/philhofer/fwd v1.2.0 // indirect
|
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
github.com/tinylib/msgp v1.6.4 // indirect
|
|
github.com/x448/float16 v0.8.4 // indirect
|
|
golang.org/x/sys v0.47.0 // indirect
|
|
modernc.org/libc v1.74.4 // indirect
|
|
modernc.org/mathutil v1.7.1 // indirect
|
|
modernc.org/memory v1.11.0 // indirect
|
|
)
|