Skip to content

Commit 48cf8ee

Browse files
committed
Decode the login-state envelope in the auth-session CSRF scenario
The state parameter is now base64url(JSON { nonce, returnTo }), so the scenario decodes it and asserts the nonce inside instead of expecting the raw hex value.
1 parent 661a155 commit 48cf8ee

1 file changed

Lines changed: 13 additions & 2 deletions

File tree

e2e/cloud/auth-session.test.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// callback refusing forged/incomplete redirects, the sealed-session cookie
66
// actually authorizing the session API, and logout dropping the cookie.
77
import { expect } from "@effect/vitest";
8-
import { Effect } from "effect";
8+
import { Effect, Encoding, Result, Schema } from "effect";
99

1010
import { scenario } from "../src/scenario";
1111
import { Api, Target } from "../src/services";
@@ -33,7 +33,18 @@ scenario(
3333

3434
const authorizeUrl = new URL(response.headers.get("location") ?? "");
3535
const state = authorizeUrl.searchParams.get("state") ?? "";
36-
expect(state, "the redirect carries an unguessable CSRF state").toMatch(/^[0-9a-f]{64}$/);
36+
// state = base64url(JSON { nonce, returnTo? }) — the nonce is the CSRF
37+
// secret; returnTo (absent here, no query was passed) rides beside it.
38+
const decoded = Schema.decodeUnknownOption(
39+
Schema.fromJsonString(
40+
Schema.Struct({ nonce: Schema.String, returnTo: Schema.optional(Schema.String) }),
41+
),
42+
)(Result.getOrElse(Encoding.decodeBase64UrlString(state), () => ""));
43+
expect(decoded._tag, "the state decodes as our login-state envelope").toBe("Some");
44+
expect(
45+
decoded._tag === "Some" ? decoded.value.nonce : "",
46+
"the state carries an unguessable CSRF nonce",
47+
).toMatch(/^[0-9a-f]{64}$/);
3748
expect(
3849
authorizeUrl.searchParams.get("redirect_uri"),
3950
"AuthKit is told to come back to this deployment's callback",

0 commit comments

Comments
 (0)