Correct continuation audit date and navigation cookie lifetime description
3 files changed,
+4
−4
docs/superpowers/specs/{2026-09-08-credential-return.md => 2026-09-07-credential-return.md}+1 −1internal/home/signin_return_factor_test.go+1 −1internal/home/signin_return_test.go+2 −2
diff --git a/docs/superpowers/specs/2026-09-08-credential-return.md b/docs/superpowers/specs/2026-09-07-credential-return.md| similarity index 82% |
| rename from docs/superpowers/specs/2026-09-08-credential-return.md |
| rename to docs/superpowers/specs/2026-09-07-credential-return.md |
| index bea4718..b501134 100644 |
| --- a/docs/superpowers/specs/2026-09-08-credential-return.md |
| +++ b/docs/superpowers/specs/2026-09-07-credential-return.md |
| @@ -4,7 +4,7 @@ Status: bounded draft, independent highest-model review pending; base651e5b3. No |
| Authority: Home design §4 credential-origin separation, §5.1 sign-in, §5.2 optional second factor/recovery and existing freshness-protected routes. No auth protocol, factor or freshness rule changes. |
| -RequireFresh already redirects stale enrol/codes GETs to /signin?return_to=... . The form discards that navigation intent and completeSignIn always redirects /. Carry only the exact compiled destinations /passkey/enrol and /passkey/codes in a host-only Secure HttpOnly SameSite=Lax cookie, Path=/, lifetime ten minutes. No address, identity, authority or content is stored. A hostile allowed target grants nothing: its existing RequireFresh still applies. No custom signing/encryption/session protocol. |
| +RequireFresh already redirects stale enrol/codes GETs to /signin?return_to=... . The form discards that navigation intent and completeSignIn always redirects /. Carry only the exact compiled destinations /passkey/enrol and /passkey/codes in a host-only Secure HttpOnly SameSite=Lax cookie, Path=/, browser MaxAge ten minutes. This bounds ordinary browser navigation retention, not a server-validated authentication lifetime. Replaying an allowed navigation target grants no authority; existing session and freshness validation remain authoritative. No address, identity, authority or content is stored. A hostile allowed target grants nothing: its existing RequireFresh still applies. No custom signing/encryption/session protocol. |
| GET /signin accepts one query return_to only when it matches the allowlist. Plain sign-in clears previous intent; the existing force=1 Keymail fallback preserves a validated existing intent. Invalid/duplicate query input clears it. POST body/query return_to never supplies intent. Verified magic-link or Keymail completion validates the cookie again and clears it. Missing/cross-browser cookie returns /. Failed first-factor completion never mints a session; ordinary Keymail failure preserves navigation through its force fallback. |
diff --git a/internal/home/signin_return_factor_test.go b/internal/home/signin_return_factor_test.go| index 09036f0..c0f44ff 100644 |
| --- a/internal/home/signin_return_factor_test.go |
| +++ b/internal/home/signin_return_factor_test.go |
| @@ -11,7 +11,7 @@ import ( |
| "github.com/carlosframework/rastrillo/webauthn/authtest" |
| ) |
| -// Mutation-checked 2026-09-08: bypassing Gate or passing the original |
| +// Mutation-checked 2026-09-07: bypassing Gate or passing the original |
| // request allows factor bypass or hostile FormValue precedence and fails. |
| func TestCredentialReturnSecondFactors(t *testing.T) { |
| for _, first := range []string{"magic", "keymail"} { |
diff --git a/internal/home/signin_return_test.go b/internal/home/signin_return_test.go| index 21e9ef4..16cc2e8 100644 |
| --- a/internal/home/signin_return_test.go |
| +++ b/internal/home/signin_return_test.go |
| @@ -33,7 +33,7 @@ func intentFrom(w *httptest.ResponseRecorder) *http.Cookie { |
| return nil |
| } |
| -// Mutation-checked 2026-09-08: discarding validated intent restores the |
| +// Mutation-checked 2026-09-07: discarding validated intent restores the |
| // original wrong destination; trusting cookie values fails hostile cases. |
| func TestCredentialReturnMagicLink(t *testing.T) { |
| for _, target := range []string{"/passkey/enrol", "/passkey/codes"} { |
| @@ -143,7 +143,7 @@ func TestCredentialReturnCookieGrantsNothing(t *testing.T) { |
| } |
| } |
| -// Mutation-checked 2026-09-08: replacing AuthTime with time.Now grants |
| +// Mutation-checked 2026-09-07: replacing AuthTime with time.Now grants |
| // access after an old gesture and fails this freshness check. |
| func TestCredentialReturnDoesNotRefreshOldKeymailGesture(t *testing.T) { |
| app := newTestApp(t) |