1# Sessions: the cookie is __Host-, Secure, HttpOnly, SameSite=Lax, for 302# days, and slides: a use an hour or more after the last sends it again and3# moves the end along; 30 days unused, it is gone. ssh sessions rm signs one4# out, passkeys rm signs out what that passkey signed in, and sign-out is a5# POST from the forge's own page that carries its CSRF token. Signed in, the6# page carries what the Signal API needs to tell the password manager which7# passkeys still work; a removed passkey is answered as unknown.8. "$(dirname "$0")/../lib.sh"9user dami10repo --private dami/secret11clone 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/"; }19for k in one two; do20 code=$(ssh_as dami passkey "$k" | sed -n 's/^ .*#enroll=//p')21 ok auth enroll "$PRIV" "$code" "$T/$k.key"22done2324c=$(auth login "$PRIV" "$T/one.key")25http -s -D "$T/h" -o "$T/login" -H "Cookie: __Host-forge=$c" "$PRIV/login/"26lacks "$T/h" '^Set-Cookie'27has "$T/h" "^Content-Security-Policy: default-src 'self'"28has "$T/h" '^Cache-Control: private, no-store'29has "$T/login" '<h2>signed in as <b>dami</b></h2>'30signal=$(sed -n 's/.*data-signal="\([^"]*\)".*/\1/p' "$T/login" | sed 's/"/"/g')31for k in one two; do32 id=$(node -e 'console.log(JSON.parse(require("fs").readFileSync(process.argv[1])).id)' "$T/$k.key")33 case $signal in *'"rpID":"forge.test"'*"\"$id\""*) ;; *) fail "the page doesn't signal passkey $k: $signal" ;; esac34done35grep -q 'name="csrf" value="[A-Za-z0-9_-]\{32\}"' "$T/login" || fail "no csrf token on the page"3637# Sliding: an hour and a minute later the cookie comes back for 30 more days.38stop_servers39export FORGE_NOW=$((T_NOW + 3660))40private_server41http -s -D "$T/h" -o /dev/null -H "Cookie: __Host-forge=$c" "$PRIV/dami/secret/"42has "$T/h" '^HTTP/1.1 200'43has "$T/h" "^Set-Cookie: __Host-forge=$c; Path=/; Max-Age=2592000; Secure; HttpOnly; SameSite=Lax"44ok ssh_as dami sessions45has "$T_LOG" '2026-09-01 01:01 2026-10-01 01:01 '46# 29 days on it still works (the end moved); 31 days after the last use it doesn't.47stop_servers48export FORGE_NOW=$((T_NOW + 3660 + 29 * 86400))49private_server50eq "$(page "$c")" 20051stop_servers52export FORGE_NOW=$((T_NOW + 3660 + 60 * 86400))53private_server54eq "$(page "$c")" 40455stop_servers56export FORGE_NOW=$T_NOW57private_server5859# Sign-out: only from the forge's page, with its token.60c=$(auth login "$PRIV" "$T/one.key")61out() { http -s -o /dev/null -w '%{http_code}' -X POST -H "Cookie: __Host-forge=$c" "$@" "$PRIV/login/logout"; }62csrf=$(http -s -H "Cookie: __Host-forge=$c" "$PRIV/login/" | sed -n 's/.*name="csrf" value="\([^"]*\)".*/\1/p')63eq "$(out -H 'Origin: https://evil.test' -d "csrf=$csrf")" 40364eq "$(out -d "csrf=$csrf")" 40365eq "$(out -H 'Origin: https://forge.test' -d 'csrf=nope')" 40366eq "$(page "$c")" 20067http -s -o /dev/null -D "$T/h" -X POST -H "Cookie: __Host-forge=$c" -H 'Origin: https://forge.test' -d "csrf=$csrf" "$PRIV/login/logout"68has "$T/h" '^HTTP/1.1 303'69has "$T/h" '^Location: /login/'70has "$T/h" '^Set-Cookie: __Host-forge=; Path=/; Max-Age=0; Secure; HttpOnly; SameSite=Lax'71eq "$(page "$c")" 4047273# ssh: one session, then all of them.74ok ssh_as dami sessions --all75c1=$(auth login "$PRIV" "$T/one.key")76c2=$(auth login "$PRIV" "$T/two.key")77ok ssh_as dami sessions rm "$(ssh_as dami sessions | awk 'NR == 2 { print $1 }')"78got="$(page "$c1") $(page "$c2")"79case $got in "404 200" | "200 404") ;; *) fail "sessions rm: got $got" ;; esac80ok ssh_as dami sessions --all81has "$T_LOG" '^signed out 1 session$'82eq "$(page "$c1") $(page "$c2")" "404 404"8384# passkeys rm: its sessions go, and from then on it is answered as unknown.85c1=$(auth login "$PRIV" "$T/one.key")86c2=$(auth login "$PRIV" "$T/two.key")87pk=$(ssh_as dami passkeys | awk '$NF == "two" { print $1 }')88ok ssh_as dami passkeys rm "$pk"89has "$T_LOG" "removed passkey $pk \\(two\\), and signed out the session it signed in"90eq "$(page "$c1") $(page "$c2")" "200 404"91fails_with "HTTP 400: .*doesn't know this passkey.*\"unknown\":\\{\"rpID\":\"forge.test\",\"credentialID\":\"" auth login "$PRIV" "$T/two.key"92ok auth login "$PRIV" "$T/one.key"