History

Stabilize auth session cookies per server mode (#1898)

Julius Marminge committed 7 months ago

Commit 4ae9de31 parent 5fa09fa11 files · +212 −22Browse files

Showing the changes to apps/server/src (6 of 11 files). Show all

Unified · Split

6 files
  1. apps/server/src/auth/Layers/ServerAuth.ts +11 −1
  2. apps/server/src/auth/Layers/ServerAuthPolicy.test.ts +3 −0
  3. apps/server/src/auth/Layers/ServerAuthPolicy.ts +5 −2
  4. apps/server/src/auth/Layers/SessionCredentialService.ts +8 −2
  5. apps/server/src/auth/utils.ts +12 −1
  6. apps/server/src/server.test.ts +1 −1
apps/server/src/auth/Layers/ServerAuth.ts+11−1
@@ -20,7 +20,10 @@ import { · 19 unmodified lines
2020 AuthError,
2121 type ServerAuthShape,
2222 } from "../Services/ServerAuth.ts";
23−import { SessionCredentialService } from "../Services/SessionCredentialService.ts";
23+import {
24+ SessionCredentialError,
25+ SessionCredentialService,
26+} from "../Services/SessionCredentialService.ts";
2427 import { AuthControlPlaneLive, AuthCoreLive } from "./AuthControlPlane.ts";
2528
2629 type BootstrapExchangeResult = {
@@ -65,6 +68,13 @@ export const makeServerAuth = Effect.gen(function* () { · 38 unmodified lines
6568
6669 const authenticateToken = (token: string): Effect.Effect<AuthenticatedSession, AuthError> =>
6770 sessions.verify(token).pipe(
71+ Effect.tapError((cause: SessionCredentialError) =>
72+ Effect.logWarning("Rejected authenticated session credential.").pipe(
73+ Effect.annotateLogs({
74+ reason: cause.message,
75+ }),
76+ ),
77+ ),
6878 Effect.map((session) => ({
6979 sessionId: session.sessionId,
7080 subject: session.subject,
apps/server/src/auth/Layers/ServerAuthPolicy.test.ts+3−0
@@ -33,10 +33,12 @@ it.layer(NodeServices.layer)("ServerAuthPolicyLive", (it) => { · 32 unmodified lines
3333
3434 expect(descriptor.policy).toBe("desktop-managed-local");
3535 expect(descriptor.bootstrapMethods).toEqual(["desktop-bootstrap"]);
36+ expect(descriptor.sessionCookieName).toBe("t3_session_3773");
3637 }).pipe(
3738 Effect.provide(
3839 makeServerAuthPolicyLayer({
3940 mode: "desktop",
41+ port: 3773,
4042 }),
4143 ),
4244 ),
@@ -66,6 +68,7 @@ it.layer(NodeServices.layer)("ServerAuthPolicyLive", (it) => { · 23 unmodified lines
6668
6769 expect(descriptor.policy).toBe("loopback-browser");
6870 expect(descriptor.bootstrapMethods).toEqual(["one-time-token"]);
71+ expect(descriptor.sessionCookieName).toBe("t3_session");
6972 }).pipe(
7073 Effect.provide(
7174 makeServerAuthPolicyLayer({
apps/server/src/auth/Layers/ServerAuthPolicy.ts+5−2
@@ -3,7 +3,7 @@ import { Effect, Layer } from "effect"; · 2 unmodified lines
33
44 import { ServerConfig } from "../../config.ts";
55 import { ServerAuthPolicy, type ServerAuthPolicyShape } from "../Services/ServerAuthPolicy.ts";
6−import { SESSION_COOKIE_NAME } from "../utils.ts";
6+import { resolveSessionCookieName } from "../utils.ts";
77 import { isLoopbackHost, isWildcardHost } from "../../startupAccess.ts";
88
99 export const makeServerAuthPolicy = Effect.gen(function* () {
@@ -30,7 +30,10 @@ export const makeServerAuthPolicy = Effect.gen(function* () { · 20 unmodified lines
3030 policy,
3131 bootstrapMethods,
3232 sessionMethods: ["browser-session-cookie", "bearer-session-token"],
33− sessionCookieName: SESSION_COOKIE_NAME,
33+ sessionCookieName: resolveSessionCookieName({
34+ mode: config.mode,
35+ port: config.port,
36+ }),
3437 };
3538
3639 return {
apps/server/src/auth/Layers/SessionCredentialService.ts+8−2
@@ -2,10 +2,10 @@ import { AuthSessionId, type AuthClientMetadata, type AuthClientSession } from " · 1 unmodified line
22 import { Clock, DateTime, Duration, Effect, Layer, PubSub, Ref, Schema, Stream } from "effect";
33 import { Option } from "effect";
44
5+import { ServerConfig } from "../../config.ts";
56 import { AuthSessionRepositoryLive } from "../../persistence/Layers/AuthSessions.ts";
67 import { AuthSessionRepository } from "../../persistence/Services/AuthSessions.ts";
78 import { ServerSecretStore } from "../Services/ServerSecretStore.ts";
8−import { SESSION_COOKIE_NAME } from "../utils.ts";
99 import {
1010 SessionCredentialError,
1111 SessionCredentialService,
@@ -17,6 +17,7 @@ import { · 5 unmodified lines
1717 import {
1818 base64UrlDecodeUtf8,
1919 base64UrlEncode,
20+ resolveSessionCookieName,
2021 signPayload,
2122 timingSafeEqualBase64Url,
2223 } from "../utils.ts";
@@ -81,11 +82,16 @@ function toAuthClientSession(input: Omit<AuthClientSession, "current">): AuthCli · 58 unmodified lines
8182 }
8283
8384 export const makeSessionCredentialService = Effect.gen(function* () {
85+ const serverConfig = yield* ServerConfig;
8486 const secretStore = yield* ServerSecretStore;
8587 const authSessions = yield* AuthSessionRepository;
8688 const signingSecret = yield* secretStore.getOrCreateRandom(SIGNING_SECRET_NAME, 32);
8789 const connectedSessionsRef = yield* Ref.make(new Map<string, number>());
8890 const changesPubSub = yield* PubSub.unbounded<SessionCredentialChange>();
91+ const cookieName = resolveSessionCookieName({
92+ mode: serverConfig.mode,
93+ port: serverConfig.port,
94+ });
8995
9096 const toSessionCredentialError = (message: string) => (cause: unknown) =>
9197 new SessionCredentialError({
@@ -472,7 +478,7 @@ export const makeSessionCredentialService = Effect.gen(function* () { · 380 unmodified lines
472478 }).pipe(Effect.mapError(toSessionCredentialError("Failed to revoke other sessions.")));
473479
474480 return {
475− cookieName: SESSION_COOKIE_NAME,
481+ cookieName,
476482 issue,
477483 verify,
478484 issueWebSocketToken,
apps/server/src/auth/utils.ts+12−1
@@ -2,7 +2,18 @@ import type { AuthClientMetadata, AuthClientMetadataDeviceType } from "@t3tools/ · 1 unmodified line
22 import type * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
33 import * as Crypto from "node:crypto";
44
5−export const SESSION_COOKIE_NAME = "t3_session";
5+const SESSION_COOKIE_NAME = "t3_session";
6+
7+export function resolveSessionCookieName(input: {
8+ readonly mode: "web" | "desktop";
9+ readonly port: number;
10+}): string {
11+ if (input.mode !== "desktop") {
12+ return SESSION_COOKIE_NAME;
13+ }
14+
15+ return `${SESSION_COOKIE_NAME}_${input.port}`;
16+}
617
718 export function base64UrlEncode(input: string | Uint8Array): string {
819 const buffer = typeof input === "string" ? Buffer.from(input, "utf8") : Buffer.from(input);
apps/server/src/server.test.ts+1−1
@@ -789,7 +789,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 788 unmodified lines
789789 "browser-session-cookie",
790790 "bearer-session-token",
791791 ]);
792− assert.equal(body.auth.sessionCookieName, "t3_session");
792+ assert.isTrue(body.auth.sessionCookieName.startsWith("t3_session_"));
793793 }).pipe(Effect.provide(NodeHttpServer.layerTest)),
794794 );
795795

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t