@auth-gate/core 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.
@@ -0,0 +1,569 @@
1
+ /**
2
+ * The authenticated user object returned after successful authentication.
3
+ *
4
+ * This is the primary data structure stored in the encrypted session cookie
5
+ * and returned by session helpers like `getSession()`.
6
+ *
7
+ * Fields are nullable because different auth providers populate different
8
+ * fields — e.g. SMS auth only provides `phone`, OAuth provides `email`
9
+ * and `picture`, etc.
10
+ */
11
+ interface AuthGateUser {
12
+ /** Unique user identifier assigned by AuthGate. */
13
+ id: string;
14
+ /** User's email address. Present for email/OAuth flows, `null` for SMS-only users. */
15
+ email: string | null;
16
+ /** User's phone number in E.164 format. Present for SMS flows, `null` otherwise. */
17
+ phone: string | null;
18
+ /** User's display name from the OAuth provider or signup form. */
19
+ name: string | null;
20
+ /** URL to the user's profile picture from the OAuth provider. */
21
+ picture: string | null;
22
+ /** The authentication provider used (e.g. `"google"`, `"github"`, `"email"`, `"sms"`). */
23
+ provider: string;
24
+ }
25
+ /**
26
+ * Result of verifying a JWT token against the AuthGate API.
27
+ *
28
+ * When `valid` is `true`, the `user` and `expiresAt` fields are populated.
29
+ * When `valid` is `false`, `error` describes why verification failed.
30
+ */
31
+ interface TokenVerifyResult {
32
+ /** Whether the token is valid and not expired. */
33
+ valid: boolean;
34
+ /** The authenticated user, present when `valid` is `true`. */
35
+ user?: AuthGateUser;
36
+ /** ISO 8601 timestamp when the token expires, present when `valid` is `true`. */
37
+ expiresAt?: string;
38
+ /** Human-readable error message, present when `valid` is `false`. */
39
+ error?: string;
40
+ }
41
+ /**
42
+ * Configuration for initializing an {@link AuthGateClient}.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * import { createAuthGateClient } from "@auth-gate/core";
47
+ *
48
+ * const client = createAuthGateClient({
49
+ * apiKey: process.env.AUTHGATE_API_KEY!,
50
+ * projectId: process.env.AUTHGATE_PROJECT_ID!,
51
+ * baseUrl: "https://authgate.dev",
52
+ * sessionSecret: process.env.SESSION_SECRET!, // min 32 chars
53
+ * });
54
+ * ```
55
+ */
56
+ interface AuthGateConfig {
57
+ /** Server-side API key for your AuthGate project. Found in the dashboard under API Keys. */
58
+ apiKey: string;
59
+ /** The project ID from your AuthGate dashboard. */
60
+ projectId: string;
61
+ /** Base URL of the AuthGate service (e.g. `"https://authgate.dev"`). */
62
+ baseUrl: string;
63
+ /**
64
+ * Secret used to derive encryption keys for session cookies and OAuth state.
65
+ * Must be at least 32 characters. Use a cryptographically random string.
66
+ */
67
+ sessionSecret: string;
68
+ /**
69
+ * Name of the session cookie.
70
+ * @defaultValue `"__authgate"`
71
+ */
72
+ cookieName?: string;
73
+ /**
74
+ * Session lifetime in seconds. Controls how long the encrypted session cookie
75
+ * and refresh token remain valid.
76
+ * @defaultValue `604800` (7 days)
77
+ */
78
+ sessionMaxAge?: number;
79
+ /**
80
+ * Path where the OAuth callback handler is mounted.
81
+ * @defaultValue `"/api/auth/callback"`
82
+ */
83
+ callbackPath?: string;
84
+ }
85
+ /**
86
+ * Internal payload structure stored inside the encrypted session cookie.
87
+ * Contains the user data plus issued-at and expiration timestamps.
88
+ */
89
+ interface SessionPayload {
90
+ /** The authenticated user data. */
91
+ user: AuthGateUser;
92
+ /** Unix timestamp (seconds) when the session was created. */
93
+ iat: number;
94
+ /** Unix timestamp (seconds) when the session expires. */
95
+ exp: number;
96
+ }
97
+ /**
98
+ * Options for configuring HTTP cookies set by AuthGate.
99
+ * Maps directly to standard `Set-Cookie` attributes.
100
+ */
101
+ interface CookieOptions {
102
+ /** Prevents client-side JavaScript from reading the cookie. */
103
+ httpOnly: boolean;
104
+ /** Only send the cookie over HTTPS connections. */
105
+ secure: boolean;
106
+ /** Controls when the cookie is sent with cross-site requests. */
107
+ sameSite: "lax" | "strict" | "none";
108
+ /** Cookie lifetime in seconds. */
109
+ maxAge: number;
110
+ /** URL path the cookie is valid for. */
111
+ path: string;
112
+ }
113
+ /**
114
+ * Supported OAuth provider identifiers.
115
+ *
116
+ * Pass one of these to {@link AuthGateClient.getOAuthUrl} to initiate an OAuth flow.
117
+ */
118
+ type OAuthProvider = "google" | "github" | "discord" | "azure" | "apple";
119
+ /**
120
+ * Response returned when MFA verification is required during sign-in.
121
+ *
122
+ * The client should redirect the user to an MFA verification page,
123
+ * passing the `mfa_challenge` token and available `mfa_methods`.
124
+ */
125
+ interface MfaChallengeResponse {
126
+ /** Always `true` — indicates MFA is required to complete authentication. */
127
+ mfa_required: true;
128
+ /** Opaque challenge token to pass to the MFA verify endpoint. */
129
+ mfa_challenge: string;
130
+ /** List of MFA methods the user has enrolled (e.g. `["totp", "sms"]`). */
131
+ mfa_methods: string[];
132
+ }
133
+ /**
134
+ * Current MFA enrollment status for a user.
135
+ * Returned by the `/api/proxy/mfa/status` endpoint.
136
+ */
137
+ interface MfaStatus {
138
+ /** Whether the user has any MFA method enrolled. */
139
+ enrolled: boolean;
140
+ /** List of enrolled MFA methods (e.g. `["totp"]`, `["sms", "totp"]`). */
141
+ methods: string[];
142
+ /** Whether MFA is required by the project's policy. */
143
+ required: boolean;
144
+ }
145
+ /**
146
+ * Response from TOTP setup containing the secret and QR code.
147
+ * Returned by the `/api/proxy/mfa/totp/setup` endpoint.
148
+ */
149
+ interface TotpSetupResponse {
150
+ /** Base32-encoded TOTP secret for manual entry. */
151
+ secret: string;
152
+ /** Data URL of the QR code image (PNG) for scanning with an authenticator app. */
153
+ qr_code: string;
154
+ /** `otpauth://` URI for the TOTP secret. */
155
+ uri: string;
156
+ }
157
+ /**
158
+ * Response from generating new MFA backup codes.
159
+ * Returned by the `/api/proxy/mfa/backup-codes/generate` endpoint.
160
+ *
161
+ * Each code can only be used once. Generating new codes invalidates all previous ones.
162
+ */
163
+ interface BackupCodesResponse {
164
+ /** Array of single-use backup codes. Store these securely — they are shown only once. */
165
+ codes: string[];
166
+ }
167
+
168
+ /**
169
+ * Core client for interacting with the AuthGate authentication proxy.
170
+ *
171
+ * Handles token verification, session cookie encryption/decryption,
172
+ * OAuth state management, and all proxy API calls (email, SMS, MFA).
173
+ *
174
+ * Use {@link createAuthGateClient} for a convenient factory function,
175
+ * or instantiate directly with `new AuthGateClient(config)`.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * const client = new AuthGateClient({
180
+ * apiKey: process.env.AUTHGATE_API_KEY!,
181
+ * projectId: process.env.AUTHGATE_PROJECT_ID!,
182
+ * baseUrl: "https://authgate.dev",
183
+ * sessionSecret: process.env.SESSION_SECRET!,
184
+ * });
185
+ * ```
186
+ */
187
+ declare class AuthGateClient {
188
+ private readonly config;
189
+ private encryptionKey;
190
+ private stateKey;
191
+ private keysReady;
192
+ /**
193
+ * @param config - Client configuration. See {@link AuthGateConfig}.
194
+ * @throws {@link AuthGateError} if `sessionSecret` is shorter than 32 characters.
195
+ */
196
+ constructor(config: AuthGateConfig);
197
+ private deriveKeys;
198
+ private getEncryptionKey;
199
+ private getStateKey;
200
+ /**
201
+ * Verify a JWT token against the AuthGate API.
202
+ *
203
+ * Makes a server-side `POST /api/v1/token/verify` call using your API key.
204
+ * Times out after 5 seconds.
205
+ *
206
+ * @param token - The JWT token string from the OAuth callback or email/SMS flow.
207
+ * @returns A {@link TokenVerifyResult} with the user data if valid.
208
+ * @throws {@link TokenVerificationError} if the request times out.
209
+ */
210
+ verifyToken(token: string): Promise<TokenVerifyResult>;
211
+ /**
212
+ * Encrypt user data into a session cookie value.
213
+ *
214
+ * Uses AES-256-GCM with a key derived from your session secret.
215
+ * The returned string is safe to store in an httpOnly cookie.
216
+ *
217
+ * @param user - The authenticated user to store.
218
+ * @returns Base64url-encoded encrypted session string.
219
+ */
220
+ encryptSession(user: AuthGateUser): Promise<string>;
221
+ /**
222
+ * Decrypt a session cookie and return the user if the session is valid.
223
+ *
224
+ * Returns `null` if the cookie is expired, tampered with, or encrypted
225
+ * with a different secret. All failure modes return `null` to prevent
226
+ * oracle attacks.
227
+ *
228
+ * @param cookieValue - The encrypted cookie string.
229
+ * @returns The user, or `null` if the session is invalid.
230
+ */
231
+ decryptSession(cookieValue: string): Promise<AuthGateUser | null>;
232
+ /**
233
+ * Decrypt a session cookie and return the full payload (user + timestamps).
234
+ *
235
+ * Used internally by middleware to check `iat` for session revalidation.
236
+ *
237
+ * @param cookieValue - The encrypted cookie string.
238
+ * @returns The full session payload, or `null` if invalid.
239
+ * @internal
240
+ */
241
+ decryptSessionPayload(cookieValue: string): Promise<SessionPayload | null>;
242
+ /**
243
+ * Create a signed OAuth state parameter and nonce for CSRF protection.
244
+ *
245
+ * Store the `nonce` in a short-lived httpOnly cookie and pass `state`
246
+ * as a query parameter in the OAuth authorization URL.
247
+ *
248
+ * @returns `{ state, nonce }` — the state string and its corresponding nonce.
249
+ */
250
+ createState(): Promise<{
251
+ state: string;
252
+ nonce: string;
253
+ }>;
254
+ /**
255
+ * Verify an OAuth state parameter returned in the callback.
256
+ *
257
+ * @param state - The `state` query parameter from the callback.
258
+ * @param nonce - The nonce from the httpOnly cookie.
259
+ * @returns `true` if the state is valid and not expired.
260
+ */
261
+ verifyState(state: string, nonce: string): Promise<boolean>;
262
+ /**
263
+ * Build the OAuth authorization URL for a given provider.
264
+ *
265
+ * @param provider - The OAuth provider (e.g. `"google"`, `"github"`).
266
+ * @param callbackUrl - Your app's callback URL where the user is redirected after auth.
267
+ * @param options - Optional parameters.
268
+ * @param options.linkTo - An existing user ID to link this OAuth account to.
269
+ * @returns The full authorization URL to redirect the user to.
270
+ * @throws {@link AuthGateError} if the provider is not in {@link SUPPORTED_PROVIDERS}.
271
+ */
272
+ getOAuthUrl(provider: OAuthProvider, callbackUrl: string, options?: {
273
+ linkTo?: string;
274
+ }): string;
275
+ /**
276
+ * Register a new user with email and password.
277
+ *
278
+ * @param data - Signup data including email, password, optional name, and callback URL.
279
+ * @returns The raw `Response` from the AuthGate proxy.
280
+ */
281
+ emailSignup(data: {
282
+ email: string;
283
+ password: string;
284
+ name?: string;
285
+ callbackUrl: string;
286
+ }): Promise<Response>;
287
+ /**
288
+ * Sign in an existing user with email and password.
289
+ *
290
+ * The response may include `mfa_required: true` if the user has MFA enabled,
291
+ * in which case you should redirect to an MFA verification page.
292
+ *
293
+ * @param data - Sign-in data including email, password, and callback URL.
294
+ * @returns The raw `Response` from the AuthGate proxy.
295
+ */
296
+ emailSignin(data: {
297
+ email: string;
298
+ password: string;
299
+ callbackUrl: string;
300
+ }): Promise<Response>;
301
+ /**
302
+ * Send a password reset email to the user.
303
+ *
304
+ * @param data - The user's email and the URL to redirect to after clicking the reset link.
305
+ * @returns The raw `Response` from the AuthGate proxy.
306
+ */
307
+ emailForgotPassword(data: {
308
+ email: string;
309
+ callbackUrl: string;
310
+ }): Promise<Response>;
311
+ /**
312
+ * Confirm a password reset with the token from the reset email.
313
+ *
314
+ * @param data - The reset token and the new password.
315
+ * @returns The raw `Response` from the AuthGate proxy.
316
+ */
317
+ emailResetPassword(data: {
318
+ token: string;
319
+ password: string;
320
+ }): Promise<Response>;
321
+ /**
322
+ * Send a magic link email for passwordless authentication.
323
+ *
324
+ * @param data - The user's email and callback URL.
325
+ * @returns The raw `Response` from the AuthGate proxy.
326
+ */
327
+ magicLinkSend(data: {
328
+ email: string;
329
+ callbackUrl: string;
330
+ }): Promise<Response>;
331
+ /**
332
+ * Verify an email address using a one-time code.
333
+ *
334
+ * @param data - The user's email and the verification code.
335
+ * @returns The raw `Response` from the AuthGate proxy.
336
+ */
337
+ emailVerifyCode(data: {
338
+ email: string;
339
+ code: string;
340
+ }): Promise<Response>;
341
+ /**
342
+ * Send an SMS verification code to the user's phone number.
343
+ *
344
+ * @param data - The phone number (E.164 format) and callback URL.
345
+ * @returns The raw `Response` from the AuthGate proxy.
346
+ */
347
+ smsSendCode(data: {
348
+ phone: string;
349
+ callbackUrl: string;
350
+ }): Promise<Response>;
351
+ /**
352
+ * Verify an SMS code and authenticate the user.
353
+ *
354
+ * @param data - The phone number, verification code, and callback URL.
355
+ * @returns The raw `Response` from the AuthGate proxy.
356
+ */
357
+ smsVerifyCode(data: {
358
+ phone: string;
359
+ code: string;
360
+ callbackUrl: string;
361
+ }): Promise<Response>;
362
+ /**
363
+ * Exchange a refresh token for a new JWT.
364
+ *
365
+ * Refresh tokens are single-use — each call returns a new JWT and
366
+ * invalidates the previous refresh token (rotation).
367
+ *
368
+ * @param refreshToken - The opaque refresh token.
369
+ * @returns A new JWT and its `expires_in` (seconds), or `null` if the refresh token is invalid.
370
+ */
371
+ refreshToken(refreshToken: string): Promise<{
372
+ token: string;
373
+ expires_in: number;
374
+ } | null>;
375
+ /**
376
+ * Revoke a session by its refresh token.
377
+ *
378
+ * After revocation, the refresh token can no longer be used to obtain
379
+ * new JWTs. The user will need to re-authenticate.
380
+ *
381
+ * @param refreshToken - The opaque refresh token to revoke.
382
+ * @returns `true` if the session was revoked, `false` on failure.
383
+ */
384
+ revokeSession(refreshToken: string): Promise<boolean>;
385
+ /**
386
+ * Verify an MFA code to complete authentication.
387
+ *
388
+ * Called after a sign-in response includes `mfa_required: true`.
389
+ *
390
+ * @param data - The MFA challenge token, verification code, and method.
391
+ * @returns The raw `Response` — includes a JWT on success.
392
+ */
393
+ mfaVerify(data: {
394
+ challenge: string;
395
+ code: string;
396
+ method: "totp" | "sms" | "backup_code";
397
+ }): Promise<Response>;
398
+ /**
399
+ * Begin TOTP (authenticator app) setup for the authenticated user.
400
+ *
401
+ * Requires a valid JWT in the `Authorization: Bearer` header.
402
+ *
403
+ * @param token - The user's current JWT.
404
+ * @returns The raw `Response` with TOTP secret, QR code, and URI.
405
+ */
406
+ mfaTotpSetup(token: string): Promise<Response>;
407
+ /**
408
+ * Confirm TOTP setup by verifying a code from the authenticator app.
409
+ *
410
+ * @param token - The user's current JWT.
411
+ * @param code - The 6-digit TOTP code from the authenticator app.
412
+ * @returns The raw `Response` — confirms enrollment on success.
413
+ */
414
+ mfaTotpVerifySetup(token: string, code: string): Promise<Response>;
415
+ /**
416
+ * Disable TOTP for the authenticated user.
417
+ *
418
+ * @param token - The user's current JWT.
419
+ * @param code - A valid TOTP code to confirm the action.
420
+ * @returns The raw `Response`.
421
+ */
422
+ mfaTotpDisable(token: string, code: string): Promise<Response>;
423
+ /**
424
+ * Enable SMS-based MFA for the authenticated user.
425
+ *
426
+ * Uses the phone number already associated with the user's account.
427
+ *
428
+ * @param token - The user's current JWT.
429
+ * @returns The raw `Response`.
430
+ */
431
+ mfaSmsEnable(token: string): Promise<Response>;
432
+ /**
433
+ * Disable SMS-based MFA for the authenticated user.
434
+ *
435
+ * @param token - The user's current JWT.
436
+ * @returns The raw `Response`.
437
+ */
438
+ mfaSmsDisable(token: string): Promise<Response>;
439
+ /**
440
+ * Generate a new set of single-use MFA backup codes.
441
+ *
442
+ * Generating new codes invalidates all previously issued backup codes.
443
+ *
444
+ * @param token - The user's current JWT.
445
+ * @returns The raw `Response` with an array of backup codes.
446
+ */
447
+ mfaBackupCodesGenerate(token: string): Promise<Response>;
448
+ /**
449
+ * Get the current MFA enrollment status for the authenticated user.
450
+ *
451
+ * @param token - The user's current JWT.
452
+ * @returns The raw `Response` with {@link MfaStatus} data.
453
+ */
454
+ mfaStatus(token: string): Promise<Response>;
455
+ /**
456
+ * Send an SMS code for MFA verification during the challenge flow.
457
+ *
458
+ * @param challenge - The MFA challenge token from the sign-in response.
459
+ * @returns The raw `Response`.
460
+ */
461
+ mfaSmsSendCode(challenge: string): Promise<Response>;
462
+ /** The name of the session cookie (default: `"__authgate"`). */
463
+ get cookieName(): string;
464
+ /** The name of the OAuth nonce cookie (`"__authgate_nonce"`). */
465
+ get nonceCookieName(): string;
466
+ /** The name of the refresh token cookie (`"__authgate_refresh"`). */
467
+ get refreshTokenCookieName(): string;
468
+ /** The callback path where OAuth redirects are handled (default: `"/api/auth/callback"`). */
469
+ get callbackPath(): string;
470
+ /** The AuthGate project ID. */
471
+ get projectId(): string;
472
+ /** The AuthGate service base URL. */
473
+ get baseUrl(): string;
474
+ /** Session lifetime in seconds. */
475
+ get sessionMaxAge(): number;
476
+ /**
477
+ * Get cookie options for the session cookie.
478
+ *
479
+ * Returns `secure: true` in production, `httpOnly: true` always.
480
+ */
481
+ getSessionCookieOptions(): CookieOptions;
482
+ /**
483
+ * Get cookie options for the OAuth nonce cookie.
484
+ *
485
+ * Short-lived (10 minutes) to match the state TTL.
486
+ */
487
+ getNonceCookieOptions(): CookieOptions;
488
+ }
489
+ /**
490
+ * Create an {@link AuthGateClient} instance.
491
+ *
492
+ * Convenience factory — equivalent to `new AuthGateClient(config)`.
493
+ *
494
+ * @param config - Client configuration. See {@link AuthGateConfig}.
495
+ * @returns A configured {@link AuthGateClient}.
496
+ */
497
+ declare function createAuthGateClient(config: AuthGateConfig): AuthGateClient;
498
+
499
+ /** Default name for the encrypted session cookie. */
500
+ declare const DEFAULT_COOKIE_NAME = "__authgate";
501
+ /** Cookie name used to store the OAuth CSRF nonce during the authorization flow. */
502
+ declare const NONCE_COOKIE_NAME = "__authgate_nonce";
503
+ /** Cookie name used to store the opaque refresh token. */
504
+ declare const REFRESH_TOKEN_COOKIE_NAME = "__authgate_refresh";
505
+ /**
506
+ * Default session lifetime in seconds (7 days).
507
+ *
508
+ * Can be overridden per-client via {@link AuthGateConfig.sessionMaxAge}
509
+ * or per-project in the AuthGate dashboard under Settings > Session Duration.
510
+ */
511
+ declare const SESSION_MAX_AGE = 604800;
512
+ /** Maximum age of an OAuth state parameter before it is considered expired (10 minutes). */
513
+ declare const STATE_TTL_MS = 600000;
514
+ /**
515
+ * List of OAuth providers supported by AuthGate.
516
+ *
517
+ * Configure provider credentials in the AuthGate dashboard under Providers.
518
+ */
519
+ declare const SUPPORTED_PROVIDERS: OAuthProvider[];
520
+
521
+ /**
522
+ * Base error class for all AuthGate errors.
523
+ *
524
+ * All errors thrown by the `@auth-gate/*` packages extend this class,
525
+ * so you can catch `AuthGateError` to handle any AuthGate-specific failure.
526
+ *
527
+ * @example
528
+ * ```ts
529
+ * try {
530
+ * await client.verifyToken(token);
531
+ * } catch (err) {
532
+ * if (err instanceof AuthGateError) {
533
+ * console.error("AuthGate error:", err.message);
534
+ * }
535
+ * }
536
+ * ```
537
+ */
538
+ declare class AuthGateError extends Error {
539
+ constructor(message: string);
540
+ }
541
+ /**
542
+ * Thrown when JWT token verification fails or times out.
543
+ *
544
+ * This can occur when the token is malformed, expired, or the
545
+ * AuthGate API is unreachable within the 5-second timeout.
546
+ */
547
+ declare class TokenVerificationError extends AuthGateError {
548
+ constructor(message?: string);
549
+ }
550
+ /**
551
+ * Thrown when an encrypted session cookie cannot be decrypted.
552
+ *
553
+ * Common causes: the session secret was rotated, the cookie was
554
+ * tampered with, or the cookie format version is unsupported.
555
+ */
556
+ declare class SessionDecryptionError extends AuthGateError {
557
+ constructor(message?: string);
558
+ }
559
+ /**
560
+ * Thrown when OAuth state verification fails.
561
+ *
562
+ * This indicates a potential CSRF attack, an expired state parameter
563
+ * (older than 10 minutes), or a nonce mismatch.
564
+ */
565
+ declare class InvalidStateError extends AuthGateError {
566
+ constructor(message?: string);
567
+ }
568
+
569
+ export { AuthGateClient, type AuthGateConfig, AuthGateError, type AuthGateUser, type BackupCodesResponse, type CookieOptions, DEFAULT_COOKIE_NAME, InvalidStateError, type MfaChallengeResponse, type MfaStatus, NONCE_COOKIE_NAME, type OAuthProvider, REFRESH_TOKEN_COOKIE_NAME, SESSION_MAX_AGE, STATE_TTL_MS, SUPPORTED_PROVIDERS, SessionDecryptionError, type SessionPayload, TokenVerificationError, type TokenVerifyResult, type TotpSetupResponse, createAuthGateClient };