clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

main
forge / test / scenarios / identity-passkey.sh
identity-passkey.sh 2.7K
1# Passkeys end to end, with a software authenticator (test/authenticator.ts,2# ES256, attestation "none"): the link ssh passkey prints adds a passkey for3# the ssh user and signs that browser in; after that it signs in by itself,4# usernameless, for a private page of the user's. The forge checks the5# origin, the RP ID, user verification, the signature counter, and that a6# challenge is used once.7. "$(dirname "$0")/../lib.sh"8user dami9user ana10repo --private dami/secret "hidden thing"11clone dami dami/secret "$T/w/dami"12at "$T/w/dami" commit "first"13ok push_as dami "$T/w/dami" origin main14build15export FORGE_BASE_URL=https://forge.test16private_server17auth() { node "$T_HOME/test/authenticator.ts" "$@"; }18page() { http -s -o /dev/null -w '%{http_code}' -H "Cookie: __Host-forge=$1" "$PRIV/dami/secret/"; }1920code=$(ssh_as dami passkey laptop | sed -n 's/^ .*#enroll=//p')21c1=$(auth enroll "$PRIV" "$code" "$T/dami.key") || fail "enroll"22eq "$(page "$c1")" 20023ok ssh_as dami passkeys24has "$T_LOG" '^[0-9a-f]{8} 2026-09-01 00:00 2026-09-01 00:00 laptop$'25ok ssh_as dami sessions26has "$T_LOG" ' a browser \(passkey [0-9a-f]{8}\)$'2728# Signing in again: a session of its own.29c2=$(auth login "$PRIV" "$T/dami.key") || fail "login"30[ "$c1" != "$c2" ] || fail "the same session twice"31eq "$(page "$c2")" 20032ok ssh_as dami sessions33eq "$(grep -c 'passkey' "$T_LOG")" 234# A session's cookie is no use to someone who may not read the repo.35code=$(ssh_as ana passkey | sed -n 's/^ .*#enroll=//p')36ca=$(auth enroll "$PRIV" "$code" "$T/ana.key") || fail "enroll ana"37eq "$(page "$ca")" 40438eq "$(page nonsense)" 4043940# What must fail.41fails_with 'HTTP 403: .*not from this forge' auth login "$PRIV" "$T/dami.key" --origin https://evil.test42fails_with 'HTTP 400: .*origin' auth login "$PRIV" "$T/dami.key" --client-origin https://evil.test43fails_with 'HTTP 400: .*RP ID' auth login "$PRIV" "$T/dami.key" --rp evil.test44fails_with 'HTTP 400: .*[Uu]ser verification' auth login "$PRIV" "$T/dami.key" --no-uv45# A counter that didn't go up: a cloned authenticator.46fails_with 'HTTP 400: .*counter' auth login "$PRIV" "$T/dami.key" --counter 147# The same signed response twice: the challenge was used up.48fails_with 'HTTP 400: .*challenge' auth login "$PRIV" "$T/dami.key" --replay49# ana's key, claiming to be dami's: the user handle doesn't match.50node -e '51const fs = require("fs");52const [a, d, out] = process.argv.slice(1);53const k = JSON.parse(fs.readFileSync(a));54k.userHandle = JSON.parse(fs.readFileSync(d)).userHandle;55fs.writeFileSync(out, JSON.stringify(k));' "$T/ana.key" "$T/dami.key" "$T/mixed.key"56fails_with "HTTP 400: .*doesn't belong" auth login "$PRIV" "$T/mixed.key"57# Through it all, the good passkey still works.58ok auth login "$PRIV" "$T/dami.key"