@ericminassian/platform 0.0.0-stage → 0.1.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/dist/app.d.ts +10 -0
- package/dist/app.js +23 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/cookies.d.ts +10 -0
- package/dist/auth/cookies.js +37 -0
- package/dist/auth/cookies.js.map +1 -0
- package/dist/auth/index.d.ts +22 -0
- package/dist/auth/index.js +217 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/pkce.d.ts +16 -0
- package/dist/auth/pkce.js +32 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/tokens.d.ts +17 -0
- package/dist/auth/tokens.js +142 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/bindings.d.ts +13 -0
- package/dist/bindings.js +32 -0
- package/dist/bindings.js.map +1 -0
- package/dist/clients.d.ts +38 -0
- package/dist/clients.js +65 -0
- package/dist/clients.js.map +1 -0
- package/dist/context.d.ts +31 -0
- package/dist/context.js +33 -0
- package/dist/context.js.map +1 -0
- package/dist/db.d.ts +97 -0
- package/dist/db.js +297 -0
- package/dist/db.js.map +1 -0
- package/dist/events.d.ts +9 -0
- package/dist/events.js +28 -0
- package/dist/events.js.map +1 -0
- package/dist/http.d.ts +14 -0
- package/dist/http.js +82 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/queue.d.ts +7 -0
- package/dist/queue.js +50 -0
- package/dist/queue.js.map +1 -0
- package/dist/secrets.d.ts +8 -0
- package/dist/secrets.js +42 -0
- package/dist/secrets.js.map +1 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.js +232 -0
- package/dist/server.js.map +1 -0
- package/dist/storage.d.ts +30 -0
- package/dist/storage.js +108 -0
- package/dist/storage.js.map +1 -0
- package/dist/telemetry.d.ts +42 -0
- package/dist/telemetry.js +218 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/testing.d.ts +31 -0
- package/dist/testing.js +54 -0
- package/dist/testing.js.map +1 -0
- package/dist/worker.d.ts +73 -0
- package/dist/worker.js +179 -0
- package/dist/worker.js.map +1 -0
- package/package.json +58 -3
- package/src/app.ts +32 -0
- package/src/auth/cookies.ts +39 -0
- package/src/auth/index.ts +246 -0
- package/src/auth/pkce.ts +43 -0
- package/src/auth/tokens.ts +174 -0
- package/src/bindings.ts +35 -0
- package/src/clients.ts +98 -0
- package/src/context.ts +56 -0
- package/src/db.ts +380 -0
- package/src/events.ts +42 -0
- package/src/http.ts +82 -0
- package/src/index.ts +40 -0
- package/src/queue.ts +67 -0
- package/src/secrets.ts +57 -0
- package/src/server.ts +274 -0
- package/src/storage.ts +143 -0
- package/src/telemetry.ts +260 -0
- package/src/testing.ts +73 -0
- package/src/worker.ts +291 -0
- package/README.md +0 -3
package/dist/app.d.ts
ADDED
package/dist/app.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getBindings } from './bindings.js';
|
|
2
|
+
/** Facts about the running app stage (lazy: read from bindings on access). */
|
|
3
|
+
export const app = {
|
|
4
|
+
get name() {
|
|
5
|
+
return getBindings().app;
|
|
6
|
+
},
|
|
7
|
+
get stage() {
|
|
8
|
+
return getBindings().stage;
|
|
9
|
+
},
|
|
10
|
+
get host() {
|
|
11
|
+
return getBindings().host;
|
|
12
|
+
},
|
|
13
|
+
get url() {
|
|
14
|
+
return getBindings().url;
|
|
15
|
+
},
|
|
16
|
+
get revision() {
|
|
17
|
+
return getBindings().revision;
|
|
18
|
+
},
|
|
19
|
+
get isLocal() {
|
|
20
|
+
return process.env.PLATFORM_LOCAL === '1';
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAW5C,8EAA8E;AAC9E,MAAM,CAAC,MAAM,GAAG,GAAY;IAC1B,IAAI,IAAI;QACN,OAAO,WAAW,EAAE,CAAC,GAAG,CAAC;IAC3B,CAAC;IACD,IAAI,KAAK;QACP,OAAO,WAAW,EAAE,CAAC,KAAK,CAAC;IAC7B,CAAC;IACD,IAAI,IAAI;QACN,OAAO,WAAW,EAAE,CAAC,IAAI,CAAC;IAC5B,CAAC;IACD,IAAI,GAAG;QACL,OAAO,WAAW,EAAE,CAAC,GAAG,CAAC;IAC3B,CAAC;IACD,IAAI,QAAQ;QACV,OAAO,WAAW,EAAE,CAAC,QAAQ,CAAC;IAChC,CAAC;IACD,IAAI,OAAO;QACT,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG,CAAC;IAC5C,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function parseCookies(header: string | null | undefined): Record<string, string>;
|
|
2
|
+
export interface CookieOptions {
|
|
3
|
+
maxAge?: number;
|
|
4
|
+
path?: string;
|
|
5
|
+
httpOnly?: boolean;
|
|
6
|
+
secure?: boolean;
|
|
7
|
+
sameSite?: 'Lax' | 'Strict' | 'None';
|
|
8
|
+
}
|
|
9
|
+
export declare function serializeCookie(name: string, value: string, opts?: CookieOptions): string;
|
|
10
|
+
export declare function clearCookie(name: string, opts?: Omit<CookieOptions, 'maxAge'>): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function parseCookies(header) {
|
|
2
|
+
const out = {};
|
|
3
|
+
if (!header)
|
|
4
|
+
return out;
|
|
5
|
+
for (const part of header.split(';')) {
|
|
6
|
+
const i = part.indexOf('=');
|
|
7
|
+
if (i <= 0)
|
|
8
|
+
continue;
|
|
9
|
+
const name = part.slice(0, i).trim();
|
|
10
|
+
const raw = part.slice(i + 1).trim();
|
|
11
|
+
if (!name || name in out)
|
|
12
|
+
continue;
|
|
13
|
+
try {
|
|
14
|
+
out[name] = decodeURIComponent(raw);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
out[name] = raw;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
export function serializeCookie(name, value, opts = {}) {
|
|
23
|
+
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
24
|
+
parts.push(`Path=${opts.path ?? '/'}`);
|
|
25
|
+
if (opts.maxAge !== undefined)
|
|
26
|
+
parts.push(`Max-Age=${Math.floor(opts.maxAge)}`);
|
|
27
|
+
if (opts.httpOnly ?? true)
|
|
28
|
+
parts.push('HttpOnly');
|
|
29
|
+
if (opts.secure)
|
|
30
|
+
parts.push('Secure');
|
|
31
|
+
parts.push(`SameSite=${opts.sameSite ?? 'Lax'}`);
|
|
32
|
+
return parts.join('; ');
|
|
33
|
+
}
|
|
34
|
+
export function clearCookie(name, opts = {}) {
|
|
35
|
+
return serializeCookie(name, '', { ...opts, maxAge: 0 });
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/auth/cookies.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,MAAiC;IAC5D,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG;YAAE,SAAS;QACnC,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAUD,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAa,EAAE,IAAI,GAAkB,EAAE;IACnF,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACvD,KAAK,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;IACvC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChF,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAClD,IAAI,IAAI,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAI,GAAkC,EAAE;IAChF,OAAO,eAAe,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Bindings, type PlatformUser } from '@ericminassian/platform-core';
|
|
2
|
+
import { type Tokens } from './tokens.ts';
|
|
3
|
+
export interface Auth {
|
|
4
|
+
/** The signed-in user for this request, or null. */
|
|
5
|
+
getUser(request: Request): Promise<PlatformUser | null>;
|
|
6
|
+
/** Like getUser but throws a 401 Response when signed out. */
|
|
7
|
+
requireUser(request: Request): Promise<PlatformUser>;
|
|
8
|
+
/** Path to start sign-in, returning to `redirect` afterwards. */
|
|
9
|
+
loginUrl(redirect?: string): string;
|
|
10
|
+
logoutUrl(redirect?: string): string;
|
|
11
|
+
}
|
|
12
|
+
export declare function sessionCookies(b: Bindings, tokens: Tokens): string[];
|
|
13
|
+
export declare function clearSessionCookies(b: Bindings): string[];
|
|
14
|
+
export declare function unauthorized(): Response;
|
|
15
|
+
export declare const auth: Auth;
|
|
16
|
+
/**
|
|
17
|
+
* `auth.required`: redirect signed-out browsers to sign-in and 401 everything
|
|
18
|
+
* else. Returns a Response to short-circuit, or null to continue.
|
|
19
|
+
*/
|
|
20
|
+
export declare function authGate(request: Request): Promise<Response | null>;
|
|
21
|
+
/** Handles the auth routes; returns null for other paths. */
|
|
22
|
+
export declare function handleAuthRoute(request: Request): Promise<Response | null>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { AUTH_COOKIES, AUTH_ROUTES, AUTH_SCOPES, } from '@ericminassian/platform-core';
|
|
2
|
+
import { getBindings } from '../bindings.js';
|
|
3
|
+
import { currentContext } from '../context.js';
|
|
4
|
+
import { log } from '../telemetry.js';
|
|
5
|
+
import { clearCookie, parseCookies, serializeCookie } from './cookies.js';
|
|
6
|
+
import { challengeFor, createVerifier, decodeFlow, encodeFlow, randomState, safeRedirect } from './pkce.js';
|
|
7
|
+
import { exchangeCode, isExpiredError, localSignIn, refreshTokens, verifyToken, } from './tokens.js';
|
|
8
|
+
const SESSION_MAX_AGE = 30 * 24 * 3600;
|
|
9
|
+
const FLOW_MAX_AGE = 600;
|
|
10
|
+
function authBindings() {
|
|
11
|
+
const a = getBindings().auth;
|
|
12
|
+
if (!a)
|
|
13
|
+
throw new Error('Auth is not enabled for this app. Add `"auth": true` to platform.json.');
|
|
14
|
+
return a;
|
|
15
|
+
}
|
|
16
|
+
function secure(b) {
|
|
17
|
+
return b.url.startsWith('https:');
|
|
18
|
+
}
|
|
19
|
+
export function sessionCookies(b, tokens) {
|
|
20
|
+
const opts = { maxAge: SESSION_MAX_AGE, secure: secure(b) };
|
|
21
|
+
const out = [serializeCookie(AUTH_COOKIES.id, tokens.idToken, opts)];
|
|
22
|
+
if (tokens.refreshToken)
|
|
23
|
+
out.push(serializeCookie(AUTH_COOKIES.refresh, tokens.refreshToken, opts));
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
export function clearSessionCookies(b) {
|
|
27
|
+
return [
|
|
28
|
+
clearCookie(AUTH_COOKIES.id, { secure: secure(b) }),
|
|
29
|
+
clearCookie(AUTH_COOKIES.refresh, { secure: secure(b) }),
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
function bearer(request) {
|
|
33
|
+
const h = request.headers.get('authorization');
|
|
34
|
+
const m = h ? /^Bearer\s+(.+)$/i.exec(h) : null;
|
|
35
|
+
return m?.[1];
|
|
36
|
+
}
|
|
37
|
+
async function resolveUser(request) {
|
|
38
|
+
const ctx = currentContext();
|
|
39
|
+
if (ctx && ctx.user !== undefined)
|
|
40
|
+
return ctx.user;
|
|
41
|
+
const b = getBindings();
|
|
42
|
+
const a = authBindings();
|
|
43
|
+
const cookies = parseCookies(request.headers.get('cookie'));
|
|
44
|
+
const idToken = cookies[AUTH_COOKIES.id] ?? bearer(request);
|
|
45
|
+
const refresh = cookies[AUTH_COOKIES.refresh];
|
|
46
|
+
let user = null;
|
|
47
|
+
let expired = !idToken;
|
|
48
|
+
if (idToken) {
|
|
49
|
+
try {
|
|
50
|
+
user = await verifyToken(a, idToken);
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
expired = isExpiredError(e);
|
|
54
|
+
if (!expired)
|
|
55
|
+
log.debug('auth: rejected session token', { reason: e.message });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!user && expired && refresh) {
|
|
59
|
+
try {
|
|
60
|
+
const tokens = await refreshTokens(a, refresh);
|
|
61
|
+
user = await verifyToken(a, tokens.idToken);
|
|
62
|
+
ctx?.cookies.push(...sessionCookies(b, tokens));
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
log.debug('auth: refresh failed', { reason: e.message });
|
|
66
|
+
ctx?.cookies.push(...clearSessionCookies(b));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (ctx)
|
|
70
|
+
ctx.user = user;
|
|
71
|
+
return user;
|
|
72
|
+
}
|
|
73
|
+
export function unauthorized() {
|
|
74
|
+
return Response.json({ error: { code: 'unauthorized', message: 'Sign in required', loginUrl: AUTH_ROUTES.login } }, { status: 401, headers: { 'cache-control': 'no-store' } });
|
|
75
|
+
}
|
|
76
|
+
export const auth = {
|
|
77
|
+
getUser: (request) => resolveUser(request),
|
|
78
|
+
async requireUser(request) {
|
|
79
|
+
const user = await resolveUser(request);
|
|
80
|
+
if (!user)
|
|
81
|
+
throw unauthorized();
|
|
82
|
+
return user;
|
|
83
|
+
},
|
|
84
|
+
loginUrl: (redirect = '/') => `${AUTH_ROUTES.login}?redirect=${encodeURIComponent(safeRedirect(redirect))}`,
|
|
85
|
+
logoutUrl: (redirect = '/') => `${AUTH_ROUTES.logout}?redirect=${encodeURIComponent(safeRedirect(redirect))}`,
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* `auth.required`: redirect signed-out browsers to sign-in and 401 everything
|
|
89
|
+
* else. Returns a Response to short-circuit, or null to continue.
|
|
90
|
+
*/
|
|
91
|
+
export async function authGate(request) {
|
|
92
|
+
const a = getBindings().auth;
|
|
93
|
+
if (!a?.required)
|
|
94
|
+
return null;
|
|
95
|
+
if (await resolveUser(request))
|
|
96
|
+
return null;
|
|
97
|
+
const url = new URL(request.url);
|
|
98
|
+
const wantsHtml = request.method === 'GET' && (request.headers.get('accept') ?? '').includes('text/html');
|
|
99
|
+
if (wantsHtml)
|
|
100
|
+
return redirect(auth.loginUrl(url.pathname + url.search));
|
|
101
|
+
return unauthorized();
|
|
102
|
+
}
|
|
103
|
+
function redirect(location, cookies = [], status = 302) {
|
|
104
|
+
const headers = new Headers({ location, 'cache-control': 'no-store' });
|
|
105
|
+
for (const c of cookies)
|
|
106
|
+
headers.append('set-cookie', c);
|
|
107
|
+
return new Response(null, { status, headers });
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------------------------------------------- /__platform/auth/*
|
|
110
|
+
/** Handles the auth routes; returns null for other paths. */
|
|
111
|
+
export async function handleAuthRoute(request) {
|
|
112
|
+
const url = new URL(request.url);
|
|
113
|
+
const route = Object.values(AUTH_ROUTES).find((p) => p === url.pathname);
|
|
114
|
+
if (!route)
|
|
115
|
+
return null;
|
|
116
|
+
const b = getBindings();
|
|
117
|
+
const a = b.auth;
|
|
118
|
+
if (!a)
|
|
119
|
+
return Response.json({ error: { code: 'not_found', message: 'Auth is not enabled' } }, { status: 404 });
|
|
120
|
+
if (route === AUTH_ROUTES.me) {
|
|
121
|
+
const user = await resolveUser(request);
|
|
122
|
+
return user ? Response.json(user, { headers: { 'cache-control': 'no-store' } }) : unauthorized();
|
|
123
|
+
}
|
|
124
|
+
if (route === AUTH_ROUTES.logout) {
|
|
125
|
+
const back = safeRedirect(url.searchParams.get('redirect'));
|
|
126
|
+
if (a.mode === 'local')
|
|
127
|
+
return redirect(back, clearSessionCookies(b));
|
|
128
|
+
const target = new URL(`${a.loginUrl.replace(/\/$/, '')}/logout`);
|
|
129
|
+
target.searchParams.set('client_id', a.clientId);
|
|
130
|
+
target.searchParams.set('logout_uri', `${b.url}/`);
|
|
131
|
+
return redirect(target.toString(), clearSessionCookies(b));
|
|
132
|
+
}
|
|
133
|
+
if (a.mode === 'local')
|
|
134
|
+
return localRoute(request, b, a, route);
|
|
135
|
+
const callbackUrl = `${b.url}${AUTH_ROUTES.callback}`;
|
|
136
|
+
if (route === AUTH_ROUTES.login) {
|
|
137
|
+
const verifier = createVerifier();
|
|
138
|
+
const state = randomState();
|
|
139
|
+
const flow = encodeFlow({ v: verifier, s: state, r: safeRedirect(url.searchParams.get('redirect')) });
|
|
140
|
+
const target = new URL(`${a.loginUrl.replace(/\/$/, '')}/oauth2/authorize`);
|
|
141
|
+
target.search = new URLSearchParams({
|
|
142
|
+
client_id: a.clientId,
|
|
143
|
+
response_type: 'code',
|
|
144
|
+
scope: AUTH_SCOPES.join(' '),
|
|
145
|
+
redirect_uri: callbackUrl,
|
|
146
|
+
state,
|
|
147
|
+
code_challenge: challengeFor(verifier),
|
|
148
|
+
code_challenge_method: 'S256',
|
|
149
|
+
}).toString();
|
|
150
|
+
return redirect(target.toString(), [
|
|
151
|
+
serializeCookie(AUTH_COOKIES.flow, flow, {
|
|
152
|
+
maxAge: FLOW_MAX_AGE,
|
|
153
|
+
secure: secure(b),
|
|
154
|
+
path: '/__platform/auth',
|
|
155
|
+
}),
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
// callback
|
|
159
|
+
const flow = decodeFlow(parseCookies(request.headers.get('cookie'))[AUTH_COOKIES.flow]);
|
|
160
|
+
const code = url.searchParams.get('code');
|
|
161
|
+
const clearFlow = clearCookie(AUTH_COOKIES.flow, { secure: secure(b), path: '/__platform/auth' });
|
|
162
|
+
if (url.searchParams.get('error')) {
|
|
163
|
+
return redirect(flow?.r ?? '/', [clearFlow]);
|
|
164
|
+
}
|
|
165
|
+
if (!flow || !code || url.searchParams.get('state') !== flow.s) {
|
|
166
|
+
return new Response('Sign-in expired or was tampered with. Please try again.', {
|
|
167
|
+
status: 400,
|
|
168
|
+
headers: { 'set-cookie': clearFlow, 'cache-control': 'no-store' },
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const tokens = await exchangeCode(a, code, flow.v, callbackUrl);
|
|
172
|
+
return redirect(flow.r, [...sessionCookies(b, tokens), clearFlow]);
|
|
173
|
+
}
|
|
174
|
+
async function localRoute(request, b, a, route) {
|
|
175
|
+
const url = new URL(request.url);
|
|
176
|
+
if (route === AUTH_ROUTES.login && request.method === 'POST') {
|
|
177
|
+
const form = await request.formData();
|
|
178
|
+
const email = String(form.get('email') ?? '')
|
|
179
|
+
.trim()
|
|
180
|
+
.toLowerCase();
|
|
181
|
+
const name = String(form.get('name') ?? '').trim();
|
|
182
|
+
const back = safeRedirect(String(form.get('redirect') ?? '/'));
|
|
183
|
+
if (!/^[^\s@]+@[^\s@]+$/.test(email))
|
|
184
|
+
return html(loginPage(b, back, 'Enter an email address.'), 400);
|
|
185
|
+
const tokens = await localSignIn(a, email, name || undefined);
|
|
186
|
+
return redirect(back, sessionCookies(b, tokens), 303);
|
|
187
|
+
}
|
|
188
|
+
if (route === AUTH_ROUTES.login)
|
|
189
|
+
return html(loginPage(b, safeRedirect(url.searchParams.get('redirect'))));
|
|
190
|
+
// No hosted UI locally; callback is never used.
|
|
191
|
+
return redirect('/');
|
|
192
|
+
}
|
|
193
|
+
function html(body, status = 200) {
|
|
194
|
+
return new Response(body, {
|
|
195
|
+
status,
|
|
196
|
+
headers: { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' },
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function escapeHtml(s) {
|
|
200
|
+
return s.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
201
|
+
}
|
|
202
|
+
function loginPage(b, back, error) {
|
|
203
|
+
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
204
|
+
<title>Sign in · ${escapeHtml(b.app)} (local)</title>
|
|
205
|
+
<style>body{font:16px system-ui,sans-serif;display:grid;place-items:center;min-height:100vh;margin:0;background:#f6f7f9;color:#111}
|
|
206
|
+
form{background:#fff;padding:2rem;border-radius:12px;box-shadow:0 1px 3px #0002;display:grid;gap:.75rem;min-width:18rem}
|
|
207
|
+
input,button{font:inherit;padding:.6rem .75rem;border-radius:8px;border:1px solid #ccd}button{background:#111;color:#fff;border:0;cursor:pointer}
|
|
208
|
+
small{color:#667}.err{color:#b00}</style></head><body>
|
|
209
|
+
<form method="post" action="${AUTH_ROUTES.login}">
|
|
210
|
+
<strong>Sign in to ${escapeHtml(b.app)}</strong><small>Local development: any email works. Users live in the AWS Emulator.</small>
|
|
211
|
+
${error ? `<span class="err">${escapeHtml(error)}</span>` : ''}
|
|
212
|
+
<input name="email" type="email" placeholder="you@example.com" required autofocus>
|
|
213
|
+
<input name="name" placeholder="Name (optional)">
|
|
214
|
+
<input type="hidden" name="redirect" value="${escapeHtml(back)}">
|
|
215
|
+
<button type="submit">Sign in</button></form></body></html>`;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,WAAW,EACX,WAAW,GAIZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC5G,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,EACX,aAAa,EAEb,WAAW,GACZ,MAAM,aAAa,CAAC;AAYrB,MAAM,eAAe,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACvC,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB,SAAS,YAAY;IACnB,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC,IAAI,CAAC;IAC7B,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAClG,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,MAAM,CAAC,CAAW;IACzB,OAAO,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,CAAW,EAAE,MAAc;IACxD,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,MAAM,GAAG,GAAG,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IACrE,IAAI,MAAM,CAAC,YAAY;QAAE,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;IACpG,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,CAAW;IAC7C,OAAO;QACL,WAAW,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,WAAW,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;KACzD,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,OAAgB;IAC9B,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC/C,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAgB;IACzC,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;IAC7B,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC;IACnD,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,GAAG,YAAY,EAAE,CAAC;IACzB,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,IAAI,GAAwB,IAAI,CAAC;IACrC,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC;IACvB,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YAC5B,IAAI,CAAC,OAAO;gBAAE,GAAG,CAAC,KAAK,CAAC,8BAA8B,EAAE,EAAE,MAAM,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAC/C,IAAI,GAAG,MAAM,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YAC5C,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,GAAG,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,MAAM,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACpE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,IAAI,GAAG;QAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IACzB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,CAAC,KAAK,EAAE,EAAE,EAC7F,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,EAAE,CAC1D,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAS;IACxB,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC;IAC1C,KAAK,CAAC,WAAW,CAAC,OAAO;QACvB,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI;YAAE,MAAM,YAAY,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,QAAQ,EAAE,CAAC,QAAQ,GAAG,GAAG,EAAE,EAAE,CAAC,GAAG,WAAW,CAAC,KAAK,aAAa,kBAAkB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE;IAC3G,SAAS,EAAE,CAAC,QAAQ,GAAG,GAAG,EAAE,EAAE,CAC5B,GAAG,WAAW,CAAC,MAAM,aAAa,kBAAkB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE;CACjF,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAgB;IAC7C,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC,IAAI,CAAC;IAC7B,IAAI,CAAC,CAAC,EAAE,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,MAAM,WAAW,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC1G,IAAI,SAAS;QAAE,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,OAAO,YAAY,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB,EAAE,OAAO,GAAa,EAAE,EAAE,MAAM,GAAG,GAAG;IACtE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC;IACvE,KAAK,MAAM,CAAC,IAAI,OAAO;QAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACzD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,sFAAsF;AAEtF,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAgB;IACpD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IACjB,IAAI,CAAC,CAAC;QACJ,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAE1G,IAAI,KAAK,KAAK,WAAW,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;IACnG,CAAC;IACD,IAAI,KAAK,KAAK,WAAW,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;QAClE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;QACjD,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACnD,OAAO,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IAEhE,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC;IACtD,IAAI,KAAK,KAAK,WAAW,CAAC,KAAK,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;QACtG,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC;QAC5E,MAAM,CAAC,MAAM,GAAG,IAAI,eAAe,CAAC;YAClC,SAAS,EAAE,CAAC,CAAC,QAAQ;YACrB,aAAa,EAAE,MAAM;YACrB,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;YAC5B,YAAY,EAAE,WAAW;YACzB,KAAK;YACL,cAAc,EAAE,YAAY,CAAC,QAAQ,CAAC;YACtC,qBAAqB,EAAE,MAAM;SAC9B,CAAC,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE;YACjC,eAAe,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE;gBACvC,MAAM,EAAE,YAAY;gBACpB,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;gBACjB,IAAI,EAAE,kBAAkB;aACzB,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IACD,WAAW;IACX,MAAM,IAAI,GAAG,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACxF,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAClG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC;QAC/D,OAAO,IAAI,QAAQ,CAAC,yDAAyD,EAAE;YAC7E,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,UAAU,EAAE;SAClE,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,OAAgB,EAAE,CAAW,EAAE,CAAe,EAAE,KAAa;IACrF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,WAAW,CAAC,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC7D,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;aAC1C,IAAI,EAAE;aACN,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,yBAAyB,CAAC,EAAE,GAAG,CAAC,CAAC;QACtG,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,IAAI,SAAS,CAAC,CAAC;QAC9D,OAAO,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,KAAK,KAAK,WAAW,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3G,gDAAgD;IAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,MAAM,GAAG,GAAG;IACtC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,eAAe,EAAE,UAAU,EAAE;KACrF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,SAAS,CAAC,CAAW,EAAE,IAAY,EAAE,KAAc;IAC1D,OAAO;mBACU,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;;;;;8BAKN,WAAW,CAAC,KAAK;qBAC1B,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;EACpC,KAAK,CAAC,CAAC,CAAC,qBAAqB,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;;;8CAGhB,UAAU,CAAC,IAAI,CAAC;4DACF,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** RFC 7636 code verifier (43-128 chars of unreserved characters). */
|
|
2
|
+
export declare function createVerifier(): string;
|
|
3
|
+
export declare function challengeFor(verifier: string): string;
|
|
4
|
+
export declare function randomState(): string;
|
|
5
|
+
export interface FlowState {
|
|
6
|
+
/** PKCE verifier. */
|
|
7
|
+
v: string;
|
|
8
|
+
/** OAuth state. */
|
|
9
|
+
s: string;
|
|
10
|
+
/** Path to return to after sign-in. */
|
|
11
|
+
r: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function encodeFlow(f: FlowState): string;
|
|
14
|
+
export declare function decodeFlow(raw: string | undefined): FlowState | null;
|
|
15
|
+
/** Only same-origin absolute paths are allowed as post-login destinations. */
|
|
16
|
+
export declare function safeRedirect(target: string | null | undefined): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
/** RFC 7636 code verifier (43-128 chars of unreserved characters). */
|
|
3
|
+
export function createVerifier() {
|
|
4
|
+
return randomBytes(32).toString('base64url');
|
|
5
|
+
}
|
|
6
|
+
export function challengeFor(verifier) {
|
|
7
|
+
return createHash('sha256').update(verifier).digest('base64url');
|
|
8
|
+
}
|
|
9
|
+
export function randomState() {
|
|
10
|
+
return randomBytes(16).toString('base64url');
|
|
11
|
+
}
|
|
12
|
+
export function encodeFlow(f) {
|
|
13
|
+
return Buffer.from(JSON.stringify(f)).toString('base64url');
|
|
14
|
+
}
|
|
15
|
+
export function decodeFlow(raw) {
|
|
16
|
+
if (!raw)
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
const f = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8'));
|
|
20
|
+
return typeof f.v === 'string' && typeof f.s === 'string' && typeof f.r === 'string' ? f : null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Only same-origin absolute paths are allowed as post-login destinations. */
|
|
27
|
+
export function safeRedirect(target) {
|
|
28
|
+
if (!target?.startsWith('/') || target.startsWith('//') || target.includes('\\'))
|
|
29
|
+
return '/';
|
|
30
|
+
return target;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=pkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../src/auth/pkce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,sEAAsE;AACtE,MAAM,UAAU,cAAc;IAC5B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAWD,MAAM,UAAU,UAAU,CAAC,CAAY;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAuB;IAChD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAc,CAAC;QAClF,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,MAAiC;IAC5D,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC7F,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AuthBindings, PlatformUser } from '@ericminassian/platform-core';
|
|
2
|
+
import { type JWTPayload } from 'jose';
|
|
3
|
+
export interface Tokens {
|
|
4
|
+
idToken: string;
|
|
5
|
+
refreshToken?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Verify a Cognito ID (or access) token for this app's client and map it to a user. */
|
|
8
|
+
export declare function verifyToken(auth: AuthBindings, token: string): Promise<PlatformUser>;
|
|
9
|
+
export declare function isExpiredError(e: unknown): boolean;
|
|
10
|
+
export declare function toUser(p: JWTPayload): PlatformUser;
|
|
11
|
+
export declare function exchangeCode(auth: AuthBindings, code: string, verifier: string, redirectUri: string): Promise<Tokens>;
|
|
12
|
+
export declare function refreshTokens(auth: AuthBindings, refreshToken: string): Promise<Tokens>;
|
|
13
|
+
/**
|
|
14
|
+
* `plat dev` sign-in against the AWS Emulator's Cognito (no hosted UI there):
|
|
15
|
+
* create the user if needed, set a throwaway password, and authenticate.
|
|
16
|
+
*/
|
|
17
|
+
export declare function localSignIn(auth: AuthBindings, email: string, name?: string): Promise<Tokens>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { createRemoteJWKSet, errors as joseErrors, jwtVerify } from 'jose';
|
|
3
|
+
import { client } from '../clients.js';
|
|
4
|
+
const jwksCache = new Map();
|
|
5
|
+
function jwks(uri) {
|
|
6
|
+
let set = jwksCache.get(uri);
|
|
7
|
+
if (!set) {
|
|
8
|
+
set = createRemoteJWKSet(new URL(uri), { cooldownDuration: 30_000, cacheMaxAge: 3_600_000 });
|
|
9
|
+
jwksCache.set(uri, set);
|
|
10
|
+
}
|
|
11
|
+
return set;
|
|
12
|
+
}
|
|
13
|
+
/** Verify a Cognito ID (or access) token for this app's client and map it to a user. */
|
|
14
|
+
export async function verifyToken(auth, token) {
|
|
15
|
+
const { payload } = await jwtVerify(token, jwks(auth.jwksUri), { issuer: auth.issuer });
|
|
16
|
+
const use = payload.token_use;
|
|
17
|
+
if (use === 'id') {
|
|
18
|
+
const aud = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
|
|
19
|
+
if (!aud.includes(auth.clientId))
|
|
20
|
+
throw new joseErrors.JWTClaimValidationFailed('unexpected "aud" claim value', payload, 'aud');
|
|
21
|
+
}
|
|
22
|
+
else if (use === 'access') {
|
|
23
|
+
if (payload.client_id !== auth.clientId)
|
|
24
|
+
throw new joseErrors.JWTClaimValidationFailed('unexpected "client_id" claim value', payload, 'client_id');
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
throw new joseErrors.JWTClaimValidationFailed('unexpected "token_use" claim value', payload, 'token_use');
|
|
28
|
+
}
|
|
29
|
+
return toUser(payload);
|
|
30
|
+
}
|
|
31
|
+
export function isExpiredError(e) {
|
|
32
|
+
return e instanceof joseErrors.JWTExpired;
|
|
33
|
+
}
|
|
34
|
+
export function toUser(p) {
|
|
35
|
+
const str = (k) => (typeof p[k] === 'string' ? p[k] : undefined);
|
|
36
|
+
const name = str('name') ?? ([str('given_name'), str('family_name')].filter(Boolean).join(' ') || undefined);
|
|
37
|
+
const groups = Array.isArray(p['cognito:groups']) ? p['cognito:groups'].map(String) : [];
|
|
38
|
+
const user = { id: String(p.sub), groups };
|
|
39
|
+
const email = str('email');
|
|
40
|
+
if (email)
|
|
41
|
+
user.email = email;
|
|
42
|
+
if (p.email_verified !== undefined)
|
|
43
|
+
user.emailVerified = p.email_verified === true || p.email_verified === 'true';
|
|
44
|
+
if (name)
|
|
45
|
+
user.name = name;
|
|
46
|
+
return user;
|
|
47
|
+
}
|
|
48
|
+
// ---------------------------------------------------------------- hosted login (cognito mode)
|
|
49
|
+
async function tokenRequest(auth, params) {
|
|
50
|
+
const res = await fetch(`${auth.loginUrl.replace(/\/$/, '')}/oauth2/token`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
53
|
+
body: new URLSearchParams({ client_id: auth.clientId, ...params }),
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok)
|
|
56
|
+
throw new Error(`Cognito token endpoint returned ${res.status}: ${(await res.text()).slice(0, 200)}`);
|
|
57
|
+
const json = (await res.json());
|
|
58
|
+
if (!json.id_token)
|
|
59
|
+
throw new Error('Cognito token endpoint returned no id_token');
|
|
60
|
+
return { idToken: json.id_token, ...(json.refresh_token ? { refreshToken: json.refresh_token } : {}) };
|
|
61
|
+
}
|
|
62
|
+
export function exchangeCode(auth, code, verifier, redirectUri) {
|
|
63
|
+
return tokenRequest(auth, {
|
|
64
|
+
grant_type: 'authorization_code',
|
|
65
|
+
code,
|
|
66
|
+
code_verifier: verifier,
|
|
67
|
+
redirect_uri: redirectUri,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export async function refreshTokens(auth, refreshToken) {
|
|
71
|
+
if (auth.mode === 'local') {
|
|
72
|
+
const { InitiateAuthCommand } = await import('@aws-sdk/client-cognito-identity-provider');
|
|
73
|
+
const out = await (await client('cognito')).send(new InitiateAuthCommand({
|
|
74
|
+
ClientId: auth.clientId,
|
|
75
|
+
AuthFlow: 'REFRESH_TOKEN_AUTH',
|
|
76
|
+
AuthParameters: { REFRESH_TOKEN: refreshToken },
|
|
77
|
+
}));
|
|
78
|
+
const id = out.AuthenticationResult?.IdToken;
|
|
79
|
+
if (!id)
|
|
80
|
+
throw new Error('refresh returned no id token');
|
|
81
|
+
return { idToken: id };
|
|
82
|
+
}
|
|
83
|
+
return tokenRequest(auth, { grant_type: 'refresh_token', refresh_token: refreshToken });
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------- local sign-in (plat dev)
|
|
86
|
+
function errorName(e) {
|
|
87
|
+
return e?.name ?? '';
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* `plat dev` sign-in against the AWS Emulator's Cognito (no hosted UI there):
|
|
91
|
+
* create the user if needed, set a throwaway password, and authenticate.
|
|
92
|
+
*/
|
|
93
|
+
export async function localSignIn(auth, email, name) {
|
|
94
|
+
const cognito = await client('cognito');
|
|
95
|
+
const { AdminCreateUserCommand, AdminInitiateAuthCommand, AdminSetUserPasswordCommand, InitiateAuthCommand, } = await import('@aws-sdk/client-cognito-identity-provider');
|
|
96
|
+
try {
|
|
97
|
+
await cognito.send(new AdminCreateUserCommand({
|
|
98
|
+
UserPoolId: auth.userPoolId,
|
|
99
|
+
Username: email,
|
|
100
|
+
MessageAction: 'SUPPRESS',
|
|
101
|
+
UserAttributes: [
|
|
102
|
+
{ Name: 'email', Value: email },
|
|
103
|
+
{ Name: 'email_verified', Value: 'true' },
|
|
104
|
+
...(name ? [{ Name: 'name', Value: name }] : []),
|
|
105
|
+
],
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
if (errorName(e) !== 'UsernameExistsException')
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
const password = `Pp1!${randomBytes(18).toString('base64url')}`;
|
|
113
|
+
await cognito.send(new AdminSetUserPasswordCommand({
|
|
114
|
+
UserPoolId: auth.userPoolId,
|
|
115
|
+
Username: email,
|
|
116
|
+
Password: password,
|
|
117
|
+
Permanent: true,
|
|
118
|
+
}));
|
|
119
|
+
const params = { USERNAME: email, PASSWORD: password };
|
|
120
|
+
let result;
|
|
121
|
+
try {
|
|
122
|
+
const out = await cognito.send(new AdminInitiateAuthCommand({
|
|
123
|
+
UserPoolId: auth.userPoolId,
|
|
124
|
+
ClientId: auth.clientId,
|
|
125
|
+
AuthFlow: 'ADMIN_USER_PASSWORD_AUTH',
|
|
126
|
+
AuthParameters: params,
|
|
127
|
+
}));
|
|
128
|
+
result = out.AuthenticationResult;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
const out = await cognito.send(new InitiateAuthCommand({
|
|
132
|
+
ClientId: auth.clientId,
|
|
133
|
+
AuthFlow: 'USER_PASSWORD_AUTH',
|
|
134
|
+
AuthParameters: params,
|
|
135
|
+
}));
|
|
136
|
+
result = out.AuthenticationResult;
|
|
137
|
+
}
|
|
138
|
+
if (!result?.IdToken)
|
|
139
|
+
throw new Error('Local sign-in returned no tokens');
|
|
140
|
+
return { idToken: result.IdToken, ...(result.RefreshToken ? { refreshToken: result.RefreshToken } : {}) };
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Bindings } from '@ericminassian/platform-core';
|
|
2
|
+
/**
|
|
3
|
+
* The stage's bindings, decoded lazily from `PLATFORM_BINDINGS` on first use
|
|
4
|
+
* so importing the SDK never throws; using a resource without bindings does.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getBindings(): Bindings;
|
|
7
|
+
/** Bindings if present, otherwise undefined (telemetry, health). */
|
|
8
|
+
export declare function tryBindings(): Bindings | undefined;
|
|
9
|
+
/** Override bindings (tests). Pass undefined to re-read the environment. */
|
|
10
|
+
export declare function setBindings(b: Bindings | undefined): void;
|
|
11
|
+
export declare function region(): string;
|
|
12
|
+
/** True under `plat dev` / `plat test` (AWS Emulator). */
|
|
13
|
+
export declare function isEmulator(): boolean;
|
package/dist/bindings.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BINDINGS_ENV, decodeBindings } from '@ericminassian/platform-core';
|
|
2
|
+
let cached;
|
|
3
|
+
/**
|
|
4
|
+
* The stage's bindings, decoded lazily from `PLATFORM_BINDINGS` on first use
|
|
5
|
+
* so importing the SDK never throws; using a resource without bindings does.
|
|
6
|
+
*/
|
|
7
|
+
export function getBindings() {
|
|
8
|
+
if (!cached)
|
|
9
|
+
cached = decodeBindings(process.env[BINDINGS_ENV]);
|
|
10
|
+
return cached;
|
|
11
|
+
}
|
|
12
|
+
/** Bindings if present, otherwise undefined (telemetry, health). */
|
|
13
|
+
export function tryBindings() {
|
|
14
|
+
try {
|
|
15
|
+
return getBindings();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Override bindings (tests). Pass undefined to re-read the environment. */
|
|
22
|
+
export function setBindings(b) {
|
|
23
|
+
cached = b;
|
|
24
|
+
}
|
|
25
|
+
export function region() {
|
|
26
|
+
return tryBindings()?.region ?? process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? 'us-east-1';
|
|
27
|
+
}
|
|
28
|
+
/** True under `plat dev` / `plat test` (AWS Emulator). */
|
|
29
|
+
export function isEmulator() {
|
|
30
|
+
return Boolean(process.env.AWS_ENDPOINT_URL);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.js","sourceRoot":"","sources":["../src/bindings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAiB,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE3F,IAAI,MAA4B,CAAC;AAEjC;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC,MAAM;QAAE,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IAChE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,CAAuB;IACjD,MAAM,GAAG,CAAC,CAAC;AACb,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,OAAO,WAAW,EAAE,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,WAAW,CAAC;AAC1G,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAC/C,CAAC"}
|