@airdraft/plugin-auth 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/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@airdraft/plugin-auth` will be documented here.
4
+
5
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
@@ -0,0 +1,49 @@
1
+ import type { Role } from '@airdraft/auth';
2
+ import type { AuthProvider } from './types.js';
3
+ import type { UserStore } from './UserStore.js';
4
+ export interface CredentialsProviderOptions {
5
+ userStore: UserStore;
6
+ /**
7
+ * AIRDRAFT_JWT_SECRET — must be ≥ 32 chars (C7).
8
+ * Defaults to `process.env.AIRDRAFT_JWT_SECRET` when omitted.
9
+ */
10
+ secret?: string;
11
+ /**
12
+ * Email-keyed role overrides. Config takes precedence over user store (C3).
13
+ * e.g. `{ 'alice@example.com': 'admin' }`
14
+ */
15
+ roles?: Record<string, Role>;
16
+ /**
17
+ * Base path for the CMS API (default: '/api/cms').
18
+ * Used to scope the refresh cookie path.
19
+ */
20
+ basePath?: string;
21
+ }
22
+ /**
23
+ * Email/password auth provider using scrypt hashing + HS256 JWT sessions.
24
+ *
25
+ * Registers four route handlers:
26
+ * - `POST /auth/login` — verifies credentials, sets httpOnly cookies
27
+ * - `POST /auth/logout` — clears cookies, blocklists the refresh token
28
+ * - `POST /auth/refresh` — rotates refresh token, issues new access token
29
+ * - `GET /auth/me` — returns the current user (from access JWT)
30
+ *
31
+ * Also registers:
32
+ * - `POST /auth/invite/:token` — accepts an invite and sets a password
33
+ *
34
+ * ```ts
35
+ * import { withAuth } from '@airdraft/plugin-auth'
36
+ * import { CredentialsProvider } from '@airdraft/plugin-auth'
37
+ * import { UserStore } from '@airdraft/plugin-auth'
38
+ *
39
+ * withAuth({
40
+ * provider: CredentialsProvider({
41
+ * userStore: UserStore.json(),
42
+ * secret: process.env.AIRDRAFT_JWT_SECRET!,
43
+ * roles: { 'alice@example.com': 'admin' },
44
+ * })
45
+ * })
46
+ * ```
47
+ */
48
+ export declare function CredentialsProvider(opts: CredentialsProviderOptions): AuthProvider;
49
+ //# sourceMappingURL=CredentialsProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CredentialsProvider.d.ts","sourceRoot":"","sources":["../src/CredentialsProvider.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAY,IAAI,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,YAAY,CAAA;AAE/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AA0E/C,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,SAAS,CAAA;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,YAAY,CA6UlF"}
@@ -0,0 +1,441 @@
1
+ import { signAccessToken, signRefreshToken, verifyAccessToken, ACCESS_TOKEN_TTL, REFRESH_TOKEN_TTL, } from '@airdraft/auth';
2
+ import { createHmac, timingSafeEqual } from 'node:crypto';
3
+ import { refreshTokenBlocklist } from './blocklist.js';
4
+ import { getRequestUser } from './withAuth.js';
5
+ // ---------------------------------------------------------------------------
6
+ // Cookie helpers
7
+ // ---------------------------------------------------------------------------
8
+ /**
9
+ * Builds an `airdraft_session` (access token) cookie string.
10
+ * httpOnly + SameSite=Strict (C6).
11
+ */
12
+ function accessCookie(token, secure) {
13
+ const attrs = [
14
+ `airdraft_session=${encodeURIComponent(token)}`,
15
+ 'HttpOnly',
16
+ 'SameSite=Strict',
17
+ 'Path=/',
18
+ `Max-Age=${ACCESS_TOKEN_TTL}`,
19
+ ...(secure ? ['Secure'] : []),
20
+ ];
21
+ return attrs.join('; ');
22
+ }
23
+ /**
24
+ * Builds an `airdraft_refresh` cookie string, scoped to the refresh endpoint.
25
+ * httpOnly + SameSite=Strict (C6).
26
+ */
27
+ function refreshCookie(token, refreshPath, secure) {
28
+ const attrs = [
29
+ `airdraft_refresh=${encodeURIComponent(token)}`,
30
+ 'HttpOnly',
31
+ 'SameSite=Strict',
32
+ `Path=${refreshPath}`,
33
+ `Max-Age=${REFRESH_TOKEN_TTL}`,
34
+ ...(secure ? ['Secure'] : []),
35
+ ];
36
+ return attrs.join('; ');
37
+ }
38
+ function clearCookies(refreshPath) {
39
+ return [
40
+ `airdraft_session=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`,
41
+ `airdraft_refresh=; HttpOnly; SameSite=Strict; Path=${refreshPath}; Max-Age=0`,
42
+ ];
43
+ }
44
+ function parseCookie(header, name) {
45
+ if (!header)
46
+ return null;
47
+ for (const part of header.split(';')) {
48
+ const [key, ...rest] = part.trim().split('=');
49
+ if (key?.trim() === name)
50
+ return decodeURIComponent(rest.join('='));
51
+ }
52
+ return null;
53
+ }
54
+ function isSecure(req) {
55
+ try {
56
+ return new URL(req.url).protocol === 'https:';
57
+ }
58
+ catch {
59
+ return false;
60
+ }
61
+ }
62
+ function json(body, status = 200) {
63
+ return new Response(JSON.stringify(body), {
64
+ status,
65
+ headers: { 'Content-Type': 'application/json' },
66
+ });
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // CredentialsProvider (C2, C5, C6, C8)
70
+ // ---------------------------------------------------------------------------
71
+ /**
72
+ * Email/password auth provider using scrypt hashing + HS256 JWT sessions.
73
+ *
74
+ * Registers four route handlers:
75
+ * - `POST /auth/login` — verifies credentials, sets httpOnly cookies
76
+ * - `POST /auth/logout` — clears cookies, blocklists the refresh token
77
+ * - `POST /auth/refresh` — rotates refresh token, issues new access token
78
+ * - `GET /auth/me` — returns the current user (from access JWT)
79
+ *
80
+ * Also registers:
81
+ * - `POST /auth/invite/:token` — accepts an invite and sets a password
82
+ *
83
+ * ```ts
84
+ * import { withAuth } from '@airdraft/plugin-auth'
85
+ * import { CredentialsProvider } from '@airdraft/plugin-auth'
86
+ * import { UserStore } from '@airdraft/plugin-auth'
87
+ *
88
+ * withAuth({
89
+ * provider: CredentialsProvider({
90
+ * userStore: UserStore.json(),
91
+ * secret: process.env.AIRDRAFT_JWT_SECRET!,
92
+ * roles: { 'alice@example.com': 'admin' },
93
+ * })
94
+ * })
95
+ * ```
96
+ */
97
+ export function CredentialsProvider(opts) {
98
+ const secret = opts.secret ?? process.env['AIRDRAFT_JWT_SECRET'] ?? '';
99
+ if (!secret || secret.length < 32) {
100
+ throw new Error('[airdraft] AIRDRAFT_JWT_SECRET must be at least 32 characters. ' +
101
+ 'Generate one with: npx airdraft generate-secret');
102
+ }
103
+ const { userStore } = opts;
104
+ const configRoles = opts.roles ?? {};
105
+ const basePath = opts.basePath ?? '/api/cms';
106
+ const refreshPath = `${basePath}/auth/refresh`;
107
+ /** Resolves the effective role: config override > user store role. */
108
+ function resolveRole(email, storeRole) {
109
+ return configRoles[email.toLowerCase()] ?? storeRole;
110
+ }
111
+ // -------------------------------------------------------------------------
112
+ // verify — called on every protected request (C2)
113
+ // -------------------------------------------------------------------------
114
+ async function verify(req) {
115
+ const cookie = req.headers.get('cookie');
116
+ const rawToken = parseCookie(cookie, 'airdraft_session');
117
+ if (!rawToken)
118
+ return null;
119
+ const user = verifyAccessToken(rawToken, secret);
120
+ if (!user)
121
+ return null;
122
+ // Apply config role override (C3)
123
+ return { ...user, role: resolveRole(user.email, user.role) };
124
+ }
125
+ // -------------------------------------------------------------------------
126
+ // Route handlers
127
+ // -------------------------------------------------------------------------
128
+ /** POST /auth/login */
129
+ async function loginHandler(req) {
130
+ let body;
131
+ try {
132
+ body = (await req.json());
133
+ }
134
+ catch {
135
+ return json({ error: 'Invalid JSON body' }, 400);
136
+ }
137
+ if (typeof body.email !== 'string' || typeof body.password !== 'string') {
138
+ return json({ error: 'email and password are required' }, 400);
139
+ }
140
+ const record = await userStore.verifyPassword(body.email, body.password);
141
+ if (!record) {
142
+ return json({ error: 'Invalid email or password' }, 401);
143
+ }
144
+ const role = resolveRole(record.email, record.role);
145
+ const user = { id: record.id, email: record.email, name: record.name, role };
146
+ const accessToken = signAccessToken(user, secret);
147
+ const refreshToken = signRefreshToken();
148
+ const secure = isSecure(req);
149
+ return new Response(JSON.stringify({ user: { id: user.id, email: user.email, name: user.name, role } }), {
150
+ status: 200,
151
+ headers: {
152
+ 'Content-Type': 'application/json',
153
+ 'Set-Cookie': [accessCookie(accessToken, secure), refreshCookie(refreshToken, refreshPath, secure)].join(', '),
154
+ },
155
+ });
156
+ }
157
+ /** POST /auth/logout */
158
+ async function logoutHandler(req) {
159
+ const cookie = req.headers.get('cookie');
160
+ const refreshToken = parseCookie(cookie, 'airdraft_refresh');
161
+ if (refreshToken) {
162
+ refreshTokenBlocklist.add(refreshToken);
163
+ }
164
+ const headers = new Headers({ 'Content-Type': 'application/json' });
165
+ for (const c of clearCookies(refreshPath)) {
166
+ headers.append('Set-Cookie', c);
167
+ }
168
+ return new Response(JSON.stringify({ ok: true }), { status: 200, headers });
169
+ }
170
+ /** POST /auth/refresh — silent token rotation (C8) */
171
+ async function refreshHandler(req) {
172
+ const cookie = req.headers.get('cookie');
173
+ const oldRefresh = parseCookie(cookie, 'airdraft_refresh');
174
+ if (!oldRefresh || refreshTokenBlocklist.has(oldRefresh)) {
175
+ return json({ error: 'Invalid or expired refresh token' }, 401);
176
+ }
177
+ // Access token may be expired — get user identity from the access JWT anyway
178
+ const rawAccess = parseCookie(cookie, 'airdraft_session');
179
+ // Allow expired access tokens here — we're explicitly refreshing
180
+ let user = null;
181
+ if (rawAccess) {
182
+ // verifyAccessToken rejects expired tokens; parse manually for refresh
183
+ user = parseExpiredAccessToken(rawAccess, secret);
184
+ }
185
+ if (!user)
186
+ return json({ error: 'Cannot identify user for token refresh' }, 401);
187
+ // Blocklist the consumed refresh token (C8)
188
+ refreshTokenBlocklist.add(oldRefresh);
189
+ const role = resolveRole(user.email, user.role);
190
+ const refreshedUser = { ...user, role };
191
+ const newAccess = signAccessToken(refreshedUser, secret);
192
+ const newRefresh = signRefreshToken();
193
+ const secure = isSecure(req);
194
+ return new Response(JSON.stringify({ user: { id: user.id, email: user.email, name: user.name, role } }), {
195
+ status: 200,
196
+ headers: {
197
+ 'Content-Type': 'application/json',
198
+ 'Set-Cookie': [
199
+ accessCookie(newAccess, secure),
200
+ refreshCookie(newRefresh, refreshPath, secure),
201
+ ].join(', '),
202
+ },
203
+ });
204
+ }
205
+ /** GET /auth/me */
206
+ async function meHandler(req) {
207
+ const user = await verify(req);
208
+ if (!user)
209
+ return json({ error: 'Not authenticated' }, 401);
210
+ return json({ user: { id: user.id, email: user.email, name: user.name, role: user.role } });
211
+ }
212
+ /** POST /auth/invite/:token */
213
+ async function inviteHandler(req) {
214
+ const url = new URL(req.url);
215
+ const segments = url.pathname.split('/');
216
+ const token = segments[segments.length - 1];
217
+ if (!token)
218
+ return json({ error: 'Missing invite token' }, 400);
219
+ let body;
220
+ try {
221
+ body = (await req.json());
222
+ }
223
+ catch {
224
+ return json({ error: 'Invalid JSON body' }, 400);
225
+ }
226
+ if (typeof body.password !== 'string' || body.password.length < 8) {
227
+ return json({ error: 'password must be at least 8 characters' }, 400);
228
+ }
229
+ try {
230
+ const record = await userStore.acceptInvite(token, body.password, typeof body.name === 'string' ? body.name : undefined);
231
+ return json({ user: { id: record.id, email: record.email, name: record.name, role: record.role } }, 201);
232
+ }
233
+ catch (err) {
234
+ return json({ error: err.message }, 400);
235
+ }
236
+ }
237
+ // -------------------------------------------------------------------------
238
+ // Admin helpers
239
+ // -------------------------------------------------------------------------
240
+ /** Ensures the caller is authenticated as admin. Returns the user or an error Response.
241
+ * Uses the user already attached by `withAuth` middleware (supports any auth provider),
242
+ * falling back to direct JWT-cookie verification for callers outside middleware context.
243
+ */
244
+ async function requireAdmin(req) {
245
+ const actor = getRequestUser(req) ?? await verify(req);
246
+ if (!actor)
247
+ return json({ error: 'Not authenticated' }, 401);
248
+ if (actor.role !== 'admin')
249
+ return json({ error: 'Forbidden' }, 403);
250
+ return actor;
251
+ }
252
+ // -------------------------------------------------------------------------
253
+ // Admin: user management routes
254
+ // -------------------------------------------------------------------------
255
+ /** GET /auth/users */
256
+ async function listUsersHandler(req) {
257
+ const actorOrResp = await requireAdmin(req);
258
+ if (actorOrResp instanceof Response)
259
+ return actorOrResp;
260
+ const users = await userStore.list();
261
+ return json({
262
+ data: users.map((u) => ({
263
+ id: u.id,
264
+ email: u.email,
265
+ name: u.name,
266
+ role: resolveRole(u.email, u.role),
267
+ createdAt: u.createdAt,
268
+ })),
269
+ });
270
+ }
271
+ /** PATCH /auth/users/:id/role */
272
+ async function updateUserRoleHandler(req) {
273
+ const actorOrResp = await requireAdmin(req);
274
+ if (actorOrResp instanceof Response)
275
+ return actorOrResp;
276
+ const url = new URL(req.url);
277
+ const segments = url.pathname.split('/');
278
+ // …/users/:id/role → id is segments[-2]
279
+ const userId = segments[segments.length - 2];
280
+ if (!userId)
281
+ return json({ error: 'Missing user id' }, 400);
282
+ let body;
283
+ try {
284
+ body = (await req.json());
285
+ }
286
+ catch {
287
+ return json({ error: 'Invalid JSON body' }, 400);
288
+ }
289
+ if (!['admin', 'publisher', 'editor'].includes(body.role)) {
290
+ return json({ error: 'role must be admin, publisher, or editor' }, 400);
291
+ }
292
+ const users = await userStore.list();
293
+ const target = users.find((u) => u.id === userId);
294
+ if (!target)
295
+ return json({ error: 'User not found' }, 404);
296
+ const updated = await userStore.updateRole(target.email, body.role);
297
+ if (!updated)
298
+ return json({ error: 'User not found' }, 404);
299
+ return json({
300
+ data: {
301
+ id: updated.id,
302
+ email: updated.email,
303
+ name: updated.name,
304
+ role: resolveRole(updated.email, updated.role),
305
+ createdAt: updated.createdAt,
306
+ },
307
+ });
308
+ }
309
+ /** DELETE /auth/users/:id */
310
+ async function removeUserHandler(req) {
311
+ const actorOrResp = await requireAdmin(req);
312
+ if (actorOrResp instanceof Response)
313
+ return actorOrResp;
314
+ const url = new URL(req.url);
315
+ const segments = url.pathname.split('/');
316
+ const userId = segments[segments.length - 1];
317
+ if (!userId)
318
+ return json({ error: 'Missing user id' }, 400);
319
+ const users = await userStore.list();
320
+ const target = users.find((u) => u.id === userId);
321
+ if (!target)
322
+ return json({ error: 'User not found' }, 404);
323
+ await userStore.remove(target.email);
324
+ return new Response(null, { status: 204 });
325
+ }
326
+ // -------------------------------------------------------------------------
327
+ // Admin: invite management routes
328
+ // -------------------------------------------------------------------------
329
+ /** GET /auth/invites */
330
+ async function listInvitesHandler(req) {
331
+ const actorOrResp = await requireAdmin(req);
332
+ if (actorOrResp instanceof Response)
333
+ return actorOrResp;
334
+ const now = Math.floor(Date.now() / 1000);
335
+ const all = await userStore.listInvites();
336
+ const pending = all.filter((inv) => inv.expiresAt > now);
337
+ return json({ data: pending });
338
+ }
339
+ /** POST /auth/invites */
340
+ async function createInviteHandler(req) {
341
+ const actorOrResp = await requireAdmin(req);
342
+ if (actorOrResp instanceof Response)
343
+ return actorOrResp;
344
+ let body;
345
+ try {
346
+ body = (await req.json());
347
+ }
348
+ catch {
349
+ return json({ error: 'Invalid JSON body' }, 400);
350
+ }
351
+ if (typeof body.email !== 'string')
352
+ return json({ error: 'email is required' }, 400);
353
+ if (!['admin', 'publisher', 'editor'].includes(body.role)) {
354
+ return json({ error: 'role must be admin, publisher, or editor' }, 400);
355
+ }
356
+ try {
357
+ const invite = await userStore.createInvite(body.email, body.role);
358
+ return json({ data: invite }, 201);
359
+ }
360
+ catch (err) {
361
+ return json({ error: err.message }, 400);
362
+ }
363
+ }
364
+ /** DELETE /auth/invites/:token */
365
+ async function revokeInviteHandler(req) {
366
+ const actorOrResp = await requireAdmin(req);
367
+ if (actorOrResp instanceof Response)
368
+ return actorOrResp;
369
+ const url = new URL(req.url);
370
+ const segments = url.pathname.split('/');
371
+ const token = segments[segments.length - 1];
372
+ if (!token)
373
+ return json({ error: 'Missing invite token' }, 400);
374
+ await userStore.revokeInvite(token);
375
+ return new Response(null, { status: 204 });
376
+ }
377
+ /** Combined handler for /auth/invites — dispatches by HTTP method. */
378
+ async function invitesRouter(req) {
379
+ if (req.method === 'POST')
380
+ return createInviteHandler(req);
381
+ return listInvitesHandler(req);
382
+ }
383
+ function handlers() {
384
+ return [
385
+ // Public — these bypass the auth middleware (no valid session required to call them)
386
+ { path: '/auth/login', handler: loginHandler, public: true },
387
+ { path: '/auth/logout', handler: logoutHandler, public: true },
388
+ { path: '/auth/refresh', handler: refreshHandler, public: true },
389
+ { path: '/auth/me', handler: meHandler, public: true },
390
+ { path: '/auth/invite/:token', handler: inviteHandler, public: true },
391
+ // Private — go through middleware so getRequestUser() is populated
392
+ { path: '/auth/users', handler: listUsersHandler, public: false },
393
+ { path: '/auth/users/:id/role', handler: updateUserRoleHandler, public: false },
394
+ { path: '/auth/users/:id', handler: removeUserHandler, public: false },
395
+ { path: '/auth/invites', handler: invitesRouter, public: false },
396
+ { path: '/auth/invites/:token', handler: revokeInviteHandler, public: false },
397
+ ];
398
+ }
399
+ async function refresh(refreshToken) {
400
+ if (refreshTokenBlocklist.has(refreshToken))
401
+ return null;
402
+ return null; // without persistent refresh token storage, rely on the access cookie path above
403
+ }
404
+ return { verify, handlers, refresh };
405
+ }
406
+ // ---------------------------------------------------------------------------
407
+ // Internal: parse expired access token (for refresh flow)
408
+ // ---------------------------------------------------------------------------
409
+ /**
410
+ * Verifies the signature of an HS256 JWT but ignores the `exp` claim.
411
+ * Used only in the refresh flow where the access token may be expired.
412
+ */
413
+ function parseExpiredAccessToken(token, secret) {
414
+ const parts = token.split('.');
415
+ if (parts.length !== 3)
416
+ return null;
417
+ const [header, payload, sig] = parts;
418
+ const expectedSig = createHmac('sha256', secret)
419
+ .update(`${header}.${payload}`)
420
+ .digest('base64url');
421
+ const a = Buffer.from(sig, 'ascii');
422
+ const b = Buffer.from(expectedSig, 'ascii');
423
+ if (a.length !== b.length || !timingSafeEqual(a, b))
424
+ return null;
425
+ try {
426
+ const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
427
+ if (typeof parsed.email !== 'string' || typeof parsed.role !== 'string')
428
+ return null;
429
+ return {
430
+ id: typeof parsed.id === 'string' ? parsed.id : String(parsed.sub ?? parsed.email),
431
+ email: parsed.email,
432
+ name: typeof parsed.name === 'string' ? parsed.name : undefined,
433
+ role: parsed.role,
434
+ meta: parsed.meta,
435
+ };
436
+ }
437
+ catch {
438
+ return null;
439
+ }
440
+ }
441
+ //# sourceMappingURL=CredentialsProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CredentialsProvider.js","sourceRoot":"","sources":["../src/CredentialsProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { Role } from '@airdraft/auth';
2
+ import type { AuthProvider } from './types.js';
3
+ export interface GitHubOAuthProviderOptions {
4
+ clientId: string;
5
+ clientSecret: string;
6
+ /**
7
+ * AIRDRAFT_JWT_SECRET — must be ≥ 32 chars (C7).
8
+ * Defaults to `process.env.AIRDRAFT_JWT_SECRET`.
9
+ */
10
+ secret?: string;
11
+ /** Email-keyed role overrides (C3). */
12
+ roles?: Record<string, Role>;
13
+ /** Only these GitHub usernames may access the CMS. */
14
+ allowedUsers?: string[];
15
+ /** Only members of these GitHub orgs may access the CMS. */
16
+ allowedOrgs?: string[];
17
+ /**
18
+ * Default role for GitHub users not in `roles`.
19
+ * Defaults to 'editor'.
20
+ */
21
+ defaultRole?: Role;
22
+ /** Base path for the CMS API (default: '/api/cms'). */
23
+ basePath?: string;
24
+ /** OAuth callback URL (default: `{origin}{basePath}/auth/github/callback`). */
25
+ callbackUrl?: string;
26
+ }
27
+ /**
28
+ * GitHub OAuth2 provider for Airdraft CMS.
29
+ *
30
+ * Flow:
31
+ * 1. Browser hits `GET /auth/github` → redirected to GitHub OAuth
32
+ * 2. GitHub redirects back to `GET /auth/github/callback?code=...&state=...`
33
+ * 3. Server exchanges code for token, fetches GitHub user, issues JWT
34
+ *
35
+ * CSRF protection: `state` param is a random nonce stored in a session cookie.
36
+ *
37
+ * ```ts
38
+ * withAuth({
39
+ * provider: GitHubOAuthProvider({
40
+ * clientId: process.env.GITHUB_CLIENT_ID!,
41
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
42
+ * secret: process.env.AIRDRAFT_JWT_SECRET!,
43
+ * roles: { 'alice@example.com': 'admin' },
44
+ * })
45
+ * })
46
+ * ```
47
+ */
48
+ export declare function GitHubOAuthProvider(opts: GitHubOAuthProviderOptions): AuthProvider;
49
+ //# sourceMappingURL=GitHubOAuthProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitHubOAuthProvider.d.ts","sourceRoot":"","sources":["../src/GitHubOAuthProvider.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAY,IAAI,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,YAAY,CAAA;AAyE/D,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC5B,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB;;;OAGG;IACH,WAAW,CAAC,EAAE,IAAI,CAAA;IAClB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,YAAY,CAqLlF"}