@airdraft/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/auth` will be documented here.
4
+
5
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
@@ -0,0 +1,4 @@
1
+ export * from './tokens.js';
2
+ export * from './middleware.js';
3
+ export * from './jwt.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // @airdraft/auth — public surface
2
+ export * from './tokens.js';
3
+ export * from './middleware.js';
4
+ export * from './jwt.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA"}
package/dist/jwt.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ /** Airdraft role hierarchy. admin > publisher > editor. */
2
+ export type Role = 'admin' | 'publisher' | 'editor';
3
+ /**
4
+ * Authenticated user identity, carried in the JWT payload and returned by
5
+ * `AuthProvider.verify()` on every protected request.
6
+ */
7
+ export interface AuthUser {
8
+ id: string;
9
+ email: string;
10
+ name?: string;
11
+ role: Role;
12
+ meta?: Record<string, unknown>;
13
+ }
14
+ export interface RolePermissions {
15
+ /** Create, edit, delete entries + trigger publish. */
16
+ publish: boolean;
17
+ /** Delete entries that have already been published. */
18
+ deletePublished: boolean;
19
+ /** Edit collection schemas via the schema editor. */
20
+ manageSchema: boolean;
21
+ /** Invite / remove team members. */
22
+ manageTeam: boolean;
23
+ }
24
+ export declare const ROLE_PERMISSIONS: Record<Role, RolePermissions>;
25
+ /** Returns the permissions object for the given user's role. */
26
+ export declare function can(user: AuthUser): RolePermissions;
27
+ /** Access JWT lifetime in seconds (15 min). */
28
+ export declare const ACCESS_TOKEN_TTL: number;
29
+ /** Refresh token lifetime in seconds (7 days). */
30
+ export declare const REFRESH_TOKEN_TTL: number;
31
+ /**
32
+ * Signs a short-lived HS256 access JWT for the given user.
33
+ *
34
+ * ```ts
35
+ * const token = signAccessToken(user, process.env.AIRDRAFT_JWT_SECRET!)
36
+ * ```
37
+ */
38
+ export declare function signAccessToken(user: AuthUser, secret: string, ttlSeconds?: number): string;
39
+ /**
40
+ * Generates a cryptographically random opaque refresh token (96 hex chars).
41
+ * Store this in an httpOnly cookie scoped to the refresh endpoint.
42
+ */
43
+ export declare function signRefreshToken(): string;
44
+ /**
45
+ * Verifies an HS256 access JWT.
46
+ *
47
+ * Returns the `AuthUser` from the payload, or `null` if the token is
48
+ * invalid, expired, or has a bad signature.
49
+ */
50
+ export declare function verifyAccessToken(token: string, secret: string): AuthUser | null;
51
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAMA,2DAA2D;AAC3D,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,WAAW,GAAG,QAAQ,CAAA;AAEnD;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,IAAI,CAAA;IACV,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAMD,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAA;IAChB,uDAAuD;IACvD,eAAe,EAAE,OAAO,CAAA;IACxB,qDAAqD;IACrD,YAAY,EAAE,OAAO,CAAA;IACrB,oCAAoC;IACpC,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,IAAI,EAAE,eAAe,CAI1D,CAAA;AAED,gEAAgE;AAChE,wBAAgB,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,eAAe,CAEnD;AAMD,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB,QAAU,CAAA;AAEvC,kDAAkD;AAClD,eAAO,MAAM,iBAAiB,QAAmB,CAAA;AA+BjD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,UAAU,GAAE,MAAyB,GACpC,MAAM,CAiBR;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CA2BhF"}
package/dist/jwt.js ADDED
@@ -0,0 +1,109 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ export const ROLE_PERMISSIONS = {
3
+ admin: { publish: true, deletePublished: true, manageSchema: true, manageTeam: true },
4
+ publisher: { publish: true, deletePublished: true, manageSchema: false, manageTeam: false },
5
+ editor: { publish: false, deletePublished: false, manageSchema: false, manageTeam: false },
6
+ };
7
+ /** Returns the permissions object for the given user's role. */
8
+ export function can(user) {
9
+ return ROLE_PERMISSIONS[user.role];
10
+ }
11
+ // ---------------------------------------------------------------------------
12
+ // Token TTL constants (C6, C8)
13
+ // ---------------------------------------------------------------------------
14
+ /** Access JWT lifetime in seconds (15 min). */
15
+ export const ACCESS_TOKEN_TTL = 15 * 60;
16
+ /** Refresh token lifetime in seconds (7 days). */
17
+ export const REFRESH_TOKEN_TTL = 7 * 24 * 60 * 60;
18
+ // ---------------------------------------------------------------------------
19
+ // Internal HS256 JWT helpers (C6, C7)
20
+ // ---------------------------------------------------------------------------
21
+ function b64url(data) {
22
+ const buf = typeof data === 'string' ? Buffer.from(data, 'utf8') : data;
23
+ return buf.toString('base64url');
24
+ }
25
+ function b64urlDecode(s) {
26
+ return Buffer.from(s, 'base64url').toString('utf8');
27
+ }
28
+ function hmacSign(data, secret) {
29
+ return createHmac('sha256', secret).update(data).digest('base64url');
30
+ }
31
+ function hmacVerify(data, sig, secret) {
32
+ const expected = hmacSign(data, secret);
33
+ const a = Buffer.from(sig, 'ascii');
34
+ const b = Buffer.from(expected, 'ascii');
35
+ if (a.length !== b.length)
36
+ return false;
37
+ return timingSafeEqual(a, b);
38
+ }
39
+ // ---------------------------------------------------------------------------
40
+ // Public JWT API (C6, C7)
41
+ // ---------------------------------------------------------------------------
42
+ /**
43
+ * Signs a short-lived HS256 access JWT for the given user.
44
+ *
45
+ * ```ts
46
+ * const token = signAccessToken(user, process.env.AIRDRAFT_JWT_SECRET!)
47
+ * ```
48
+ */
49
+ export function signAccessToken(user, secret, ttlSeconds = ACCESS_TOKEN_TTL) {
50
+ const now = Math.floor(Date.now() / 1000);
51
+ const header = b64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
52
+ const payload = b64url(JSON.stringify({
53
+ sub: user.email,
54
+ id: user.id,
55
+ email: user.email,
56
+ ...(user.name ? { name: user.name } : {}),
57
+ role: user.role,
58
+ ...(user.meta ? { meta: user.meta } : {}),
59
+ iat: now,
60
+ exp: now + ttlSeconds,
61
+ }));
62
+ const sig = hmacSign(`${header}.${payload}`, secret);
63
+ return `${header}.${payload}.${sig}`;
64
+ }
65
+ /**
66
+ * Generates a cryptographically random opaque refresh token (96 hex chars).
67
+ * Store this in an httpOnly cookie scoped to the refresh endpoint.
68
+ */
69
+ export function signRefreshToken() {
70
+ return randomBytes(48).toString('hex');
71
+ }
72
+ /**
73
+ * Verifies an HS256 access JWT.
74
+ *
75
+ * Returns the `AuthUser` from the payload, or `null` if the token is
76
+ * invalid, expired, or has a bad signature.
77
+ */
78
+ export function verifyAccessToken(token, secret) {
79
+ const parts = token.split('.');
80
+ if (parts.length !== 3)
81
+ return null;
82
+ const [header, payload, sig] = parts;
83
+ if (!hmacVerify(`${header}.${payload}`, sig, secret))
84
+ return null;
85
+ let parsed;
86
+ try {
87
+ parsed = JSON.parse(b64urlDecode(payload));
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ const now = Math.floor(Date.now() / 1000);
93
+ if (typeof parsed.exp === 'number' && parsed.exp < now)
94
+ return null;
95
+ if (typeof parsed.email !== 'string')
96
+ return null;
97
+ if (typeof parsed.role !== 'string')
98
+ return null;
99
+ if (!['admin', 'publisher', 'editor'].includes(parsed.role))
100
+ return null;
101
+ return {
102
+ id: typeof parsed.id === 'string' ? parsed.id : String(parsed.sub ?? parsed.email),
103
+ email: parsed.email,
104
+ name: typeof parsed.name === 'string' ? parsed.name : undefined,
105
+ role: parsed.role,
106
+ meta: parsed.meta,
107
+ };
108
+ }
109
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export type CredentialType = 'bearer' | 'apiKey' | 'embedToken' | 'sessionCookie';
2
+ export interface ExtractedCredential {
3
+ type: CredentialType;
4
+ token: string;
5
+ /** True when token came from `?embedToken` query param (must be redacted in logs) */
6
+ fromQuery?: boolean;
7
+ }
8
+ /**
9
+ * Extracts credentials from an incoming HTTP request in the following priority order:
10
+ *
11
+ * 1. `Authorization: Bearer <token>` → `bearer`
12
+ * 2. `X-API-Key: <token>` → `apiKey`
13
+ * 3. `X-Embed-Token: <token>` → `embedToken`
14
+ * 4. `Cookie: airdraft_session=<token>` → `sessionCookie`
15
+ * 5. `?embedToken=<token>` query param → `embedToken` (fromQuery: true)
16
+ *
17
+ * When both a cookie and an Authorization header are present, Authorization wins.
18
+ * `embedToken` header takes precedence over `?embedToken` query param.
19
+ *
20
+ * Returns `null` if no credentials are found.
21
+ */
22
+ export declare function extractCredentials(request: Request): ExtractedCredential | null;
23
+ /**
24
+ * Returns a log-safe representation of a credential, redacting sensitive values.
25
+ *
26
+ * - Bearer and cookie tokens: shows first 8 chars + `...`
27
+ * - API keys: shows `ntk_` prefix + first 4 chars + `...`
28
+ * - Embed tokens from query: fully redacted (never log embed query tokens)
29
+ */
30
+ export declare function redactCredential(cred: ExtractedCredential): string;
31
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,eAAe,CAAA;AAEjF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,cAAc,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,qFAAqF;IACrF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CAmC/E;AAoBD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAIlE"}
@@ -0,0 +1,82 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Credential extraction from HTTP requests
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Extracts credentials from an incoming HTTP request in the following priority order:
6
+ *
7
+ * 1. `Authorization: Bearer <token>` → `bearer`
8
+ * 2. `X-API-Key: <token>` → `apiKey`
9
+ * 3. `X-Embed-Token: <token>` → `embedToken`
10
+ * 4. `Cookie: airdraft_session=<token>` → `sessionCookie`
11
+ * 5. `?embedToken=<token>` query param → `embedToken` (fromQuery: true)
12
+ *
13
+ * When both a cookie and an Authorization header are present, Authorization wins.
14
+ * `embedToken` header takes precedence over `?embedToken` query param.
15
+ *
16
+ * Returns `null` if no credentials are found.
17
+ */
18
+ export function extractCredentials(request) {
19
+ const headers = request.headers;
20
+ // 1. Authorization: Bearer
21
+ const auth = headers.get('authorization');
22
+ if (auth) {
23
+ const match = auth.match(/^Bearer\s+(.+)$/i);
24
+ if (match)
25
+ return { type: 'bearer', token: match[1] };
26
+ }
27
+ // 2. X-API-Key
28
+ const apiKey = headers.get('x-api-key');
29
+ if (apiKey)
30
+ return { type: 'apiKey', token: apiKey };
31
+ // 3. X-Embed-Token (header takes precedence over query)
32
+ const embedHeader = headers.get('x-embed-token');
33
+ if (embedHeader)
34
+ return { type: 'embedToken', token: embedHeader };
35
+ // 4. Session cookie
36
+ const cookie = headers.get('cookie');
37
+ if (cookie) {
38
+ const sessionToken = parseCookie(cookie, 'airdraft_session');
39
+ if (sessionToken)
40
+ return { type: 'sessionCookie', token: sessionToken };
41
+ }
42
+ // 5. ?embedToken query param (iframe fallback)
43
+ try {
44
+ const url = new URL(request.url);
45
+ const queryToken = url.searchParams.get('embedToken');
46
+ if (queryToken)
47
+ return { type: 'embedToken', token: queryToken, fromQuery: true };
48
+ }
49
+ catch {
50
+ // Malformed URL — no credentials from query
51
+ }
52
+ return null;
53
+ }
54
+ // ---------------------------------------------------------------------------
55
+ // Helpers
56
+ // ---------------------------------------------------------------------------
57
+ function parseCookie(cookieHeader, name) {
58
+ for (const part of cookieHeader.split(';')) {
59
+ const [key, ...valueParts] = part.trim().split('=');
60
+ if (key.trim() === name) {
61
+ return decodeURIComponent(valueParts.join('='));
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // Log-safe credential representation
68
+ // ---------------------------------------------------------------------------
69
+ /**
70
+ * Returns a log-safe representation of a credential, redacting sensitive values.
71
+ *
72
+ * - Bearer and cookie tokens: shows first 8 chars + `...`
73
+ * - API keys: shows `ntk_` prefix + first 4 chars + `...`
74
+ * - Embed tokens from query: fully redacted (never log embed query tokens)
75
+ */
76
+ export function redactCredential(cred) {
77
+ if (cred.fromQuery)
78
+ return '[embedToken: REDACTED]';
79
+ const preview = cred.token.slice(0, cred.type === 'apiKey' ? 8 : 8);
80
+ return `[${cred.type}: ${preview}...]`;
81
+ }
82
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2CAA2C;AAC3C,8EAA8E;AAW9E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;IAE/B,2BAA2B;IAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IACzC,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAA;QAC5C,IAAI,KAAK;YAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IACvD,CAAC;IAED,eAAe;IACf,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACvC,IAAI,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IAEpD,wDAAwD;IACxD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IAChD,IAAI,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,CAAA;IAElE,oBAAoB;IACpB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACpC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;QAC5D,IAAI,YAAY;YAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,EAAE,CAAA;IACzE,CAAC;IAED,+CAA+C;IAC/C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAChC,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACrD,IAAI,UAAU;YAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;IACnF,CAAC;IAAC,MAAM,CAAC;QACP,4CAA4C;IAC9C,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,WAAW,CAAC,YAAoB,EAAE,IAAY;IACrD,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACnD,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACxB,OAAO,kBAAkB,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QACjD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,8EAA8E;AAC9E,qCAAqC;AACrC,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAyB;IACxD,IAAI,IAAI,CAAC,SAAS;QAAE,OAAO,wBAAwB,CAAA;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACnE,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,MAAM,CAAA;AACxC,CAAC"}
@@ -0,0 +1,85 @@
1
+ export type TokenType = 'session' | 'apiKey' | 'embed';
2
+ export interface ApiKeyPair {
3
+ /** The plaintext key shown to the user once. Format: `ntk_<64-char-hex>`. */
4
+ key: string;
5
+ /** SHA-256 hex digest for secure storage. Never return this to users. */
6
+ hash: string;
7
+ }
8
+ export interface EmbedTokenOptions {
9
+ /** Project identifier. */
10
+ projectId: string;
11
+ /** Allowed request origin (exact match). */
12
+ allowedOrigin: string;
13
+ /** Token TTL in seconds. Default: 3600 (1 hour). Max: 86400 (24 hours). */
14
+ ttlSeconds?: number;
15
+ /** Optional additional claims. */
16
+ claims?: Record<string, unknown>;
17
+ }
18
+ export interface EmbedTokenPayload {
19
+ projectId: string;
20
+ allowedOrigin: string;
21
+ exp: number;
22
+ iat: number;
23
+ claims?: Record<string, unknown>;
24
+ }
25
+ export interface EmbedTokenVerifyResult {
26
+ valid: true;
27
+ payload: EmbedTokenPayload;
28
+ }
29
+ export interface EmbedTokenInvalidResult {
30
+ valid: false;
31
+ reason: 'expired' | 'origin_mismatch' | 'invalid_signature' | 'malformed';
32
+ }
33
+ export type EmbedTokenResult = EmbedTokenVerifyResult | EmbedTokenInvalidResult;
34
+ /**
35
+ * Generates a new API key pair.
36
+ *
37
+ * The `key` is shown to the user once and never stored.
38
+ * The `hash` is stored in the database and used for verification.
39
+ *
40
+ * ```ts
41
+ * const { key, hash } = generateApiKey()
42
+ * await db.apiKeys.create({ hash, projectId, name })
43
+ * return { key } // shown once to user
44
+ * ```
45
+ */
46
+ export declare function generateApiKey(): ApiKeyPair;
47
+ /**
48
+ * Produces a SHA-256 hex digest of the plaintext API key for storage.
49
+ */
50
+ export declare function hashApiKey(key: string): string;
51
+ /**
52
+ * Verifies a plaintext API key against its stored hash using a
53
+ * constant-time comparison to prevent timing attacks.
54
+ *
55
+ * Returns `false` if either value is empty.
56
+ */
57
+ export declare function verifyApiKey(plaintextKey: string, storedHash: string): boolean;
58
+ /**
59
+ * Returns true if the string looks like an Airdraft API key.
60
+ * Does NOT validate the key against a stored hash.
61
+ */
62
+ export declare function isApiKeyFormat(value: string): boolean;
63
+ /**
64
+ * Issues a short-lived, origin-restricted embed token.
65
+ *
66
+ * Token format: `<b64url(payload)>.<b64url(hmac-sha256)>`
67
+ *
68
+ * ```ts
69
+ * const token = generateEmbedToken(
70
+ * { projectId: 'proj_123', allowedOrigin: 'https://example.com', ttlSeconds: 3600 },
71
+ * process.env.EMBED_TOKEN_SECRET!
72
+ * )
73
+ * ```
74
+ */
75
+ export declare function generateEmbedToken(opts: EmbedTokenOptions, secret: string): string;
76
+ /**
77
+ * Verifies an embed token against the secret and a request origin.
78
+ *
79
+ * Checks:
80
+ * 1. HMAC signature integrity
81
+ * 2. Token has not expired
82
+ * 3. Request origin matches `allowedOrigin` in the payload
83
+ */
84
+ export declare function verifyEmbedToken(token: string, secret: string, requestOrigin: string): EmbedTokenResult;
85
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEtD,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAA;IACX,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAA;IACrB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,IAAI,CAAA;IACX,OAAO,EAAE,iBAAiB,CAAA;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,SAAS,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,WAAW,CAAA;CAC1E;AAED,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,GAAG,uBAAuB,CAAA;AAS/E;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAK3C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAU9E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAiBlF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB,gBAAgB,CAuClB"}
package/dist/tokens.js ADDED
@@ -0,0 +1,138 @@
1
+ import { randomBytes, createHash, timingSafeEqual } from 'node:crypto';
2
+ // ---------------------------------------------------------------------------
3
+ // API Keys (ntk_*)
4
+ // ---------------------------------------------------------------------------
5
+ const API_KEY_PREFIX = 'ntk_';
6
+ const API_KEY_BYTES = 32; // 64 hex chars
7
+ /**
8
+ * Generates a new API key pair.
9
+ *
10
+ * The `key` is shown to the user once and never stored.
11
+ * The `hash` is stored in the database and used for verification.
12
+ *
13
+ * ```ts
14
+ * const { key, hash } = generateApiKey()
15
+ * await db.apiKeys.create({ hash, projectId, name })
16
+ * return { key } // shown once to user
17
+ * ```
18
+ */
19
+ export function generateApiKey() {
20
+ const raw = randomBytes(API_KEY_BYTES).toString('hex');
21
+ const key = `${API_KEY_PREFIX}${raw}`;
22
+ const hash = hashApiKey(key);
23
+ return { key, hash };
24
+ }
25
+ /**
26
+ * Produces a SHA-256 hex digest of the plaintext API key for storage.
27
+ */
28
+ export function hashApiKey(key) {
29
+ return createHash('sha256').update(key).digest('hex');
30
+ }
31
+ /**
32
+ * Verifies a plaintext API key against its stored hash using a
33
+ * constant-time comparison to prevent timing attacks.
34
+ *
35
+ * Returns `false` if either value is empty.
36
+ */
37
+ export function verifyApiKey(plaintextKey, storedHash) {
38
+ if (!plaintextKey || !storedHash)
39
+ return false;
40
+ try {
41
+ const expected = Buffer.from(storedHash, 'hex');
42
+ const actual = Buffer.from(hashApiKey(plaintextKey), 'hex');
43
+ if (expected.length !== actual.length)
44
+ return false;
45
+ return timingSafeEqual(expected, actual);
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ /**
52
+ * Returns true if the string looks like an Airdraft API key.
53
+ * Does NOT validate the key against a stored hash.
54
+ */
55
+ export function isApiKeyFormat(value) {
56
+ return value.startsWith(API_KEY_PREFIX) && value.length === API_KEY_PREFIX.length + API_KEY_BYTES * 2;
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // Embed tokens (HMAC-SHA256 signed, base64url encoded)
60
+ // ---------------------------------------------------------------------------
61
+ const MAX_EMBED_TTL = 86400;
62
+ function b64url(buf) {
63
+ return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
64
+ }
65
+ function b64urlDecode(s) {
66
+ const padded = s.replace(/-/g, '+').replace(/_/g, '/') + '=='.slice((s.length + 3) & 3);
67
+ return Buffer.from(padded, 'base64');
68
+ }
69
+ /**
70
+ * Issues a short-lived, origin-restricted embed token.
71
+ *
72
+ * Token format: `<b64url(payload)>.<b64url(hmac-sha256)>`
73
+ *
74
+ * ```ts
75
+ * const token = generateEmbedToken(
76
+ * { projectId: 'proj_123', allowedOrigin: 'https://example.com', ttlSeconds: 3600 },
77
+ * process.env.EMBED_TOKEN_SECRET!
78
+ * )
79
+ * ```
80
+ */
81
+ export function generateEmbedToken(opts, secret) {
82
+ const ttl = Math.min(opts.ttlSeconds ?? 3600, MAX_EMBED_TTL);
83
+ const now = Math.floor(Date.now() / 1000);
84
+ const payload = {
85
+ projectId: opts.projectId,
86
+ allowedOrigin: opts.allowedOrigin,
87
+ iat: now,
88
+ exp: now + ttl,
89
+ ...(opts.claims ? { claims: opts.claims } : {}),
90
+ };
91
+ const payloadB64 = b64url(Buffer.from(JSON.stringify(payload)));
92
+ const hmac = createHash('sha256')
93
+ .update(`${payloadB64}.${secret}`)
94
+ .digest();
95
+ const sig = b64url(hmac);
96
+ return `${payloadB64}.${sig}`;
97
+ }
98
+ /**
99
+ * Verifies an embed token against the secret and a request origin.
100
+ *
101
+ * Checks:
102
+ * 1. HMAC signature integrity
103
+ * 2. Token has not expired
104
+ * 3. Request origin matches `allowedOrigin` in the payload
105
+ */
106
+ export function verifyEmbedToken(token, secret, requestOrigin) {
107
+ const parts = token.split('.');
108
+ if (parts.length !== 2)
109
+ return { valid: false, reason: 'malformed' };
110
+ const [payloadB64, sig] = parts;
111
+ // Verify signature
112
+ const expectedSig = b64url(Buffer.from(createHash('sha256').update(`${payloadB64}.${secret}`).digest()));
113
+ const expectedBuf = Buffer.from(expectedSig);
114
+ const actualBuf = Buffer.from(sig);
115
+ if (expectedBuf.length !== actualBuf.length ||
116
+ !timingSafeEqual(expectedBuf, actualBuf)) {
117
+ return { valid: false, reason: 'invalid_signature' };
118
+ }
119
+ // Decode payload
120
+ let payload;
121
+ try {
122
+ payload = JSON.parse(b64urlDecode(payloadB64).toString());
123
+ }
124
+ catch {
125
+ return { valid: false, reason: 'malformed' };
126
+ }
127
+ // Check expiry
128
+ const now = Math.floor(Date.now() / 1000);
129
+ if (payload.exp < now) {
130
+ return { valid: false, reason: 'expired' };
131
+ }
132
+ // Check origin
133
+ if (payload.allowedOrigin !== requestOrigin) {
134
+ return { valid: false, reason: 'origin_mismatch' };
135
+ }
136
+ return { valid: true, payload };
137
+ }
138
+ //# sourceMappingURL=tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@airdraft/auth",
3
+ "version": "0.1.0",
4
+ "description": "Airdraft authentication utilities — session, API key, embed token",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["dist", "README.md", "CHANGELOG.md"],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsc --watch",
18
+ "clean": "rm -rf dist",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "typecheck": "tsc --noEmit",
22
+ "prepublishOnly": "npm run build",
23
+ "release": "standard-version",
24
+ "release:patch": "standard-version --release-as patch",
25
+ "release:minor": "standard-version --release-as minor",
26
+ "release:major": "standard-version --release-as major"
27
+ },
28
+ "publishConfig": { "access": "public" },
29
+ "license": "MIT",
30
+ "devDependencies": {
31
+ "@types/node": "^20.0.0",
32
+ "standard-version": "^9.5.0",
33
+ "tsx": "^4.7.0",
34
+ "typescript": "^5.4.0",
35
+ "vitest": "^1.5.0"
36
+ },
37
+ "dependencies": {
38
+ "@airdraft/core": "*",
39
+ "zod": "^3.23.0"
40
+ },
41
+ "engines": { "node": ">=18.0.0" }
42
+ }