@voyant-travel/auth 0.133.5 → 0.134.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cloud-admin-session-public.d.ts +2 -0
- package/dist/cloud-admin-session-public.js.map +1 -1
- package/dist/cloud-admin-session.d.ts +2 -0
- package/dist/cloud-admin-session.js +1 -0
- package/dist/cloud-admin-session.js.map +1 -1
- package/dist/cloud-broker/state.d.ts +3 -1
- package/dist/cloud-broker/state.js +11 -13
- package/dist/cloud-broker/state.js.map +1 -1
- package/dist/edge.js +1 -1
- package/dist/edge.js.map +1 -1
- package/dist/node-runtime.d.ts +5 -4
- package/dist/node-runtime.js +24 -31
- package/dist/node-runtime.js.map +1 -1
- package/dist/runtime-contributor.js +1 -2
- package/dist/runtime-contributor.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +6 -5
- package/dist/server.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -172,11 +172,11 @@ import { createBetterAuth } from "@voyant-travel/auth/server"
|
|
|
172
172
|
|
|
173
173
|
const auth = createBetterAuth({
|
|
174
174
|
db,
|
|
175
|
-
basePath: "/auth",
|
|
175
|
+
basePath: "/auth/admin",
|
|
176
176
|
plugins: [
|
|
177
177
|
createVoyantCloudAdminAuthPlugin({
|
|
178
178
|
db,
|
|
179
|
-
cookieSecret: env.
|
|
179
|
+
cookieSecret: env.SESSION_CLAIMS_ADMIN_SECRET,
|
|
180
180
|
exchange: {
|
|
181
181
|
exchangeUrl: env.VOYANT_CLOUD_ADMIN_AUTH_EXCHANGE_URL,
|
|
182
182
|
deploymentId: env.VOYANT_CLOUD_DEPLOYMENT_ID,
|
|
@@ -194,7 +194,7 @@ const auth = createBetterAuth({
|
|
|
194
194
|
})
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
The plugin mounts Better Auth's `/auth/cloud/callback` endpoint. That endpoint
|
|
197
|
+
The plugin mounts Better Auth's `/auth/admin/cloud/callback` endpoint. That endpoint
|
|
198
198
|
validates the signed broker state, exchanges the one-time Cloud code, verifies
|
|
199
199
|
the signed Cloud assertion, upserts the local mirror user/account/profile,
|
|
200
200
|
stores Cloud linkage side-table rows, and then creates the local Better Auth
|
|
@@ -24,6 +24,8 @@ export type CloudAdminUserProvisioningHandler = (input: CloudAdminUserProvisioni
|
|
|
24
24
|
export interface VoyantCloudAdminAuthPluginOptions {
|
|
25
25
|
db: ReturnType<typeof getDb>;
|
|
26
26
|
cookieSecret: string;
|
|
27
|
+
/** Whether the externally visible callback is HTTPS, even behind an HTTP proxy hop. */
|
|
28
|
+
secureStateCookie?: boolean;
|
|
27
29
|
exchange: CloudAdminAuthExchangeConfig;
|
|
28
30
|
fetch?: typeof fetch;
|
|
29
31
|
revalidateAfterSeconds?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-admin-session-public.js","sourceRoot":"","sources":["../src/cloud-admin-session-public.ts"],"names":[],"mappings":"","sourcesContent":["import type { getDb } from \"@voyant-travel/db\"\nimport type { BetterAuthPlugin } from \"better-auth/types\"\n\nimport type {\n CloudAdminAssertion,\n CloudAdminAuthExchangeConfig,\n CloudAdminAuthRevalidateConfig,\n} from \"./cloud-broker.js\"\n\nexport type CloudAdminProvisionedUser = {\n id: string\n name: string\n email: string\n emailVerified: boolean\n image?: string | null\n createdAt: Date\n updatedAt: Date\n}\n\nexport type CloudAdminUserProvisioningInput = {\n db: ReturnType<typeof getDb>\n assertion: CloudAdminAssertion\n user: CloudAdminProvisionedUser\n isNewUser: boolean\n provider: {\n providerId: \"voyant-cloud\"\n providerAccountId: string\n }\n}\n\nexport type CloudAdminUserProvisioningHandler = (\n input: CloudAdminUserProvisioningInput,\n) => Promise<void> | void\n\nexport interface VoyantCloudAdminAuthPluginOptions {\n db: ReturnType<typeof getDb>\n cookieSecret: string\n exchange: CloudAdminAuthExchangeConfig\n fetch?: typeof fetch\n revalidateAfterSeconds?: number\n onUserProvisioning?: CloudAdminUserProvisioningHandler\n}\n\nexport declare function createVoyantCloudAdminAuthPlugin(\n options: VoyantCloudAdminAuthPluginOptions,\n): BetterAuthPlugin\n\nexport type VoyantCloudAdminSessionRevalidationInput = {\n db: ReturnType<typeof getDb>\n sessionId: string\n config: CloudAdminAuthRevalidateConfig\n fetch?: typeof fetch\n now?: Date\n revalidateAfterSeconds?: number\n}\n\nexport type VoyantCloudAdminSessionRevalidationResult =\n | {\n ok: true\n status: \"active\" | \"cached\"\n }\n | {\n ok: false\n status: \"revoked\"\n reason?: string\n }\n\nexport declare function revalidateVoyantCloudAdminAuthSession(\n input: VoyantCloudAdminSessionRevalidationInput,\n): Promise<VoyantCloudAdminSessionRevalidationResult>\n\nexport type VoyantCloudAdminUserRevalidationInput = {\n db: ReturnType<typeof getDb>\n userId: string\n config: CloudAdminAuthRevalidateConfig\n fetch?: typeof fetch\n now?: Date\n revalidateAfterSeconds?: number\n}\n\nexport type VoyantCloudAdminUserRevalidationResult =\n | {\n ok: true\n status: \"active\" | \"cached\"\n }\n | {\n ok: false\n status: \"revoked\"\n reason?: string\n }\n\nexport declare function revalidateVoyantCloudAdminAuthUser(\n input: VoyantCloudAdminUserRevalidationInput,\n): Promise<VoyantCloudAdminUserRevalidationResult>\n"]}
|
|
1
|
+
{"version":3,"file":"cloud-admin-session-public.js","sourceRoot":"","sources":["../src/cloud-admin-session-public.ts"],"names":[],"mappings":"","sourcesContent":["import type { getDb } from \"@voyant-travel/db\"\nimport type { BetterAuthPlugin } from \"better-auth/types\"\n\nimport type {\n CloudAdminAssertion,\n CloudAdminAuthExchangeConfig,\n CloudAdminAuthRevalidateConfig,\n} from \"./cloud-broker.js\"\n\nexport type CloudAdminProvisionedUser = {\n id: string\n name: string\n email: string\n emailVerified: boolean\n image?: string | null\n createdAt: Date\n updatedAt: Date\n}\n\nexport type CloudAdminUserProvisioningInput = {\n db: ReturnType<typeof getDb>\n assertion: CloudAdminAssertion\n user: CloudAdminProvisionedUser\n isNewUser: boolean\n provider: {\n providerId: \"voyant-cloud\"\n providerAccountId: string\n }\n}\n\nexport type CloudAdminUserProvisioningHandler = (\n input: CloudAdminUserProvisioningInput,\n) => Promise<void> | void\n\nexport interface VoyantCloudAdminAuthPluginOptions {\n db: ReturnType<typeof getDb>\n cookieSecret: string\n /** Whether the externally visible callback is HTTPS, even behind an HTTP proxy hop. */\n secureStateCookie?: boolean\n exchange: CloudAdminAuthExchangeConfig\n fetch?: typeof fetch\n revalidateAfterSeconds?: number\n onUserProvisioning?: CloudAdminUserProvisioningHandler\n}\n\nexport declare function createVoyantCloudAdminAuthPlugin(\n options: VoyantCloudAdminAuthPluginOptions,\n): BetterAuthPlugin\n\nexport type VoyantCloudAdminSessionRevalidationInput = {\n db: ReturnType<typeof getDb>\n sessionId: string\n config: CloudAdminAuthRevalidateConfig\n fetch?: typeof fetch\n now?: Date\n revalidateAfterSeconds?: number\n}\n\nexport type VoyantCloudAdminSessionRevalidationResult =\n | {\n ok: true\n status: \"active\" | \"cached\"\n }\n | {\n ok: false\n status: \"revoked\"\n reason?: string\n }\n\nexport declare function revalidateVoyantCloudAdminAuthSession(\n input: VoyantCloudAdminSessionRevalidationInput,\n): Promise<VoyantCloudAdminSessionRevalidationResult>\n\nexport type VoyantCloudAdminUserRevalidationInput = {\n db: ReturnType<typeof getDb>\n userId: string\n config: CloudAdminAuthRevalidateConfig\n fetch?: typeof fetch\n now?: Date\n revalidateAfterSeconds?: number\n}\n\nexport type VoyantCloudAdminUserRevalidationResult =\n | {\n ok: true\n status: \"active\" | \"cached\"\n }\n | {\n ok: false\n status: \"revoked\"\n reason?: string\n }\n\nexport declare function revalidateVoyantCloudAdminAuthUser(\n input: VoyantCloudAdminUserRevalidationInput,\n): Promise<VoyantCloudAdminUserRevalidationResult>\n"]}
|
|
@@ -4,6 +4,8 @@ import { type CloudAdminAssertion, type CloudAdminAuthExchangeConfig, type Cloud
|
|
|
4
4
|
export interface VoyantCloudAdminAuthPluginOptions {
|
|
5
5
|
db: ReturnType<typeof getDb>;
|
|
6
6
|
cookieSecret: string;
|
|
7
|
+
/** Whether the externally visible callback is HTTPS, even behind an HTTP proxy hop. */
|
|
8
|
+
secureStateCookie?: boolean;
|
|
7
9
|
exchange: CloudAdminAuthExchangeConfig;
|
|
8
10
|
fetch?: typeof fetch;
|
|
9
11
|
revalidateAfterSeconds?: number;
|
|
@@ -16,6 +16,7 @@ export function createVoyantCloudAdminAuthPlugin(options) {
|
|
|
16
16
|
requestUrl: ctx.request.url,
|
|
17
17
|
cookieHeader: ctx.headers?.get("cookie") ?? ctx.request.headers.get("cookie"),
|
|
18
18
|
cookieSecret: options.cookieSecret,
|
|
19
|
+
secureCookie: options.secureStateCookie,
|
|
19
20
|
});
|
|
20
21
|
ctx.setHeader("Set-Cookie", callback.clearCookie);
|
|
21
22
|
if (!callback.ok) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-admin-session.js","sourceRoot":"","sources":["../src/cloud-admin-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { getDb } from \"@voyant-travel/db\"\nimport {\n authAccount,\n authUser,\n cloudAuthSessionLinks,\n cloudAuthUserLinks,\n} from \"@voyant-travel/db/schema/iam\"\nimport { createAuthEndpoint } from \"better-auth/api\"\nimport { setSessionCookie } from \"better-auth/cookies\"\nimport type { BetterAuthPlugin } from \"better-auth/types\"\nimport { and, eq } from \"drizzle-orm\"\nimport {\n markCloudAuthSessionRevalidated,\n markCloudAuthUserRevalidated,\n revokeCloudAuthUserAccess,\n} from \"./cloud-admin-session-revalidation-store.js\"\nimport {\n type CloudAdminAssertion,\n type CloudAdminAuthExchangeConfig,\n type CloudAdminAuthRevalidateConfig,\n exchangeCloudAdminAuthCode,\n revalidateCloudAdminAuthAccess,\n verifyCloudAdminAuthCallback,\n} from \"./cloud-broker.js\"\nimport { isFirstAuthUser, provisionCurrentUserProfile } from \"./workspace.js\"\n\nexport interface VoyantCloudAdminAuthPluginOptions {\n db: ReturnType<typeof getDb>\n cookieSecret: string\n exchange: CloudAdminAuthExchangeConfig\n fetch?: typeof fetch\n revalidateAfterSeconds?: number\n onUserProvisioning?: CloudAdminUserProvisioningHandler\n}\n\nexport type VoyantCloudAdminSessionRevalidationInput = {\n db: ReturnType<typeof getDb>\n sessionId: string\n config: CloudAdminAuthRevalidateConfig\n fetch?: typeof fetch\n now?: Date\n revalidateAfterSeconds?: number\n}\n\nexport type VoyantCloudAdminSessionRevalidationResult =\n | {\n ok: true\n status: \"active\" | \"cached\"\n }\n | {\n ok: false\n status: \"revoked\"\n reason?: string\n }\n\nexport type VoyantCloudAdminUserRevalidationInput = {\n db: ReturnType<typeof getDb>\n userId: string\n config: CloudAdminAuthRevalidateConfig\n fetch?: typeof fetch\n now?: Date\n revalidateAfterSeconds?: number\n}\n\nexport type VoyantCloudAdminUserRevalidationResult =\n | {\n ok: true\n status: \"active\" | \"cached\"\n }\n | {\n ok: false\n status: \"revoked\"\n reason?: string\n }\n\nconst VOYANT_CLOUD_PROVIDER_ID = \"voyant-cloud\"\nconst DEFAULT_CLOUD_SESSION_REVALIDATE_AFTER_SECONDS = 15 * 60\n\ntype BetterAuthUserForCookie = {\n id: string\n name: string\n email: string\n emailVerified: boolean\n image?: string | null\n createdAt: Date\n updatedAt: Date\n}\n\ntype BetterAuthSessionForCookie = {\n id: string\n token: string\n userId: string\n expiresAt: Date\n createdAt: Date\n updatedAt: Date\n ipAddress?: string | null\n userAgent?: string | null\n}\n\nexport type CloudAdminProvisionedUser = BetterAuthUserForCookie\n\nexport type CloudAdminUserProvisioningInput = {\n db: ReturnType<typeof getDb>\n assertion: CloudAdminAssertion\n user: CloudAdminProvisionedUser\n isNewUser: boolean\n provider: {\n providerId: typeof VOYANT_CLOUD_PROVIDER_ID\n providerAccountId: string\n }\n}\n\nexport type CloudAdminUserProvisioningHandler = (\n input: CloudAdminUserProvisioningInput,\n) => Promise<void> | void\n\nexport function createVoyantCloudAdminAuthPlugin(\n options: VoyantCloudAdminAuthPluginOptions,\n): BetterAuthPlugin {\n return {\n id: \"voyant-cloud-admin-auth\",\n endpoints: {\n voyantCloudAdminAuthCallback: createAuthEndpoint(\n \"/cloud/callback\",\n { method: \"GET\", requireRequest: true },\n async (ctx) => {\n const callback = await verifyCloudAdminAuthCallback({\n requestUrl: ctx.request.url,\n cookieHeader: ctx.headers?.get(\"cookie\") ?? ctx.request.headers.get(\"cookie\"),\n cookieSecret: options.cookieSecret,\n })\n ctx.setHeader(\"Set-Cookie\", callback.clearCookie)\n\n if (!callback.ok) {\n const status = callback.error === \"cloud_error\" ? 401 : 400\n return ctx.json(\n {\n error: callback.error,\n ...(callback.cloudError ? { cloudError: callback.cloudError } : {}),\n },\n { status },\n )\n }\n\n let assertion: CloudAdminAssertion\n try {\n assertion = await exchangeCloudAdminAuthCode({\n code: callback.code,\n state: callback.state,\n config: options.exchange,\n ...(options.fetch ? { fetch: options.fetch } : {}),\n })\n } catch (error) {\n ctx.context.logger.error(\"[auth/cloud/callback] Exchange error\", error)\n return ctx.json({ error: \"Voyant Cloud auth exchange failed\" }, { status: 401 })\n }\n\n const user = await upsertVoyantCloudMirrorUser(options.db, assertion, {\n onUserProvisioning: options.onUserProvisioning,\n })\n const session = (await ctx.context.internalAdapter.createSession(\n user.id,\n )) as BetterAuthSessionForCookie\n await upsertVoyantCloudSessionLink(options.db, {\n assertion,\n session,\n revalidateAfterSeconds:\n options.revalidateAfterSeconds ?? DEFAULT_CLOUD_SESSION_REVALIDATE_AFTER_SECONDS,\n })\n await setSessionCookie(ctx, { session, user })\n\n throw ctx.redirect(callback.state.next)\n },\n ),\n },\n }\n}\n\nexport async function revalidateVoyantCloudAdminAuthSession({\n db,\n sessionId,\n config,\n fetch,\n now = new Date(),\n revalidateAfterSeconds = DEFAULT_CLOUD_SESSION_REVALIDATE_AFTER_SECONDS,\n}: VoyantCloudAdminSessionRevalidationInput): Promise<VoyantCloudAdminSessionRevalidationResult> {\n const [sessionLink] = await db\n .select()\n .from(cloudAuthSessionLinks)\n .where(eq(cloudAuthSessionLinks.sessionId, sessionId))\n .limit(1)\n\n if (!sessionLink || sessionLink.revokedAt) {\n return { ok: false, status: \"revoked\", reason: \"missing_or_revoked_session\" }\n }\n\n if (sessionLink.revalidateAfter > now) {\n return { ok: true, status: \"cached\" }\n }\n\n const revalidation = await revalidateCloudAdminAuthAccess({\n workosUserId: sessionLink.providerAccountId,\n config,\n ...(fetch ? { fetch } : {}),\n })\n\n if (revalidation.ok) {\n await markCloudAuthSessionRevalidated(db, {\n sessionId: sessionLink.sessionId,\n userId: sessionLink.userId,\n now,\n revalidateAfterSeconds,\n })\n // Refresh the cached RBAC scopes so a permission change applied while the\n // member is signed in takes effect at the next revalidation, not only at\n // next login (member-rbac-rfc, voyant#2085). `undefined` ⇒ older platform\n // didn't send scopes; leave the column as-is.\n if (revalidation.scopes !== undefined) {\n await db\n .update(cloudAuthUserLinks)\n .set({ scopes: revalidation.scopes, updatedAt: now })\n .where(eq(cloudAuthUserLinks.userId, sessionLink.userId))\n }\n return { ok: true, status: \"active\" }\n }\n\n await revokeCloudAuthUserAccess(db, {\n sessionId: sessionLink.sessionId,\n userId: sessionLink.userId,\n now,\n })\n\n return {\n ok: false,\n status: \"revoked\",\n reason: revalidation.reason,\n }\n}\n\nexport async function revalidateVoyantCloudAdminAuthUser({\n db,\n userId,\n config,\n fetch,\n now = new Date(),\n revalidateAfterSeconds = DEFAULT_CLOUD_SESSION_REVALIDATE_AFTER_SECONDS,\n}: VoyantCloudAdminUserRevalidationInput): Promise<VoyantCloudAdminUserRevalidationResult> {\n const [userLink] = await db\n .select()\n .from(cloudAuthUserLinks)\n .where(eq(cloudAuthUserLinks.userId, userId))\n .limit(1)\n\n if (!userLink || userLink.revokedAt) {\n return { ok: false, status: \"revoked\", reason: \"missing_or_revoked_user\" }\n }\n\n const nextRevalidationAt = userLink.lastRevalidatedAt\n ? new Date(userLink.lastRevalidatedAt.getTime() + revalidateAfterSeconds * 1000)\n : null\n if (nextRevalidationAt && nextRevalidationAt > now) {\n return { ok: true, status: \"cached\" }\n }\n\n const revalidation = await revalidateCloudAdminAuthAccess({\n workosUserId: userLink.providerAccountId,\n config,\n ...(fetch ? { fetch } : {}),\n })\n\n if (revalidation.ok) {\n await markCloudAuthUserRevalidated(db, {\n userId: userLink.userId,\n now,\n })\n return { ok: true, status: \"active\" }\n }\n\n await revokeCloudAuthUserAccess(db, {\n userId: userLink.userId,\n now,\n })\n\n return {\n ok: false,\n status: \"revoked\",\n reason: revalidation.reason,\n }\n}\n\nasync function upsertVoyantCloudMirrorUser(\n db: ReturnType<typeof getDb>,\n assertion: CloudAdminAssertion,\n options: {\n onUserProvisioning?: CloudAdminUserProvisioningHandler\n } = {},\n): Promise<BetterAuthUserForCookie> {\n const now = new Date()\n const providerAccountId = assertion.workosUserId\n const displayName = cloudAssertionDisplayName(assertion)\n const existingUser = await findCloudMirrorUser(\n db,\n providerAccountId,\n assertion.email,\n assertion.emailVerified,\n )\n const isNewUser = !existingUser\n const userId = existingUser?.id ?? crypto.randomUUID()\n\n if (existingUser) {\n await db\n .update(authUser)\n .set({\n name: displayName,\n email: assertion.email,\n emailVerified: assertion.emailVerified,\n image: assertion.image ?? null,\n updatedAt: now,\n })\n .where(eq(authUser.id, existingUser.id))\n } else {\n await db.insert(authUser).values({\n id: userId,\n name: displayName,\n email: assertion.email,\n emailVerified: assertion.emailVerified,\n image: assertion.image ?? null,\n createdAt: now,\n updatedAt: now,\n })\n }\n\n await db\n .insert(authAccount)\n .values({\n id: cloudAuthAccountId(providerAccountId),\n userId,\n accountId: providerAccountId,\n providerId: VOYANT_CLOUD_PROVIDER_ID,\n createdAt: now,\n updatedAt: now,\n })\n .onConflictDoUpdate({\n target: [authAccount.providerId, authAccount.accountId],\n set: {\n userId,\n updatedAt: now,\n },\n })\n\n await upsertVoyantCloudUserLink(db, userId, assertion, now)\n\n await provisionCurrentUserProfile(db, {\n userId,\n name: displayName,\n image: assertion.image,\n isSuperAdmin: await isFirstAuthUser(db),\n })\n\n const [user] = await db\n .select({\n id: authUser.id,\n name: authUser.name,\n email: authUser.email,\n emailVerified: authUser.emailVerified,\n image: authUser.image,\n createdAt: authUser.createdAt,\n updatedAt: authUser.updatedAt,\n })\n .from(authUser)\n .where(eq(authUser.id, userId))\n .limit(1)\n\n if (!user?.email) {\n throw new Error(\"Voyant Cloud auth mirror user was not provisioned\")\n }\n\n const provisionedUser = {\n id: user.id,\n name: user.name,\n email: user.email,\n emailVerified: user.emailVerified,\n image: user.image,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt,\n }\n\n await options.onUserProvisioning?.({\n db,\n assertion,\n user: provisionedUser,\n isNewUser,\n provider: {\n providerId: VOYANT_CLOUD_PROVIDER_ID,\n providerAccountId,\n },\n })\n\n return provisionedUser\n}\n\nasync function findCloudMirrorUser(\n db: ReturnType<typeof getDb>,\n providerAccountId: string,\n email?: string,\n emailVerified?: boolean,\n): Promise<{ id: string } | null> {\n const [linked] = await db\n .select({ id: authUser.id })\n .from(cloudAuthUserLinks)\n .innerJoin(authUser, eq(authUser.id, cloudAuthUserLinks.userId))\n .where(\n and(\n eq(cloudAuthUserLinks.providerId, VOYANT_CLOUD_PROVIDER_ID),\n eq(cloudAuthUserLinks.providerAccountId, providerAccountId),\n ),\n )\n .limit(1)\n\n if (linked) return linked\n\n const [account] = await db\n .select({ id: authUser.id })\n .from(authAccount)\n .innerJoin(authUser, eq(authUser.id, authAccount.userId))\n .where(\n and(\n eq(authAccount.providerId, VOYANT_CLOUD_PROVIDER_ID),\n eq(authAccount.accountId, providerAccountId),\n ),\n )\n .limit(1)\n\n if (account) return account\n if (!email || emailVerified !== true) return null\n\n const [emailUser] = await db\n .select({ id: authUser.id })\n .from(authUser)\n .where(eq(authUser.email, email))\n .limit(1)\n\n return emailUser ?? null\n}\n\nfunction cloudAuthUserLinkValues(\n userId: string,\n assertion: CloudAdminAssertion,\n now: Date,\n): typeof cloudAuthUserLinks.$inferInsert {\n return {\n userId,\n providerId: VOYANT_CLOUD_PROVIDER_ID,\n providerAccountId: assertion.workosUserId,\n deploymentId: assertion.deploymentId,\n platformOrganizationId: assertion.platformOrganizationId,\n workosOrganizationId: assertion.workosOrganizationId,\n membershipId: assertion.membershipId ?? null,\n roleSlug: assertion.roleSlug ?? null,\n roleName: assertion.roleName ?? null,\n surfaces: assertion.surfaces ?? [],\n // Member RBAC scope set from the assertion. `null` (not `[]`) when the\n // platform sent none, so resolveAuthRequest can tell \"no scopes claim\"\n // (derive from role / full-access fallback) from \"explicitly empty\".\n scopes: assertion.scopes ?? null,\n lastAssertionAt: new Date(assertion.iat * 1000),\n lastRevalidatedAt: now,\n revokedAt: null,\n updatedAt: now,\n }\n}\n\nasync function upsertVoyantCloudUserLink(\n db: ReturnType<typeof getDb>,\n userId: string,\n assertion: CloudAdminAssertion,\n now: Date,\n): Promise<void> {\n const values = cloudAuthUserLinkValues(userId, assertion, now)\n const [existingForUser] = await db\n .select({ userId: cloudAuthUserLinks.userId })\n .from(cloudAuthUserLinks)\n .where(eq(cloudAuthUserLinks.userId, userId))\n .limit(1)\n\n if (existingForUser) {\n await db\n .update(cloudAuthUserLinks)\n .set({\n ...values,\n userId,\n revokedAt: null,\n })\n .where(eq(cloudAuthUserLinks.userId, userId))\n return\n }\n\n await db\n .insert(cloudAuthUserLinks)\n .values(values)\n .onConflictDoUpdate({\n target: [cloudAuthUserLinks.providerId, cloudAuthUserLinks.providerAccountId],\n set: {\n ...values,\n userId,\n revokedAt: null,\n },\n })\n}\n\nasync function upsertVoyantCloudSessionLink(\n db: ReturnType<typeof getDb>,\n input: {\n assertion: CloudAdminAssertion\n session: BetterAuthSessionForCookie\n revalidateAfterSeconds: number\n },\n): Promise<void> {\n const now = new Date()\n await db.insert(cloudAuthSessionLinks).values({\n sessionId: input.session.id,\n userId: input.session.userId,\n providerId: VOYANT_CLOUD_PROVIDER_ID,\n providerAccountId: input.assertion.workosUserId,\n deploymentId: input.assertion.deploymentId,\n revalidateAfter: new Date(now.getTime() + input.revalidateAfterSeconds * 1000),\n lastRevalidatedAt: now,\n revokedAt: null,\n updatedAt: now,\n })\n}\n\nfunction cloudAssertionDisplayName(assertion: CloudAdminAssertion): string {\n const fromParts = [assertion.firstName, assertion.lastName]\n .map((part) => part?.trim())\n .filter((part): part is string => Boolean(part))\n .join(\" \")\n return assertion.name?.trim() || fromParts || assertion.email\n}\n\nfunction cloudAuthAccountId(providerAccountId: string): string {\n return `acc_${VOYANT_CLOUD_PROVIDER_ID}_${providerAccountId}`\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cloud-admin-session.js","sourceRoot":"","sources":["../src/cloud-admin-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type { getDb } from \"@voyant-travel/db\"\nimport {\n authAccount,\n authUser,\n cloudAuthSessionLinks,\n cloudAuthUserLinks,\n} from \"@voyant-travel/db/schema/iam\"\nimport { createAuthEndpoint } from \"better-auth/api\"\nimport { setSessionCookie } from \"better-auth/cookies\"\nimport type { BetterAuthPlugin } from \"better-auth/types\"\nimport { and, eq } from \"drizzle-orm\"\nimport {\n markCloudAuthSessionRevalidated,\n markCloudAuthUserRevalidated,\n revokeCloudAuthUserAccess,\n} from \"./cloud-admin-session-revalidation-store.js\"\nimport {\n type CloudAdminAssertion,\n type CloudAdminAuthExchangeConfig,\n type CloudAdminAuthRevalidateConfig,\n exchangeCloudAdminAuthCode,\n revalidateCloudAdminAuthAccess,\n verifyCloudAdminAuthCallback,\n} from \"./cloud-broker.js\"\nimport { isFirstAuthUser, provisionCurrentUserProfile } from \"./workspace.js\"\n\nexport interface VoyantCloudAdminAuthPluginOptions {\n db: ReturnType<typeof getDb>\n cookieSecret: string\n /** Whether the externally visible callback is HTTPS, even behind an HTTP proxy hop. */\n secureStateCookie?: boolean\n exchange: CloudAdminAuthExchangeConfig\n fetch?: typeof fetch\n revalidateAfterSeconds?: number\n onUserProvisioning?: CloudAdminUserProvisioningHandler\n}\n\nexport type VoyantCloudAdminSessionRevalidationInput = {\n db: ReturnType<typeof getDb>\n sessionId: string\n config: CloudAdminAuthRevalidateConfig\n fetch?: typeof fetch\n now?: Date\n revalidateAfterSeconds?: number\n}\n\nexport type VoyantCloudAdminSessionRevalidationResult =\n | {\n ok: true\n status: \"active\" | \"cached\"\n }\n | {\n ok: false\n status: \"revoked\"\n reason?: string\n }\n\nexport type VoyantCloudAdminUserRevalidationInput = {\n db: ReturnType<typeof getDb>\n userId: string\n config: CloudAdminAuthRevalidateConfig\n fetch?: typeof fetch\n now?: Date\n revalidateAfterSeconds?: number\n}\n\nexport type VoyantCloudAdminUserRevalidationResult =\n | {\n ok: true\n status: \"active\" | \"cached\"\n }\n | {\n ok: false\n status: \"revoked\"\n reason?: string\n }\n\nconst VOYANT_CLOUD_PROVIDER_ID = \"voyant-cloud\"\nconst DEFAULT_CLOUD_SESSION_REVALIDATE_AFTER_SECONDS = 15 * 60\n\ntype BetterAuthUserForCookie = {\n id: string\n name: string\n email: string\n emailVerified: boolean\n image?: string | null\n createdAt: Date\n updatedAt: Date\n}\n\ntype BetterAuthSessionForCookie = {\n id: string\n token: string\n userId: string\n expiresAt: Date\n createdAt: Date\n updatedAt: Date\n ipAddress?: string | null\n userAgent?: string | null\n}\n\nexport type CloudAdminProvisionedUser = BetterAuthUserForCookie\n\nexport type CloudAdminUserProvisioningInput = {\n db: ReturnType<typeof getDb>\n assertion: CloudAdminAssertion\n user: CloudAdminProvisionedUser\n isNewUser: boolean\n provider: {\n providerId: typeof VOYANT_CLOUD_PROVIDER_ID\n providerAccountId: string\n }\n}\n\nexport type CloudAdminUserProvisioningHandler = (\n input: CloudAdminUserProvisioningInput,\n) => Promise<void> | void\n\nexport function createVoyantCloudAdminAuthPlugin(\n options: VoyantCloudAdminAuthPluginOptions,\n): BetterAuthPlugin {\n return {\n id: \"voyant-cloud-admin-auth\",\n endpoints: {\n voyantCloudAdminAuthCallback: createAuthEndpoint(\n \"/cloud/callback\",\n { method: \"GET\", requireRequest: true },\n async (ctx) => {\n const callback = await verifyCloudAdminAuthCallback({\n requestUrl: ctx.request.url,\n cookieHeader: ctx.headers?.get(\"cookie\") ?? ctx.request.headers.get(\"cookie\"),\n cookieSecret: options.cookieSecret,\n secureCookie: options.secureStateCookie,\n })\n ctx.setHeader(\"Set-Cookie\", callback.clearCookie)\n\n if (!callback.ok) {\n const status = callback.error === \"cloud_error\" ? 401 : 400\n return ctx.json(\n {\n error: callback.error,\n ...(callback.cloudError ? { cloudError: callback.cloudError } : {}),\n },\n { status },\n )\n }\n\n let assertion: CloudAdminAssertion\n try {\n assertion = await exchangeCloudAdminAuthCode({\n code: callback.code,\n state: callback.state,\n config: options.exchange,\n ...(options.fetch ? { fetch: options.fetch } : {}),\n })\n } catch (error) {\n ctx.context.logger.error(\"[auth/cloud/callback] Exchange error\", error)\n return ctx.json({ error: \"Voyant Cloud auth exchange failed\" }, { status: 401 })\n }\n\n const user = await upsertVoyantCloudMirrorUser(options.db, assertion, {\n onUserProvisioning: options.onUserProvisioning,\n })\n const session = (await ctx.context.internalAdapter.createSession(\n user.id,\n )) as BetterAuthSessionForCookie\n await upsertVoyantCloudSessionLink(options.db, {\n assertion,\n session,\n revalidateAfterSeconds:\n options.revalidateAfterSeconds ?? DEFAULT_CLOUD_SESSION_REVALIDATE_AFTER_SECONDS,\n })\n await setSessionCookie(ctx, { session, user })\n\n throw ctx.redirect(callback.state.next)\n },\n ),\n },\n }\n}\n\nexport async function revalidateVoyantCloudAdminAuthSession({\n db,\n sessionId,\n config,\n fetch,\n now = new Date(),\n revalidateAfterSeconds = DEFAULT_CLOUD_SESSION_REVALIDATE_AFTER_SECONDS,\n}: VoyantCloudAdminSessionRevalidationInput): Promise<VoyantCloudAdminSessionRevalidationResult> {\n const [sessionLink] = await db\n .select()\n .from(cloudAuthSessionLinks)\n .where(eq(cloudAuthSessionLinks.sessionId, sessionId))\n .limit(1)\n\n if (!sessionLink || sessionLink.revokedAt) {\n return { ok: false, status: \"revoked\", reason: \"missing_or_revoked_session\" }\n }\n\n if (sessionLink.revalidateAfter > now) {\n return { ok: true, status: \"cached\" }\n }\n\n const revalidation = await revalidateCloudAdminAuthAccess({\n workosUserId: sessionLink.providerAccountId,\n config,\n ...(fetch ? { fetch } : {}),\n })\n\n if (revalidation.ok) {\n await markCloudAuthSessionRevalidated(db, {\n sessionId: sessionLink.sessionId,\n userId: sessionLink.userId,\n now,\n revalidateAfterSeconds,\n })\n // Refresh the cached RBAC scopes so a permission change applied while the\n // member is signed in takes effect at the next revalidation, not only at\n // next login (member-rbac-rfc, voyant#2085). `undefined` ⇒ older platform\n // didn't send scopes; leave the column as-is.\n if (revalidation.scopes !== undefined) {\n await db\n .update(cloudAuthUserLinks)\n .set({ scopes: revalidation.scopes, updatedAt: now })\n .where(eq(cloudAuthUserLinks.userId, sessionLink.userId))\n }\n return { ok: true, status: \"active\" }\n }\n\n await revokeCloudAuthUserAccess(db, {\n sessionId: sessionLink.sessionId,\n userId: sessionLink.userId,\n now,\n })\n\n return {\n ok: false,\n status: \"revoked\",\n reason: revalidation.reason,\n }\n}\n\nexport async function revalidateVoyantCloudAdminAuthUser({\n db,\n userId,\n config,\n fetch,\n now = new Date(),\n revalidateAfterSeconds = DEFAULT_CLOUD_SESSION_REVALIDATE_AFTER_SECONDS,\n}: VoyantCloudAdminUserRevalidationInput): Promise<VoyantCloudAdminUserRevalidationResult> {\n const [userLink] = await db\n .select()\n .from(cloudAuthUserLinks)\n .where(eq(cloudAuthUserLinks.userId, userId))\n .limit(1)\n\n if (!userLink || userLink.revokedAt) {\n return { ok: false, status: \"revoked\", reason: \"missing_or_revoked_user\" }\n }\n\n const nextRevalidationAt = userLink.lastRevalidatedAt\n ? new Date(userLink.lastRevalidatedAt.getTime() + revalidateAfterSeconds * 1000)\n : null\n if (nextRevalidationAt && nextRevalidationAt > now) {\n return { ok: true, status: \"cached\" }\n }\n\n const revalidation = await revalidateCloudAdminAuthAccess({\n workosUserId: userLink.providerAccountId,\n config,\n ...(fetch ? { fetch } : {}),\n })\n\n if (revalidation.ok) {\n await markCloudAuthUserRevalidated(db, {\n userId: userLink.userId,\n now,\n })\n return { ok: true, status: \"active\" }\n }\n\n await revokeCloudAuthUserAccess(db, {\n userId: userLink.userId,\n now,\n })\n\n return {\n ok: false,\n status: \"revoked\",\n reason: revalidation.reason,\n }\n}\n\nasync function upsertVoyantCloudMirrorUser(\n db: ReturnType<typeof getDb>,\n assertion: CloudAdminAssertion,\n options: {\n onUserProvisioning?: CloudAdminUserProvisioningHandler\n } = {},\n): Promise<BetterAuthUserForCookie> {\n const now = new Date()\n const providerAccountId = assertion.workosUserId\n const displayName = cloudAssertionDisplayName(assertion)\n const existingUser = await findCloudMirrorUser(\n db,\n providerAccountId,\n assertion.email,\n assertion.emailVerified,\n )\n const isNewUser = !existingUser\n const userId = existingUser?.id ?? crypto.randomUUID()\n\n if (existingUser) {\n await db\n .update(authUser)\n .set({\n name: displayName,\n email: assertion.email,\n emailVerified: assertion.emailVerified,\n image: assertion.image ?? null,\n updatedAt: now,\n })\n .where(eq(authUser.id, existingUser.id))\n } else {\n await db.insert(authUser).values({\n id: userId,\n name: displayName,\n email: assertion.email,\n emailVerified: assertion.emailVerified,\n image: assertion.image ?? null,\n createdAt: now,\n updatedAt: now,\n })\n }\n\n await db\n .insert(authAccount)\n .values({\n id: cloudAuthAccountId(providerAccountId),\n userId,\n accountId: providerAccountId,\n providerId: VOYANT_CLOUD_PROVIDER_ID,\n createdAt: now,\n updatedAt: now,\n })\n .onConflictDoUpdate({\n target: [authAccount.providerId, authAccount.accountId],\n set: {\n userId,\n updatedAt: now,\n },\n })\n\n await upsertVoyantCloudUserLink(db, userId, assertion, now)\n\n await provisionCurrentUserProfile(db, {\n userId,\n name: displayName,\n image: assertion.image,\n isSuperAdmin: await isFirstAuthUser(db),\n })\n\n const [user] = await db\n .select({\n id: authUser.id,\n name: authUser.name,\n email: authUser.email,\n emailVerified: authUser.emailVerified,\n image: authUser.image,\n createdAt: authUser.createdAt,\n updatedAt: authUser.updatedAt,\n })\n .from(authUser)\n .where(eq(authUser.id, userId))\n .limit(1)\n\n if (!user?.email) {\n throw new Error(\"Voyant Cloud auth mirror user was not provisioned\")\n }\n\n const provisionedUser = {\n id: user.id,\n name: user.name,\n email: user.email,\n emailVerified: user.emailVerified,\n image: user.image,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt,\n }\n\n await options.onUserProvisioning?.({\n db,\n assertion,\n user: provisionedUser,\n isNewUser,\n provider: {\n providerId: VOYANT_CLOUD_PROVIDER_ID,\n providerAccountId,\n },\n })\n\n return provisionedUser\n}\n\nasync function findCloudMirrorUser(\n db: ReturnType<typeof getDb>,\n providerAccountId: string,\n email?: string,\n emailVerified?: boolean,\n): Promise<{ id: string } | null> {\n const [linked] = await db\n .select({ id: authUser.id })\n .from(cloudAuthUserLinks)\n .innerJoin(authUser, eq(authUser.id, cloudAuthUserLinks.userId))\n .where(\n and(\n eq(cloudAuthUserLinks.providerId, VOYANT_CLOUD_PROVIDER_ID),\n eq(cloudAuthUserLinks.providerAccountId, providerAccountId),\n ),\n )\n .limit(1)\n\n if (linked) return linked\n\n const [account] = await db\n .select({ id: authUser.id })\n .from(authAccount)\n .innerJoin(authUser, eq(authUser.id, authAccount.userId))\n .where(\n and(\n eq(authAccount.providerId, VOYANT_CLOUD_PROVIDER_ID),\n eq(authAccount.accountId, providerAccountId),\n ),\n )\n .limit(1)\n\n if (account) return account\n if (!email || emailVerified !== true) return null\n\n const [emailUser] = await db\n .select({ id: authUser.id })\n .from(authUser)\n .where(eq(authUser.email, email))\n .limit(1)\n\n return emailUser ?? null\n}\n\nfunction cloudAuthUserLinkValues(\n userId: string,\n assertion: CloudAdminAssertion,\n now: Date,\n): typeof cloudAuthUserLinks.$inferInsert {\n return {\n userId,\n providerId: VOYANT_CLOUD_PROVIDER_ID,\n providerAccountId: assertion.workosUserId,\n deploymentId: assertion.deploymentId,\n platformOrganizationId: assertion.platformOrganizationId,\n workosOrganizationId: assertion.workosOrganizationId,\n membershipId: assertion.membershipId ?? null,\n roleSlug: assertion.roleSlug ?? null,\n roleName: assertion.roleName ?? null,\n surfaces: assertion.surfaces ?? [],\n // Member RBAC scope set from the assertion. `null` (not `[]`) when the\n // platform sent none, so resolveAuthRequest can tell \"no scopes claim\"\n // (derive from role / full-access fallback) from \"explicitly empty\".\n scopes: assertion.scopes ?? null,\n lastAssertionAt: new Date(assertion.iat * 1000),\n lastRevalidatedAt: now,\n revokedAt: null,\n updatedAt: now,\n }\n}\n\nasync function upsertVoyantCloudUserLink(\n db: ReturnType<typeof getDb>,\n userId: string,\n assertion: CloudAdminAssertion,\n now: Date,\n): Promise<void> {\n const values = cloudAuthUserLinkValues(userId, assertion, now)\n const [existingForUser] = await db\n .select({ userId: cloudAuthUserLinks.userId })\n .from(cloudAuthUserLinks)\n .where(eq(cloudAuthUserLinks.userId, userId))\n .limit(1)\n\n if (existingForUser) {\n await db\n .update(cloudAuthUserLinks)\n .set({\n ...values,\n userId,\n revokedAt: null,\n })\n .where(eq(cloudAuthUserLinks.userId, userId))\n return\n }\n\n await db\n .insert(cloudAuthUserLinks)\n .values(values)\n .onConflictDoUpdate({\n target: [cloudAuthUserLinks.providerId, cloudAuthUserLinks.providerAccountId],\n set: {\n ...values,\n userId,\n revokedAt: null,\n },\n })\n}\n\nasync function upsertVoyantCloudSessionLink(\n db: ReturnType<typeof getDb>,\n input: {\n assertion: CloudAdminAssertion\n session: BetterAuthSessionForCookie\n revalidateAfterSeconds: number\n },\n): Promise<void> {\n const now = new Date()\n await db.insert(cloudAuthSessionLinks).values({\n sessionId: input.session.id,\n userId: input.session.userId,\n providerId: VOYANT_CLOUD_PROVIDER_ID,\n providerAccountId: input.assertion.workosUserId,\n deploymentId: input.assertion.deploymentId,\n revalidateAfter: new Date(now.getTime() + input.revalidateAfterSeconds * 1000),\n lastRevalidatedAt: now,\n revokedAt: null,\n updatedAt: now,\n })\n}\n\nfunction cloudAssertionDisplayName(assertion: CloudAdminAssertion): string {\n const fromParts = [assertion.firstName, assertion.lastName]\n .map((part) => part?.trim())\n .filter((part): part is string => Boolean(part))\n .join(\" \")\n return assertion.name?.trim() || fromParts || assertion.email\n}\n\nfunction cloudAuthAccountId(providerAccountId: string): string {\n return `acc_${VOYANT_CLOUD_PROVIDER_ID}_${providerAccountId}`\n}\n"]}
|
|
@@ -34,6 +34,8 @@ export type VerifyCloudAdminAuthCallbackInput = {
|
|
|
34
34
|
requestUrl: string;
|
|
35
35
|
cookieHeader?: string | null;
|
|
36
36
|
cookieSecret: string;
|
|
37
|
+
/** Trusted external-scheme hint for deployments behind TLS termination. */
|
|
38
|
+
secureCookie?: boolean;
|
|
37
39
|
now?: Date;
|
|
38
40
|
};
|
|
39
41
|
export type VerifyCloudAdminAuthCallbackResult = {
|
|
@@ -49,6 +51,6 @@ export type VerifyCloudAdminAuthCallbackResult = {
|
|
|
49
51
|
clearCookie: string;
|
|
50
52
|
};
|
|
51
53
|
export declare function createCloudAdminAuthStart({ requestUrl, next, config, now, randomState, randomNonce, }: CreateCloudAdminAuthStartInput): Promise<CreateCloudAdminAuthStartResult>;
|
|
52
|
-
export declare function verifyCloudAdminAuthCallback({ requestUrl, cookieHeader, cookieSecret, now, }: VerifyCloudAdminAuthCallbackInput): Promise<VerifyCloudAdminAuthCallbackResult>;
|
|
54
|
+
export declare function verifyCloudAdminAuthCallback({ requestUrl, cookieHeader, cookieSecret, secureCookie, now, }: VerifyCloudAdminAuthCallbackInput): Promise<VerifyCloudAdminAuthCallbackResult>;
|
|
53
55
|
export declare function normalizeCloudAdminAuthNext(next: string | null | undefined, adminOrigin: string): string;
|
|
54
56
|
export declare function buildClearCloudAdminAuthStateCookie(secure: boolean, path?: string): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CLOUD_STATE_COOKIE_KEY_CONTEXT, deriveContextKey, } from "@voyant-travel/utils/session-claims";
|
|
1
2
|
import { base64UrlDecode, base64UrlEncode, isRecord, normalizeAbsoluteUrl, timingSafeEqual, } from "./utils.js";
|
|
2
3
|
export const VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE = "voyant-cloud-admin-auth";
|
|
3
4
|
const DEFAULT_STATE_TTL_SECONDS = 300;
|
|
@@ -33,16 +34,16 @@ export async function createCloudAdminAuthStart({ requestUrl, next, config, now
|
|
|
33
34
|
setCookie: await buildCloudAdminAuthStateCookie({
|
|
34
35
|
state,
|
|
35
36
|
secret: config.cookieSecret,
|
|
36
|
-
secure:
|
|
37
|
+
secure: new URL(state.redirectUri).protocol === "https:",
|
|
37
38
|
path: cloudAuthCookiePath(new URL(state.redirectUri).pathname),
|
|
38
39
|
maxAgeSeconds: ttlSeconds,
|
|
39
40
|
}),
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
|
-
export async function verifyCloudAdminAuthCallback({ requestUrl, cookieHeader, cookieSecret, now = new Date(), }) {
|
|
43
|
+
export async function verifyCloudAdminAuthCallback({ requestUrl, cookieHeader, cookieSecret, secureCookie, now = new Date(), }) {
|
|
43
44
|
assertUsableSecret(cookieSecret);
|
|
44
45
|
const url = new URL(requestUrl);
|
|
45
|
-
const clearCookie = buildClearCloudAdminAuthStateCookie(url.protocol === "https:", cloudAuthCookiePath(url.pathname));
|
|
46
|
+
const clearCookie = buildClearCloudAdminAuthStateCookie(secureCookie ?? url.protocol === "https:", cloudAuthCookiePath(url.pathname));
|
|
46
47
|
const cloudError = url.searchParams.get("error");
|
|
47
48
|
if (cloudError) {
|
|
48
49
|
return {
|
|
@@ -88,7 +89,7 @@ export function normalizeCloudAdminAuthNext(next, adminOrigin) {
|
|
|
88
89
|
return "/";
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
|
-
export function buildClearCloudAdminAuthStateCookie(secure, path = "/auth/cloud") {
|
|
92
|
+
export function buildClearCloudAdminAuthStateCookie(secure, path = "/auth/admin/cloud") {
|
|
92
93
|
return serializeCookie(VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE, "", {
|
|
93
94
|
httpOnly: true,
|
|
94
95
|
maxAge: 0,
|
|
@@ -155,15 +156,11 @@ function readCookie(cookieHeader, name) {
|
|
|
155
156
|
}
|
|
156
157
|
function cloudAuthCookiePath(pathname) {
|
|
157
158
|
const normalized = pathname.replace(/\/+$/, "") || "/";
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (
|
|
161
|
-
return normalized.slice(0, markerIndex + marker.length - 1) || "/auth/cloud";
|
|
162
|
-
}
|
|
163
|
-
if (normalized.endsWith("/auth/cloud")) {
|
|
159
|
+
if (normalized.endsWith("/callback"))
|
|
160
|
+
return normalized.slice(0, -"/callback".length) || "/";
|
|
161
|
+
if (normalized.endsWith("/cloud"))
|
|
164
162
|
return normalized;
|
|
165
|
-
|
|
166
|
-
return "/auth/cloud";
|
|
163
|
+
return "/auth/admin/cloud";
|
|
167
164
|
}
|
|
168
165
|
function serializeCookie(name, value, options) {
|
|
169
166
|
const parts = [
|
|
@@ -184,7 +181,8 @@ function randomToken() {
|
|
|
184
181
|
return base64UrlEncode(bytes);
|
|
185
182
|
}
|
|
186
183
|
async function hmac(value, secret) {
|
|
187
|
-
const
|
|
184
|
+
const stateCookieKey = await deriveContextKey(secret, CLOUD_STATE_COOKIE_KEY_CONTEXT);
|
|
185
|
+
const key = await globalThis.crypto.subtle.importKey("raw", new TextEncoder().encode(stateCookieKey), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
188
186
|
const signature = await globalThis.crypto.subtle.sign("HMAC", key, new TextEncoder().encode(value));
|
|
189
187
|
return base64UrlEncode(new Uint8Array(signature));
|
|
190
188
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/cloud-broker/state.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n base64UrlDecode,\n base64UrlEncode,\n isRecord,\n normalizeAbsoluteUrl,\n timingSafeEqual,\n} from \"./utils.js\"\n\nexport const VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE = \"voyant-cloud-admin-auth\"\n\nconst DEFAULT_STATE_TTL_SECONDS = 300\nconst RANDOM_BYTES = 32\n\nexport type CloudAdminAuthStartConfig = {\n cloudAuthStartUrl: string\n deploymentId: string\n adminCallbackUrl: string\n cookieSecret: string\n appId?: string | null\n environment?: string | null\n surface?: string | null\n stateTtlSeconds?: number\n}\n\nexport type CloudAdminAuthState = {\n state: string\n nonce: string\n deploymentId: string\n redirectUri: string\n next: string\n expiresAt: number\n}\n\nexport type CreateCloudAdminAuthStartInput = {\n requestUrl: string\n next?: string | null\n config: CloudAdminAuthStartConfig\n now?: Date\n randomState?: string\n randomNonce?: string\n}\n\nexport type CreateCloudAdminAuthStartResult = {\n redirectUrl: string\n state: CloudAdminAuthState\n setCookie: string\n}\n\nexport type VerifyCloudAdminAuthCallbackInput = {\n requestUrl: string\n cookieHeader?: string | null\n cookieSecret: string\n now?: Date\n}\n\nexport type VerifyCloudAdminAuthCallbackResult =\n | {\n ok: true\n code: string\n state: CloudAdminAuthState\n clearCookie: string\n }\n | {\n ok: false\n error: \"cloud_error\" | \"invalid_request\" | \"invalid_state\" | \"expired_state\"\n description?: string\n cloudError?: string\n clearCookie: string\n }\n\nexport async function createCloudAdminAuthStart({\n requestUrl,\n next,\n config,\n now = new Date(),\n randomState,\n randomNonce,\n}: CreateCloudAdminAuthStartInput): Promise<CreateCloudAdminAuthStartResult> {\n validateStartConfig(config)\n\n const parsedRequestUrl = new URL(requestUrl)\n const ttlSeconds = config.stateTtlSeconds ?? DEFAULT_STATE_TTL_SECONDS\n const state: CloudAdminAuthState = {\n state: randomState ?? randomToken(),\n nonce: randomNonce ?? randomToken(),\n deploymentId: config.deploymentId.trim(),\n redirectUri: normalizeAbsoluteUrl(config.adminCallbackUrl, \"adminCallbackUrl\"),\n next: normalizeCloudAdminAuthNext(next, parsedRequestUrl.origin),\n expiresAt: now.getTime() + ttlSeconds * 1000,\n }\n const redirectUrl = new URL(config.cloudAuthStartUrl)\n redirectUrl.searchParams.set(\"deployment_id\", state.deploymentId)\n redirectUrl.searchParams.set(\"redirect_uri\", state.redirectUri)\n redirectUrl.searchParams.set(\"state\", state.state)\n redirectUrl.searchParams.set(\"nonce\", state.nonce)\n redirectUrl.searchParams.set(\"surface\", config.surface?.trim() || \"admin\")\n redirectUrl.searchParams.set(\"next\", state.next)\n\n if (config.appId?.trim()) {\n redirectUrl.searchParams.set(\"app_id\", config.appId.trim())\n }\n if (config.environment?.trim()) {\n redirectUrl.searchParams.set(\"environment\", config.environment.trim())\n }\n\n return {\n redirectUrl: redirectUrl.toString(),\n state,\n setCookie: await buildCloudAdminAuthStateCookie({\n state,\n secret: config.cookieSecret,\n secure: parsedRequestUrl.protocol === \"https:\",\n path: cloudAuthCookiePath(new URL(state.redirectUri).pathname),\n maxAgeSeconds: ttlSeconds,\n }),\n }\n}\n\nexport async function verifyCloudAdminAuthCallback({\n requestUrl,\n cookieHeader,\n cookieSecret,\n now = new Date(),\n}: VerifyCloudAdminAuthCallbackInput): Promise<VerifyCloudAdminAuthCallbackResult> {\n assertUsableSecret(cookieSecret)\n\n const url = new URL(requestUrl)\n const clearCookie = buildClearCloudAdminAuthStateCookie(\n url.protocol === \"https:\",\n cloudAuthCookiePath(url.pathname),\n )\n const cloudError = url.searchParams.get(\"error\")\n if (cloudError) {\n return {\n ok: false,\n error: \"cloud_error\",\n cloudError,\n description: url.searchParams.get(\"error_description\") ?? undefined,\n clearCookie,\n }\n }\n\n const stateParam = url.searchParams.get(\"state\")\n const code = url.searchParams.get(\"code\")\n if (!stateParam || !code) {\n return { ok: false, error: \"invalid_request\", clearCookie }\n }\n\n const stateCookie = readCookie(cookieHeader, VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE)\n if (!stateCookie) {\n return { ok: false, error: \"invalid_state\", clearCookie }\n }\n\n const state = await verifyCloudAdminAuthStateCookie(stateCookie, cookieSecret)\n if (!state || state.state !== stateParam) {\n return { ok: false, error: \"invalid_state\", clearCookie }\n }\n\n if (state.expiresAt <= now.getTime()) {\n return { ok: false, error: \"expired_state\", clearCookie }\n }\n\n return { ok: true, code, state, clearCookie }\n}\n\nexport function normalizeCloudAdminAuthNext(\n next: string | null | undefined,\n adminOrigin: string,\n): string {\n const value = next?.trim()\n if (!value) return \"/\"\n\n if (value.startsWith(\"/\") && !value.startsWith(\"//\")) {\n return value\n }\n\n try {\n const url = new URL(value)\n if (url.origin !== adminOrigin) return \"/\"\n return `${url.pathname}${url.search}${url.hash}` || \"/\"\n } catch {\n return \"/\"\n }\n}\n\nexport function buildClearCloudAdminAuthStateCookie(secure: boolean, path = \"/auth/cloud\"): string {\n return serializeCookie(VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE, \"\", {\n httpOnly: true,\n maxAge: 0,\n path,\n sameSite: \"Lax\",\n secure,\n })\n}\n\nasync function buildCloudAdminAuthStateCookie({\n state,\n secret,\n secure,\n path,\n maxAgeSeconds,\n}: {\n state: CloudAdminAuthState\n secret: string\n secure: boolean\n path: string\n maxAgeSeconds: number\n}): Promise<string> {\n const payload = base64UrlEncode(new TextEncoder().encode(JSON.stringify(state)))\n const signature = await hmac(payload, secret)\n\n return serializeCookie(VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE, `${payload}.${signature}`, {\n httpOnly: true,\n maxAge: maxAgeSeconds,\n path,\n sameSite: \"Lax\",\n secure,\n })\n}\n\nasync function verifyCloudAdminAuthStateCookie(\n cookieValue: string,\n secret: string,\n): Promise<CloudAdminAuthState | null> {\n const [payload, signature, ...rest] = cookieValue.split(\".\")\n if (!payload || !signature || rest.length > 0) return null\n\n const expectedSignature = await hmac(payload, secret)\n if (!timingSafeEqual(signature, expectedSignature)) return null\n\n const parsed = JSON.parse(new TextDecoder().decode(base64UrlDecode(payload))) as unknown\n if (!isCloudAdminAuthState(parsed)) return null\n return parsed\n}\n\nfunction validateStartConfig(config: CloudAdminAuthStartConfig): void {\n assertUsableSecret(config.cookieSecret)\n normalizeAbsoluteUrl(config.cloudAuthStartUrl, \"cloudAuthStartUrl\")\n normalizeAbsoluteUrl(config.adminCallbackUrl, \"adminCallbackUrl\")\n\n if (!config.deploymentId.trim()) {\n throw new Error(\"Voyant Cloud auth requires deploymentId\")\n }\n}\n\nfunction assertUsableSecret(secret: string): void {\n if (!secret || secret.length < 32) {\n throw new Error(\"Voyant Cloud auth requires a cookie secret with at least 32 characters\")\n }\n}\n\nfunction isCloudAdminAuthState(value: unknown): value is CloudAdminAuthState {\n if (!isRecord(value)) return false\n return (\n typeof value.state === \"string\" &&\n typeof value.nonce === \"string\" &&\n typeof value.deploymentId === \"string\" &&\n typeof value.redirectUri === \"string\" &&\n typeof value.next === \"string\" &&\n typeof value.expiresAt === \"number\"\n )\n}\n\nfunction readCookie(cookieHeader: string | null | undefined, name: string): string | null {\n if (!cookieHeader) return null\n\n for (const part of cookieHeader.split(\";\")) {\n const [rawName, ...rawValue] = part.trim().split(\"=\")\n if (rawName === name) return rawValue.join(\"=\")\n }\n\n return null\n}\n\nfunction cloudAuthCookiePath(pathname: string): string {\n const normalized = pathname.replace(/\\/+$/, \"\") || \"/\"\n const marker = \"/auth/cloud/\"\n const markerIndex = normalized.indexOf(marker)\n if (markerIndex >= 0) {\n return normalized.slice(0, markerIndex + marker.length - 1) || \"/auth/cloud\"\n }\n\n if (normalized.endsWith(\"/auth/cloud\")) {\n return normalized\n }\n\n return \"/auth/cloud\"\n}\n\nfunction serializeCookie(\n name: string,\n value: string,\n options: {\n httpOnly: boolean\n maxAge: number\n path: string\n sameSite: \"Lax\" | \"Strict\" | \"None\"\n secure: boolean\n },\n): string {\n const parts = [\n `${name}=${value}`,\n `Max-Age=${options.maxAge}`,\n `Path=${options.path}`,\n `SameSite=${options.sameSite}`,\n ]\n\n if (options.httpOnly) parts.push(\"HttpOnly\")\n if (options.secure) parts.push(\"Secure\")\n\n return parts.join(\"; \")\n}\n\nfunction randomToken(): string {\n const bytes = new Uint8Array(RANDOM_BYTES)\n globalThis.crypto.getRandomValues(bytes)\n return base64UrlEncode(bytes)\n}\n\nasync function hmac(value: string, secret: string): Promise<string> {\n const key = await globalThis.crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n )\n const signature = await globalThis.crypto.subtle.sign(\n \"HMAC\",\n key,\n new TextEncoder().encode(value),\n )\n return base64UrlEncode(new Uint8Array(signature))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/cloud-broker/state.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CLOUD_STATE_COOKIE_KEY_CONTEXT,\n deriveContextKey,\n} from \"@voyant-travel/utils/session-claims\"\nimport {\n base64UrlDecode,\n base64UrlEncode,\n isRecord,\n normalizeAbsoluteUrl,\n timingSafeEqual,\n} from \"./utils.js\"\n\nexport const VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE = \"voyant-cloud-admin-auth\"\n\nconst DEFAULT_STATE_TTL_SECONDS = 300\nconst RANDOM_BYTES = 32\n\nexport type CloudAdminAuthStartConfig = {\n cloudAuthStartUrl: string\n deploymentId: string\n adminCallbackUrl: string\n cookieSecret: string\n appId?: string | null\n environment?: string | null\n surface?: string | null\n stateTtlSeconds?: number\n}\n\nexport type CloudAdminAuthState = {\n state: string\n nonce: string\n deploymentId: string\n redirectUri: string\n next: string\n expiresAt: number\n}\n\nexport type CreateCloudAdminAuthStartInput = {\n requestUrl: string\n next?: string | null\n config: CloudAdminAuthStartConfig\n now?: Date\n randomState?: string\n randomNonce?: string\n}\n\nexport type CreateCloudAdminAuthStartResult = {\n redirectUrl: string\n state: CloudAdminAuthState\n setCookie: string\n}\n\nexport type VerifyCloudAdminAuthCallbackInput = {\n requestUrl: string\n cookieHeader?: string | null\n cookieSecret: string\n /** Trusted external-scheme hint for deployments behind TLS termination. */\n secureCookie?: boolean\n now?: Date\n}\n\nexport type VerifyCloudAdminAuthCallbackResult =\n | {\n ok: true\n code: string\n state: CloudAdminAuthState\n clearCookie: string\n }\n | {\n ok: false\n error: \"cloud_error\" | \"invalid_request\" | \"invalid_state\" | \"expired_state\"\n description?: string\n cloudError?: string\n clearCookie: string\n }\n\nexport async function createCloudAdminAuthStart({\n requestUrl,\n next,\n config,\n now = new Date(),\n randomState,\n randomNonce,\n}: CreateCloudAdminAuthStartInput): Promise<CreateCloudAdminAuthStartResult> {\n validateStartConfig(config)\n\n const parsedRequestUrl = new URL(requestUrl)\n const ttlSeconds = config.stateTtlSeconds ?? DEFAULT_STATE_TTL_SECONDS\n const state: CloudAdminAuthState = {\n state: randomState ?? randomToken(),\n nonce: randomNonce ?? randomToken(),\n deploymentId: config.deploymentId.trim(),\n redirectUri: normalizeAbsoluteUrl(config.adminCallbackUrl, \"adminCallbackUrl\"),\n next: normalizeCloudAdminAuthNext(next, parsedRequestUrl.origin),\n expiresAt: now.getTime() + ttlSeconds * 1000,\n }\n const redirectUrl = new URL(config.cloudAuthStartUrl)\n redirectUrl.searchParams.set(\"deployment_id\", state.deploymentId)\n redirectUrl.searchParams.set(\"redirect_uri\", state.redirectUri)\n redirectUrl.searchParams.set(\"state\", state.state)\n redirectUrl.searchParams.set(\"nonce\", state.nonce)\n redirectUrl.searchParams.set(\"surface\", config.surface?.trim() || \"admin\")\n redirectUrl.searchParams.set(\"next\", state.next)\n\n if (config.appId?.trim()) {\n redirectUrl.searchParams.set(\"app_id\", config.appId.trim())\n }\n if (config.environment?.trim()) {\n redirectUrl.searchParams.set(\"environment\", config.environment.trim())\n }\n\n return {\n redirectUrl: redirectUrl.toString(),\n state,\n setCookie: await buildCloudAdminAuthStateCookie({\n state,\n secret: config.cookieSecret,\n secure: new URL(state.redirectUri).protocol === \"https:\",\n path: cloudAuthCookiePath(new URL(state.redirectUri).pathname),\n maxAgeSeconds: ttlSeconds,\n }),\n }\n}\n\nexport async function verifyCloudAdminAuthCallback({\n requestUrl,\n cookieHeader,\n cookieSecret,\n secureCookie,\n now = new Date(),\n}: VerifyCloudAdminAuthCallbackInput): Promise<VerifyCloudAdminAuthCallbackResult> {\n assertUsableSecret(cookieSecret)\n\n const url = new URL(requestUrl)\n const clearCookie = buildClearCloudAdminAuthStateCookie(\n secureCookie ?? url.protocol === \"https:\",\n cloudAuthCookiePath(url.pathname),\n )\n const cloudError = url.searchParams.get(\"error\")\n if (cloudError) {\n return {\n ok: false,\n error: \"cloud_error\",\n cloudError,\n description: url.searchParams.get(\"error_description\") ?? undefined,\n clearCookie,\n }\n }\n\n const stateParam = url.searchParams.get(\"state\")\n const code = url.searchParams.get(\"code\")\n if (!stateParam || !code) {\n return { ok: false, error: \"invalid_request\", clearCookie }\n }\n\n const stateCookie = readCookie(cookieHeader, VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE)\n if (!stateCookie) {\n return { ok: false, error: \"invalid_state\", clearCookie }\n }\n\n const state = await verifyCloudAdminAuthStateCookie(stateCookie, cookieSecret)\n if (!state || state.state !== stateParam) {\n return { ok: false, error: \"invalid_state\", clearCookie }\n }\n\n if (state.expiresAt <= now.getTime()) {\n return { ok: false, error: \"expired_state\", clearCookie }\n }\n\n return { ok: true, code, state, clearCookie }\n}\n\nexport function normalizeCloudAdminAuthNext(\n next: string | null | undefined,\n adminOrigin: string,\n): string {\n const value = next?.trim()\n if (!value) return \"/\"\n\n if (value.startsWith(\"/\") && !value.startsWith(\"//\")) {\n return value\n }\n\n try {\n const url = new URL(value)\n if (url.origin !== adminOrigin) return \"/\"\n return `${url.pathname}${url.search}${url.hash}` || \"/\"\n } catch {\n return \"/\"\n }\n}\n\nexport function buildClearCloudAdminAuthStateCookie(\n secure: boolean,\n path = \"/auth/admin/cloud\",\n): string {\n return serializeCookie(VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE, \"\", {\n httpOnly: true,\n maxAge: 0,\n path,\n sameSite: \"Lax\",\n secure,\n })\n}\n\nasync function buildCloudAdminAuthStateCookie({\n state,\n secret,\n secure,\n path,\n maxAgeSeconds,\n}: {\n state: CloudAdminAuthState\n secret: string\n secure: boolean\n path: string\n maxAgeSeconds: number\n}): Promise<string> {\n const payload = base64UrlEncode(new TextEncoder().encode(JSON.stringify(state)))\n const signature = await hmac(payload, secret)\n\n return serializeCookie(VOYANT_CLOUD_ADMIN_AUTH_STATE_COOKIE, `${payload}.${signature}`, {\n httpOnly: true,\n maxAge: maxAgeSeconds,\n path,\n sameSite: \"Lax\",\n secure,\n })\n}\n\nasync function verifyCloudAdminAuthStateCookie(\n cookieValue: string,\n secret: string,\n): Promise<CloudAdminAuthState | null> {\n const [payload, signature, ...rest] = cookieValue.split(\".\")\n if (!payload || !signature || rest.length > 0) return null\n\n const expectedSignature = await hmac(payload, secret)\n if (!timingSafeEqual(signature, expectedSignature)) return null\n\n const parsed = JSON.parse(new TextDecoder().decode(base64UrlDecode(payload))) as unknown\n if (!isCloudAdminAuthState(parsed)) return null\n return parsed\n}\n\nfunction validateStartConfig(config: CloudAdminAuthStartConfig): void {\n assertUsableSecret(config.cookieSecret)\n normalizeAbsoluteUrl(config.cloudAuthStartUrl, \"cloudAuthStartUrl\")\n normalizeAbsoluteUrl(config.adminCallbackUrl, \"adminCallbackUrl\")\n\n if (!config.deploymentId.trim()) {\n throw new Error(\"Voyant Cloud auth requires deploymentId\")\n }\n}\n\nfunction assertUsableSecret(secret: string): void {\n if (!secret || secret.length < 32) {\n throw new Error(\"Voyant Cloud auth requires a cookie secret with at least 32 characters\")\n }\n}\n\nfunction isCloudAdminAuthState(value: unknown): value is CloudAdminAuthState {\n if (!isRecord(value)) return false\n return (\n typeof value.state === \"string\" &&\n typeof value.nonce === \"string\" &&\n typeof value.deploymentId === \"string\" &&\n typeof value.redirectUri === \"string\" &&\n typeof value.next === \"string\" &&\n typeof value.expiresAt === \"number\"\n )\n}\n\nfunction readCookie(cookieHeader: string | null | undefined, name: string): string | null {\n if (!cookieHeader) return null\n\n for (const part of cookieHeader.split(\";\")) {\n const [rawName, ...rawValue] = part.trim().split(\"=\")\n if (rawName === name) return rawValue.join(\"=\")\n }\n\n return null\n}\n\nfunction cloudAuthCookiePath(pathname: string): string {\n const normalized = pathname.replace(/\\/+$/, \"\") || \"/\"\n if (normalized.endsWith(\"/callback\")) return normalized.slice(0, -\"/callback\".length) || \"/\"\n if (normalized.endsWith(\"/cloud\")) return normalized\n\n return \"/auth/admin/cloud\"\n}\n\nfunction serializeCookie(\n name: string,\n value: string,\n options: {\n httpOnly: boolean\n maxAge: number\n path: string\n sameSite: \"Lax\" | \"Strict\" | \"None\"\n secure: boolean\n },\n): string {\n const parts = [\n `${name}=${value}`,\n `Max-Age=${options.maxAge}`,\n `Path=${options.path}`,\n `SameSite=${options.sameSite}`,\n ]\n\n if (options.httpOnly) parts.push(\"HttpOnly\")\n if (options.secure) parts.push(\"Secure\")\n\n return parts.join(\"; \")\n}\n\nfunction randomToken(): string {\n const bytes = new Uint8Array(RANDOM_BYTES)\n globalThis.crypto.getRandomValues(bytes)\n return base64UrlEncode(bytes)\n}\n\nasync function hmac(value: string, secret: string): Promise<string> {\n const stateCookieKey = await deriveContextKey(secret, CLOUD_STATE_COOKIE_KEY_CONTEXT)\n const key = await globalThis.crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(stateCookieKey),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n )\n const signature = await globalThis.crypto.subtle.sign(\n \"HMAC\",\n key,\n new TextEncoder().encode(value),\n )\n return base64UrlEncode(new Uint8Array(signature))\n}\n"]}
|
package/dist/edge.js
CHANGED
|
@@ -82,7 +82,7 @@ export async function getAuthContextFromHeaders(headers, options = {}) {
|
|
|
82
82
|
? [options.cookieName]
|
|
83
83
|
: realm === "customer"
|
|
84
84
|
? ["voyant-customer.session_token"]
|
|
85
|
-
: ["voyant-admin.session_token"
|
|
85
|
+
: ["voyant-admin.session_token"];
|
|
86
86
|
const rawCookie = getRawCookieValue(headers, cookieNames);
|
|
87
87
|
if (!rawCookie)
|
|
88
88
|
return EMPTY;
|
package/dist/edge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edge.js","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"edge.js","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Edge-compatible session check for Next.js middleware.\n *\n * Does NOT import better-auth (which uses eval / new Function via\n * createTelemetry, incompatible with Edge Runtime).\n *\n * Instead it reads the session cookie, unsigns it using the same\n * HMAC-SHA256 scheme that better-call uses, and queries better-auth's\n * `session` + `user` tables directly via the Neon HTTP drizzle adapter.\n *\n * With the identity table collapse, authUser.id IS the userId —\n * no extra identity lookup required.\n */\n\nimport { getDb } from \"@voyant-travel/db\"\nimport {\n authSession,\n authUser,\n customerAuthSession,\n customerAuthUser,\n} from \"@voyant-travel/db/schema/iam\"\nimport { and, eq, gt } from \"drizzle-orm\"\n\nexport interface EdgeAuthRealmOptions {\n realm?: \"admin\" | \"customer\"\n cookieName?: string\n secret?: string\n}\n\nfunction getAuthSecret(realm: \"admin\" | \"customer\"): string {\n if (realm === \"customer\") {\n return process.env.BETTER_AUTH_CUSTOMER_SECRET || \"\"\n }\n return process.env.BETTER_AUTH_ADMIN_SECRET || \"\"\n}\n\n/**\n * Unsign a Better Auth cookie value.\n *\n * Better Auth (via better-call) signs cookies as:\n * encodeURIComponent(value + \".\" + base64(HMAC-SHA256(value, secret)))\n *\n * This function reverses that: URL-decode, split at last \".\",\n * verify the HMAC-SHA256 signature, and return the raw value.\n */\nasync function unsignCookie(rawCookieValue: string, secret: string): Promise<string | null> {\n const decoded = decodeURIComponent(rawCookieValue)\n\n const lastDot = decoded.lastIndexOf(\".\")\n if (lastDot < 1) return null\n\n const value = decoded.substring(0, lastDot)\n const signature = decoded.substring(lastDot + 1)\n\n // Better Auth signatures are 44-char base64 strings ending with \"=\"\n if (signature.length !== 44 || !signature.endsWith(\"=\")) return null\n\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"verify\"],\n )\n\n const sigBinStr = atob(signature)\n const sigBytes = new Uint8Array(sigBinStr.length)\n for (let i = 0; i < sigBinStr.length; i++) sigBytes[i] = sigBinStr.charCodeAt(i)\n\n const valid = await crypto.subtle.verify(\"HMAC\", key, sigBytes, encoder.encode(value))\n return valid ? value : null\n}\n\nfunction getRawCookieValue(headers: Headers, cookieNames: readonly string[]): string | null {\n const cookieHeader = headers.get(\"cookie\")\n if (!cookieHeader) return null\n\n for (const part of cookieHeader.split(\";\")) {\n const trimmed = part.trim()\n const eqIdx = trimmed.indexOf(\"=\")\n if (eqIdx === -1) continue\n const name = trimmed.slice(0, eqIdx).trim()\n if (cookieNames.includes(name)) {\n return trimmed.slice(eqIdx + 1).trim() || null\n }\n }\n return null\n}\n\ntype AuthContext = {\n userId: string | null\n email: string | null\n sessionId: string | null\n}\n\nconst EMPTY: AuthContext = {\n userId: null,\n email: null,\n sessionId: null,\n}\n\n/**\n * Lightweight edge-compatible session check.\n * Safe to call from Next.js middleware.\n *\n * Returns `userId` which is Better Auth's `user.id` — the single\n * canonical user identifier after the identity table collapse.\n */\nexport async function getAuthContextFromHeaders(\n headers: Headers,\n options: EdgeAuthRealmOptions = {},\n): Promise<AuthContext> {\n const realm = options.realm ?? \"admin\"\n const cookieNames = options.cookieName\n ? [options.cookieName]\n : realm === \"customer\"\n ? [\"voyant-customer.session_token\"]\n : [\"voyant-admin.session_token\"]\n const rawCookie = getRawCookieValue(headers, cookieNames)\n if (!rawCookie) return EMPTY\n\n const secret = options.secret ?? getAuthSecret(realm)\n if (!secret) return EMPTY\n\n const token = await unsignCookie(rawCookie, secret)\n if (!token) return EMPTY\n\n const db = getDb(\"edge\")\n\n const [row] =\n realm === \"customer\"\n ? await db\n .select({\n sessionId: customerAuthSession.id,\n userId: customerAuthSession.userId,\n email: customerAuthUser.email,\n })\n .from(customerAuthSession)\n .innerJoin(customerAuthUser, eq(customerAuthUser.id, customerAuthSession.userId))\n .where(\n and(\n eq(customerAuthSession.token, token),\n gt(customerAuthSession.expiresAt, new Date()),\n ),\n )\n .limit(1)\n : await db\n .select({\n sessionId: authSession.id,\n userId: authSession.userId,\n email: authUser.email,\n })\n .from(authSession)\n .innerJoin(authUser, eq(authUser.id, authSession.userId))\n .where(and(eq(authSession.token, token), gt(authSession.expiresAt, new Date())))\n .limit(1)\n\n if (!row) return EMPTY\n\n return {\n userId: row.userId,\n email: row.email,\n sessionId: row.sessionId,\n }\n}\n"]}
|
package/dist/node-runtime.d.ts
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* agent-quality: file-size exception -- Auth handler keeps local and Voyant Cloud auth flows co-located until the route surface is split by auth mode.
|
|
5
5
|
*
|
|
6
|
-
* Mounts Better Auth at /auth/*
|
|
6
|
+
* Mounts Better Auth at /auth/admin/* and /auth/customer/*.
|
|
7
7
|
* Same-origin — no CORS needed. Session cookies work naturally.
|
|
8
8
|
*
|
|
9
9
|
* Also provides /auth/status (user provisioning) and /auth/me (user info).
|
|
10
10
|
*/
|
|
11
11
|
import { type NodeDatabaseEnv } from "@voyant-travel/db/runtime";
|
|
12
12
|
import { type SelectApikey } from "@voyant-travel/db/schema/iam";
|
|
13
|
-
import type { Reporter, VoyantDb,
|
|
13
|
+
import type { Reporter, VoyantDb, VoyantResolvedSessionAuthContext } from "@voyant-travel/hono";
|
|
14
14
|
import type { AccessCatalog } from "@voyant-travel/types/api-keys";
|
|
15
15
|
import { Hono } from "hono";
|
|
16
16
|
import { type CreateBetterAuthOptions, type CustomerAuthMethods } from "./server.js";
|
|
@@ -24,7 +24,8 @@ export interface OperatorAuthNodeEnv extends NodeDatabaseEnv {
|
|
|
24
24
|
CORS_ALLOWLIST?: string;
|
|
25
25
|
DASH_BASE_URL?: string;
|
|
26
26
|
EMAIL_FROM?: string;
|
|
27
|
-
|
|
27
|
+
SESSION_CLAIMS_ADMIN_SECRET: string;
|
|
28
|
+
SESSION_CLAIMS_CUSTOMER_SECRET?: string;
|
|
28
29
|
VOYANT_ADMIN_AUTH_MODE?: string;
|
|
29
30
|
VOYANT_CUSTOMER_AUTH_MODE?: string;
|
|
30
31
|
VOYANT_CUSTOMER_AUTH_CONFIG_JSON?: string;
|
|
@@ -133,7 +134,7 @@ export declare function createOperatorAuthNodeRuntime<Env extends OperatorAuthNo
|
|
|
133
134
|
getBootstrapStatusForRequest: (request: Request, env: Env) => Promise<OperatorAuthBootstrapStatus>;
|
|
134
135
|
getCurrentUserForRequest: (request: Request, env: Env) => Promise<OperatorCurrentUser | null>;
|
|
135
136
|
hasAuthPermission: (request: Request, env: Env) => Promise<boolean>;
|
|
136
|
-
resolveAuthRequest: (request: Request, env: Env) => Promise<
|
|
137
|
+
resolveAuthRequest: (request: Request, env: Env) => Promise<VoyantResolvedSessionAuthContext | null>;
|
|
137
138
|
validateApiTokenAccess: (env: Env, db: VoyantDb, apiKey: SelectApikey) => Promise<boolean>;
|
|
138
139
|
};
|
|
139
140
|
export {};
|
package/dist/node-runtime.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* agent-quality: file-size exception -- Auth handler keeps local and Voyant Cloud auth flows co-located until the route surface is split by auth mode.
|
|
5
5
|
*
|
|
6
|
-
* Mounts Better Auth at /auth/*
|
|
6
|
+
* Mounts Better Auth at /auth/admin/* and /auth/customer/*.
|
|
7
7
|
* Same-origin — no CORS needed. Session cookies work naturally.
|
|
8
8
|
*
|
|
9
9
|
* Also provides /auth/status (user provisioning) and /auth/me (user info).
|
|
@@ -19,6 +19,15 @@ import { createVoyantCloudAdminAuthPlugin, revalidateVoyantCloudAdminAuthSession
|
|
|
19
19
|
import { buildClearCloudAdminAuthStateCookie, createCloudAdminAuthStart } from "./cloud-broker.js";
|
|
20
20
|
import { createAdminBetterAuth, createCustomerBetterAuth, handleApiTokenManagementRequest, handleOrganizationMembersRequest, } from "./server.js";
|
|
21
21
|
import { ensureCurrentUserProfile } from "./workspace.js";
|
|
22
|
+
function classifyOperatorApiAuthSurface(requestUrl) {
|
|
23
|
+
const pathname = new URL(requestUrl).pathname.replace(/\/+$/, "") || "/";
|
|
24
|
+
const normalized = pathname.startsWith("/api/") ? pathname.slice(4) : pathname;
|
|
25
|
+
if (normalized === "/v1/admin" || normalized.startsWith("/v1/admin/"))
|
|
26
|
+
return "admin";
|
|
27
|
+
if (normalized === "/v1/public" || normalized.startsWith("/v1/public/"))
|
|
28
|
+
return "customer";
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
22
31
|
/** Resolve Better Auth's standard cross-subdomain cookie policy for Node hosts. */
|
|
23
32
|
export function buildBetterAuthCookieAdvancedOptions(env) {
|
|
24
33
|
const domain = env.AUTH_COOKIE_DOMAIN?.trim();
|
|
@@ -186,7 +195,7 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
186
195
|
cloudAuthStartUrl,
|
|
187
196
|
deploymentId,
|
|
188
197
|
adminCallbackUrl: `${getPublicApiBaseUrl(env)}/auth/admin/cloud/callback`,
|
|
189
|
-
cookieSecret: env.
|
|
198
|
+
cookieSecret: env.SESSION_CLAIMS_ADMIN_SECRET,
|
|
190
199
|
appId: env.VOYANT_CLOUD_APP_ID?.trim() || undefined,
|
|
191
200
|
environment: env.VOYANT_CLOUD_ENVIRONMENT?.trim() || undefined,
|
|
192
201
|
};
|
|
@@ -387,7 +396,9 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
387
396
|
? [
|
|
388
397
|
createVoyantCloudAdminAuthPlugin({
|
|
389
398
|
db: cloudAuthDb,
|
|
390
|
-
cookieSecret: env.
|
|
399
|
+
cookieSecret: env.SESSION_CLAIMS_ADMIN_SECRET,
|
|
400
|
+
secureStateCookie: new URL(`${getPublicApiBaseUrl(env)}/auth/admin/cloud/callback`).protocol ===
|
|
401
|
+
"https:",
|
|
391
402
|
exchange: cloudAuthExchange,
|
|
392
403
|
}),
|
|
393
404
|
]
|
|
@@ -453,11 +464,6 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
453
464
|
},
|
|
454
465
|
});
|
|
455
466
|
}
|
|
456
|
-
function rewriteLegacyAdminAuthRequest(request) {
|
|
457
|
-
const url = new URL(request.url);
|
|
458
|
-
url.pathname = url.pathname.replace("/auth", "/auth/admin");
|
|
459
|
-
return new Request(url, request);
|
|
460
|
-
}
|
|
461
467
|
const FULL_ACCESS_SCOPES = ["*"];
|
|
462
468
|
function scopesForOperatorRole(role) {
|
|
463
469
|
const base = accessCatalogScopesForRole(role, runtimeOptions.accessCatalog);
|
|
@@ -503,7 +509,10 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
503
509
|
return profile?.permissions ?? FULL_ACCESS_SCOPES;
|
|
504
510
|
}
|
|
505
511
|
async function resolveAuthRequest(request, env) {
|
|
506
|
-
const
|
|
512
|
+
const surface = classifyOperatorApiAuthSurface(request.url);
|
|
513
|
+
if (!surface)
|
|
514
|
+
return null;
|
|
515
|
+
const customerSurface = surface === "customer";
|
|
507
516
|
if (customerSurface && env.VOYANT_CUSTOMER_AUTH_MODE?.trim() === "disabled") {
|
|
508
517
|
return null;
|
|
509
518
|
}
|
|
@@ -837,17 +846,15 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
837
846
|
const exchangeConfig = getCloudAuthExchangeConfig(c.env);
|
|
838
847
|
if (!exchangeConfig) {
|
|
839
848
|
const url = new URL(c.req.url);
|
|
849
|
+
const callbackUrl = getCloudAuthStartConfig(c.env)?.adminCallbackUrl;
|
|
840
850
|
return c.json({ error: "Voyant Cloud auth exchange is not configured yet" }, 501, {
|
|
841
|
-
"Set-Cookie": buildClearCloudAdminAuthStateCookie(url.protocol === "https:", url.pathname.replace(/\/callback$/, "") || "/auth/cloud"),
|
|
851
|
+
"Set-Cookie": buildClearCloudAdminAuthStateCookie(callbackUrl ? new URL(callbackUrl).protocol === "https:" : url.protocol === "https:", url.pathname.replace(/\/callback$/, "") || "/auth/admin/cloud"),
|
|
842
852
|
});
|
|
843
853
|
}
|
|
844
854
|
const { db, dispose } = openDatabase(c.env);
|
|
845
855
|
try {
|
|
846
856
|
const betterAuth = buildAdminBetterAuth(c.env, db);
|
|
847
|
-
|
|
848
|
-
? c.req.raw
|
|
849
|
-
: rewriteLegacyAdminAuthRequest(c.req.raw);
|
|
850
|
-
return await betterAuth.handler(request);
|
|
857
|
+
return await betterAuth.handler(c.req.raw);
|
|
851
858
|
}
|
|
852
859
|
catch (error) {
|
|
853
860
|
console.error("[auth/cloud/callback] Error:", error);
|
|
@@ -859,14 +866,14 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
859
866
|
}
|
|
860
867
|
auth.get("/auth/admin/cloud/start", handleCloudAuthStart);
|
|
861
868
|
auth.get("/auth/admin/cloud/callback", handleCloudAuthCallback);
|
|
862
|
-
/** @deprecated Use /auth/admin/cloud/*. */
|
|
863
|
-
auth.get("/auth/cloud/start", handleCloudAuthStart);
|
|
864
|
-
auth.get("/auth/cloud/callback", handleCloudAuthCallback);
|
|
865
869
|
auth.all("/auth/api-tokens", handleApiTokensFacade);
|
|
866
870
|
auth.all("/auth/api-tokens/:keyId", handleApiTokensFacade);
|
|
867
871
|
auth.all("/auth/api-tokens/:keyId/rotate", handleApiTokensFacade);
|
|
868
872
|
auth.get("/auth/organization/list-members", handleOrganizationMembersFacade);
|
|
869
873
|
auth.get("/auth/customer/status", async (c) => {
|
|
874
|
+
if (c.env.VOYANT_CUSTOMER_AUTH_MODE?.trim() === "disabled") {
|
|
875
|
+
return c.json({ authenticated: false, disabled: true });
|
|
876
|
+
}
|
|
870
877
|
const { db, dispose } = openDatabase(c.env);
|
|
871
878
|
try {
|
|
872
879
|
const betterAuth = buildCustomerBetterAuth(c.env, db, c.req.raw);
|
|
@@ -924,20 +931,6 @@ export function createOperatorAuthNodeRuntime(runtimeOptions) {
|
|
|
924
931
|
c.executionCtx.waitUntil(dispose());
|
|
925
932
|
}
|
|
926
933
|
});
|
|
927
|
-
/** @deprecated Admin Better Auth routes moved to /auth/admin/*. */
|
|
928
|
-
auth.all("/auth/*", async (c) => {
|
|
929
|
-
const rewritten = rewriteLegacyAdminAuthRequest(c.req.raw);
|
|
930
|
-
if (isVoyantCloudAuthMode(c.env) && !isCloudAllowedBetterAuthRoute(rewritten)) {
|
|
931
|
-
return localAuthDisabledResponse(c);
|
|
932
|
-
}
|
|
933
|
-
const { db, dispose } = openDatabase(c.env);
|
|
934
|
-
try {
|
|
935
|
-
return await buildAdminBetterAuth(c.env, db).handler(rewritten);
|
|
936
|
-
}
|
|
937
|
-
finally {
|
|
938
|
-
c.executionCtx.waitUntil(dispose());
|
|
939
|
-
}
|
|
940
|
-
});
|
|
941
934
|
return {
|
|
942
935
|
handler: auth,
|
|
943
936
|
getBootstrapStatusForRequest,
|