@atlasauth/backend 0.23.0 → 0.24.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/auth0-session.d.ts +23 -0
  3. package/dist/auth0-session.js +80 -0
  4. package/dist/auth0-session.js.map +1 -0
  5. package/dist/better-auth.d.ts +4 -8
  6. package/dist/better-auth.js +42 -150
  7. package/dist/better-auth.js.map +1 -1
  8. package/dist/client/index.d.ts +54 -0
  9. package/dist/client/index.js +4 -0
  10. package/dist/client/index.js.map +1 -1
  11. package/dist/client/migrations.d.ts +286 -0
  12. package/dist/client/migrations.js +103 -0
  13. package/dist/client/migrations.js.map +1 -0
  14. package/dist/framework-bridge.d.ts +98 -0
  15. package/dist/framework-bridge.js +264 -0
  16. package/dist/framework-bridge.js.map +1 -0
  17. package/dist/framework-sessions.d.ts +147 -0
  18. package/dist/framework-sessions.js +544 -0
  19. package/dist/framework-sessions.js.map +1 -0
  20. package/dist/index.d.ts +8 -1
  21. package/dist/index.js +11 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/legacy-issuer-presets.d.ts +85 -0
  24. package/dist/legacy-issuer-presets.js +165 -0
  25. package/dist/legacy-issuer-presets.js.map +1 -0
  26. package/dist/legacy-issuers.d.ts +16 -2
  27. package/dist/legacy-issuers.js +16 -1
  28. package/dist/legacy-issuers.js.map +1 -1
  29. package/dist/legacy-verify.d.ts +103 -0
  30. package/dist/legacy-verify.js +244 -0
  31. package/dist/legacy-verify.js.map +1 -0
  32. package/dist/migration-bridge.d.ts +153 -0
  33. package/dist/migration-bridge.js +328 -0
  34. package/dist/migration-bridge.js.map +1 -0
  35. package/dist/migration-presets.d.ts +150 -0
  36. package/dist/migration-presets.js +440 -0
  37. package/dist/migration-presets.js.map +1 -0
  38. package/package.json +1 -1
@@ -0,0 +1,286 @@
1
+ import { type BetterAuthImportActionItem } from './better-auth-imports';
2
+ import { type RequestFn } from './request';
3
+ import type { CursorPage, CursorParams } from './types';
4
+ /**
5
+ * Migrate to Atlas from any source: the vendor-neutral surface every vendor
6
+ * plugs into (`{source}`: `auth0`, `clerk`, `cognito`, `firebase`, …,
7
+ * `better_auth`, `custom`).
8
+ *
9
+ * imports `/v1/migrations/{source}/imports` — a sealed migration bundle
10
+ * (`@atlasauth/migrate`, or `atlas migrate export <vendor>`):
11
+ * `getPublicKey()`, `upload()` (create + parts + finalize), read the
12
+ * `report`, `apply()`; applying again after a failure resumes.
13
+ * bridge `/v1/migrations/{source}/bridge` — the zero-logout bridge settings
14
+ * (a vendor JWT preset, or imported sessions).
15
+ * sources `/v1/migrations/sources` — what each source supports.
16
+ */
17
+ export type MigrationSourceId = 'better_auth' | 'auth0' | 'clerk' | 'cognito' | 'firebase' | 'supabase' | 'workos' | 'authjs' | 'okta' | 'stytch' | 'keycloak' | 'fusionauth' | 'kinde' | 'descope' | 'lucia' | 'ory' | 'frontegg' | 'custom';
18
+ export type MigrationImportStatus = 'uploading' | 'analyzing' | 'ready' | 'invalid' | 'queued' | 'applying' | 'completed' | 'failed';
19
+ export interface MigrationImportKey {
20
+ object: 'migration_import_key';
21
+ kid: string;
22
+ /** base64url raw X25519 public key. One key serves every source. */
23
+ public_key: string;
24
+ alg: 'X25519-HKDF-SHA256-A256GCM';
25
+ status: 'active' | 'retired';
26
+ fingerprint: string;
27
+ /** `kid:public_key`, the form the exporters take. */
28
+ compact: string;
29
+ created_at: number;
30
+ retired_at: number | null;
31
+ }
32
+ /** Options of a non-Better-Auth import (a `better_auth` import takes {@link BetterAuthImportOptions}). */
33
+ export interface MigrationImportOptions {
34
+ models?: Array<'organization' | 'role' | 'user' | 'identity' | 'mfa_factor' | 'passkey' | 'membership' | 'invitation' | 'api_key' | 'sso_connection' | 'jwks' | 'session'>;
35
+ on_email_conflict?: 'skip' | 'link';
36
+ create_app_roles?: boolean;
37
+ import_oauth_tokens?: boolean;
38
+ passkeys_on_rp_mismatch?: 'skip' | 'import';
39
+ import_sessions?: boolean;
40
+ import_jwks?: boolean;
41
+ jwks_cutover_days?: number;
42
+ /** Source provider name → Atlas provider key. */
43
+ provider_map?: Record<string, string>;
44
+ /** Hash parameters that live outside the export, per algorithm id (Firebase signer key …). */
45
+ password_hash_params?: Record<string, Record<string, string | number | boolean>>;
46
+ retain_bundle?: boolean;
47
+ [option: string]: unknown;
48
+ }
49
+ interface MigrationReportCommon {
50
+ skipped: Array<{
51
+ model: string;
52
+ row_id: string | null;
53
+ reason_code: string;
54
+ reason: string;
55
+ }>;
56
+ skipped_total: number;
57
+ skipped_by_code: Record<string, number>;
58
+ conflicts: Array<{
59
+ model: string;
60
+ row_id: string | null;
61
+ code: string;
62
+ message: string;
63
+ resolution: 'merge' | 'skip';
64
+ existing_id: string | null;
65
+ }>;
66
+ conflicts_total: number;
67
+ action_items: BetterAuthImportActionItem[];
68
+ warnings: string[];
69
+ notes: Record<string, unknown>;
70
+ }
71
+ export interface MigrationImportReport extends MigrationReportCommon {
72
+ models: Array<{
73
+ model: string;
74
+ label: string;
75
+ found: number;
76
+ will_create: number;
77
+ will_update: number;
78
+ will_skip: number;
79
+ }>;
80
+ /** Users per password-hash algorithm. */
81
+ password_algorithms?: Record<string, number>;
82
+ }
83
+ export interface MigrationImportResult extends MigrationReportCommon {
84
+ models: Array<{
85
+ model: string;
86
+ label: string;
87
+ found: number;
88
+ created: number;
89
+ updated: number;
90
+ unchanged: number;
91
+ skipped: number;
92
+ failed: number;
93
+ }>;
94
+ completed_at: number | null;
95
+ }
96
+ export interface MigrationImport {
97
+ object: 'migration_import';
98
+ id: string;
99
+ source: MigrationSourceId;
100
+ format: 'atlas-migration-bundle' | 'atlas-better-auth-bundle';
101
+ status: MigrationImportStatus;
102
+ key_id: string | null;
103
+ parts: number;
104
+ bytes: number;
105
+ bundle_sha256: string | null;
106
+ bundle_purged: boolean;
107
+ options: Record<string, unknown>;
108
+ manifest: {
109
+ exported_at: string;
110
+ exporter: {
111
+ name: string;
112
+ version: string;
113
+ };
114
+ origin: string | null;
115
+ source_version: string | null;
116
+ models: string[];
117
+ providers: string[];
118
+ rp_id: string | null;
119
+ warnings: string[];
120
+ } | null;
121
+ total: number;
122
+ progress: {
123
+ total: number;
124
+ processed: number;
125
+ succeeded: number;
126
+ failed: number;
127
+ percent: number;
128
+ } | null;
129
+ report: MigrationImportReport | null;
130
+ result: MigrationImportResult | null;
131
+ error: {
132
+ code: string;
133
+ message: string;
134
+ } | null;
135
+ created_at: number;
136
+ updated_at: number;
137
+ analyzed_at: number | null;
138
+ apply_started_at: number | null;
139
+ completed_at: number | null;
140
+ }
141
+ export interface MigrationClaimMap {
142
+ subject?: string;
143
+ email?: string;
144
+ email_verified?: string;
145
+ name?: string;
146
+ session_key?: string;
147
+ mfa?: string;
148
+ }
149
+ /** PUT body of the bridge settings; every field optional, merged onto the stored ones. */
150
+ export interface MigrationBridgeSettingsUpdate {
151
+ enabled?: boolean;
152
+ ends_at?: string | number | null;
153
+ mode?: 'jwt' | 'session_table' | 'remote';
154
+ preset?: string | null;
155
+ preset_vars?: Record<string, string>;
156
+ issuer?: string | null;
157
+ jwks_url?: string | null;
158
+ audience?: string[] | null;
159
+ algorithms?: string[] | null;
160
+ claim_map?: MigrationClaimMap;
161
+ expired_grace_seconds?: number;
162
+ cookie_names?: string[];
163
+ create_users?: boolean;
164
+ /** Write-only HS256 secret; omit to keep, null to clear. */
165
+ shared_secret?: string | null;
166
+ }
167
+ export interface MigrationBridgeSettings {
168
+ object: 'migration_bridge';
169
+ source: MigrationSourceId;
170
+ enabled: boolean;
171
+ ends_at: number | null;
172
+ mode: 'jwt' | 'session_table' | 'remote';
173
+ preset: string | null;
174
+ preset_vars: Record<string, string>;
175
+ issuer: string | null;
176
+ jwks_url: string | null;
177
+ audience: string[] | null;
178
+ algorithms: string[];
179
+ claim_map: Record<string, string>;
180
+ expired_grace_seconds: number;
181
+ cookie_names: string[];
182
+ create_users: boolean;
183
+ has_shared_secret: boolean;
184
+ exchange_path: string;
185
+ stats: {
186
+ exchanged: number;
187
+ from_import: number;
188
+ from_jwt: number;
189
+ };
190
+ created_at: number | null;
191
+ updated_at: number | null;
192
+ }
193
+ export interface MigrationSourceView {
194
+ object: 'migration_source';
195
+ source: MigrationSourceId;
196
+ label: string;
197
+ models: string[];
198
+ passwords: 'export' | 'ticket' | 'lazy' | 'none';
199
+ export_via: Array<'api' | 'file' | 'database' | 'support_ticket'>;
200
+ bridge: {
201
+ mode: 'jwt' | 'session_table' | 'remote' | 'none';
202
+ preset: string | null;
203
+ vars: Record<string, string>;
204
+ cookies: string[];
205
+ };
206
+ lazy_migration: {
207
+ preset: string;
208
+ config: Record<string, {
209
+ label: string;
210
+ required: boolean;
211
+ }>;
212
+ secret: {
213
+ label: string;
214
+ required: boolean;
215
+ } | null;
216
+ } | null;
217
+ password_algorithms: string[];
218
+ limits: string | null;
219
+ }
220
+ export declare function migrationsResource(request: RequestFn): {
221
+ /** Every migration source and what it supports. */
222
+ sources(): Promise<CursorPage<MigrationSourceView>>;
223
+ /** The import surface of one source: `migrations.imports('auth0').upload(text)`. */
224
+ imports: (source: MigrationSourceId | string) => {
225
+ /** The import public key (created on first read; shared by every source). */
226
+ getPublicKey(): Promise<MigrationImportKey>;
227
+ /** Replace the import key; bundles sealed to the old one no longer finalize. */
228
+ rotatePublicKey(): Promise<MigrationImportKey>;
229
+ create(body?: {
230
+ options?: MigrationImportOptions;
231
+ }, idempotencyKey?: string): Promise<MigrationImport>;
232
+ list(params?: CursorParams): Promise<CursorPage<MigrationImport>>;
233
+ get(id: string): Promise<MigrationImport>;
234
+ /** Upload part `part` (1-based) of the sealed bundle text. */
235
+ uploadPart(id: string, part: number, data: string): Promise<{
236
+ object: "migration_import_part";
237
+ import_id: string;
238
+ part: number;
239
+ bytes: number;
240
+ sha256: string;
241
+ }>;
242
+ /** Close the upload; Atlas checks the key and the format, then runs the dry run. */
243
+ finalize(id: string): Promise<MigrationImport>;
244
+ dryRun(id: string, body?: {
245
+ options?: MigrationImportOptions;
246
+ }): Promise<MigrationImport>;
247
+ /** Import (or resume). Pass confirm_conflicts when the dry run reported conflicts. */
248
+ apply(id: string, body?: {
249
+ confirm_conflicts?: boolean;
250
+ }): Promise<MigrationImport>;
251
+ delete(id: string): Promise<{
252
+ object: "migration_import";
253
+ id: string;
254
+ deleted: true;
255
+ }>;
256
+ /**
257
+ * Create an import (or continue `importId`), upload the sealed bundle text
258
+ * in parts from `resumeFrom` (1-based) and finalize it.
259
+ */
260
+ upload(bundleText: string, opts?: {
261
+ options?: MigrationImportOptions;
262
+ partBytes?: number;
263
+ importId?: string;
264
+ resumeFrom?: number;
265
+ onPart?: (n: number, of: number) => void;
266
+ }): Promise<MigrationImport>;
267
+ /** Poll until the import leaves analyzing / queued / applying. */
268
+ waitFor(id: string, opts?: {
269
+ intervalMs?: number;
270
+ timeoutMs?: number;
271
+ onProgress?: (imp: MigrationImport) => void;
272
+ sleep?: (ms: number) => Promise<void>;
273
+ }): Promise<MigrationImport>;
274
+ };
275
+ /** The zero-logout bridge settings of one source. */
276
+ bridge: (source: MigrationSourceId | string) => {
277
+ get(): Promise<MigrationBridgeSettings>;
278
+ update(body: MigrationBridgeSettingsUpdate): Promise<MigrationBridgeSettings>;
279
+ delete(): Promise<{
280
+ object: "migration_bridge";
281
+ source: string;
282
+ deleted: true;
283
+ }>;
284
+ };
285
+ };
286
+ export {};
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.migrationsResource = migrationsResource;
4
+ const better_auth_imports_1 = require("./better-auth-imports");
5
+ const request_1 = require("./request");
6
+ const TERMINAL = ['ready', 'invalid', 'completed', 'failed'];
7
+ function migrationsResource(request) {
8
+ const importsFor = (source) => {
9
+ const base = `/v1/migrations/${(0, request_1.enc)(source)}/imports`;
10
+ const self = {
11
+ /** The import public key (created on first read; shared by every source). */
12
+ getPublicKey() {
13
+ return request({ method: 'GET', path: `${base}/public_key` });
14
+ },
15
+ /** Replace the import key; bundles sealed to the old one no longer finalize. */
16
+ rotatePublicKey() {
17
+ return request({ method: 'POST', path: `${base}/public_key/rotate` });
18
+ },
19
+ create(body = {}, idempotencyKey) {
20
+ return request({ method: 'POST', path: base, body, idempotencyKey });
21
+ },
22
+ list(params = {}) {
23
+ return request({ method: 'GET', path: base, query: { ...params } });
24
+ },
25
+ get(id) {
26
+ return request({ method: 'GET', path: `${base}/${(0, request_1.enc)(id)}` });
27
+ },
28
+ /** Upload part `part` (1-based) of the sealed bundle text. */
29
+ uploadPart(id, part, data) {
30
+ return request({ method: 'PUT', path: `${base}/${(0, request_1.enc)(id)}/parts/${part}`, body: { data } });
31
+ },
32
+ /** Close the upload; Atlas checks the key and the format, then runs the dry run. */
33
+ finalize(id) {
34
+ return request({ method: 'POST', path: `${base}/${(0, request_1.enc)(id)}/finalize` });
35
+ },
36
+ dryRun(id, body = {}) {
37
+ return request({ method: 'POST', path: `${base}/${(0, request_1.enc)(id)}/dry_run`, body });
38
+ },
39
+ /** Import (or resume). Pass confirm_conflicts when the dry run reported conflicts. */
40
+ apply(id, body = {}) {
41
+ return request({ method: 'POST', path: `${base}/${(0, request_1.enc)(id)}/apply`, body });
42
+ },
43
+ delete(id) {
44
+ return request({ method: 'DELETE', path: `${base}/${(0, request_1.enc)(id)}` });
45
+ },
46
+ /**
47
+ * Create an import (or continue `importId`), upload the sealed bundle text
48
+ * in parts from `resumeFrom` (1-based) and finalize it.
49
+ */
50
+ async upload(bundleText, opts = {}) {
51
+ const parts = (0, better_auth_imports_1.splitBundleParts)(bundleText, opts.partBytes);
52
+ if (parts.length === 0)
53
+ throw new Error('The bundle is empty.');
54
+ const id = opts.importId ?? (await self.create(opts.options ? { options: opts.options } : {})).id;
55
+ for (let i = Math.max(0, (opts.resumeFrom ?? 1) - 1); i < parts.length; i++) {
56
+ await self.uploadPart(id, i + 1, parts[i]);
57
+ opts.onPart?.(i + 1, parts.length);
58
+ }
59
+ return self.finalize(id);
60
+ },
61
+ /** Poll until the import leaves analyzing / queued / applying. */
62
+ async waitFor(id, opts = {}) {
63
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
64
+ const deadline = Date.now() + (opts.timeoutMs ?? 6 * 3600_000);
65
+ for (;;) {
66
+ const imp = await self.get(id);
67
+ opts.onProgress?.(imp);
68
+ if (TERMINAL.includes(imp.status) || imp.status === 'uploading')
69
+ return imp;
70
+ if (Date.now() > deadline)
71
+ throw new Error(`Timed out waiting for migration import ${id} (status ${imp.status}).`);
72
+ await sleep(opts.intervalMs ?? 2000);
73
+ }
74
+ },
75
+ };
76
+ return self;
77
+ };
78
+ const bridgeFor = (source) => {
79
+ const path = `/v1/migrations/${(0, request_1.enc)(source)}/bridge`;
80
+ return {
81
+ get() {
82
+ return request({ method: 'GET', path });
83
+ },
84
+ update(body) {
85
+ return request({ method: 'PUT', path, body });
86
+ },
87
+ delete() {
88
+ return request({ method: 'DELETE', path });
89
+ },
90
+ };
91
+ };
92
+ return {
93
+ /** Every migration source and what it supports. */
94
+ sources() {
95
+ return request({ method: 'GET', path: '/v1/migrations/sources' });
96
+ },
97
+ /** The import surface of one source: `migrations.imports('auth0').upload(text)`. */
98
+ imports: importsFor,
99
+ /** The zero-logout bridge settings of one source. */
100
+ bridge: bridgeFor,
101
+ };
102
+ }
103
+ //# sourceMappingURL=migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/client/migrations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import type { MigrationTokenExtractor } from './migration-bridge';
2
+ /**
3
+ * Zero-logout bridge for the SELF-HOSTED frameworks (Auth.js, Lucia,
4
+ * Passport, iron-session, Devise, Django): their presets, and the token
5
+ * extractors `migrationBridge({ extractToken })` takes.
6
+ *
7
+ * Two families:
8
+ *
9
+ * - Database sessions (Auth.js database strategy, Lucia, express-session
10
+ * stores, Django's database backend, Rails' activerecord-session_store):
11
+ * preset mode `session_table`. The cookie carries an opaque id; Atlas finds
12
+ * sha256(id) among the sessions you imported. Lucia, Auth.js and Django
13
+ * cookies are read as they are; express-session's cookie is signed
14
+ * (`s:<id>.<signature>`), so {@link expressSessionExtractor} pulls the id out.
15
+ *
16
+ * - Sealed cookies (Auth.js JWT strategy, iron-session, Rails' cookie store
17
+ * under Devise, Django's signed-cookie backend): there is no session row to
18
+ * import — the cookie IS the session, and only your app's secret opens it.
19
+ * The extractor opens it ON YOUR SERVER and hands Atlas a short-lived
20
+ * BRIDGE ASSERTION instead: an HS256 JWT (`iss` = `urn:atlas:bridge:<source>`,
21
+ * `sub` = the user id you exported, `jti` = the old session's id) signed
22
+ * with a bridge secret you generate and store in the bridge settings
23
+ * (`shared_secret`). Your AUTH_SECRET / secret_key_base / SECRET_KEY /
24
+ * iron password never leave your server; Atlas verifies the assertion,
25
+ * spends its `jti` once, and signs the user in.
26
+ */
27
+ /** The issuer of a bridge assertion for a source. */
28
+ export declare function bridgeAssertionIssuer(source: string): string;
29
+ /** Lifetime of a bridge assertion: it is exchanged immediately, once. */
30
+ export declare const BRIDGE_ASSERTION_TTL_SECONDS = 120;
31
+ export interface BridgeAssertion {
32
+ source: string;
33
+ /** The source user id (the `user.id` your export wrote). */
34
+ sub: string;
35
+ /** The old session's own id, so the session exchanges once (single-use ledger). */
36
+ sessionKey: string;
37
+ email?: string | null;
38
+ name?: string | null;
39
+ /** The old session had already passed a second factor. */
40
+ mfa?: boolean;
41
+ }
42
+ /** Sign a bridge assertion with the bridge secret (HS256). */
43
+ export declare function signBridgeAssertion(assertion: BridgeAssertion, bridgeSecret: string, now?: Date): Promise<string>;
44
+ /**
45
+ * Auth.js / NextAuth, either session strategy. A database-session cookie is
46
+ * returned as it is (preset `authjs`); a JWT-strategy cookie is decrypted with
47
+ * `secret` (AUTH_SECRET / NEXTAUTH_SECRET, or the rotation list) and turned
48
+ * into a bridge assertion (preset `authjs_jwt`, needs `bridgeSecret`).
49
+ */
50
+ export declare function authJsExtractor(options: {
51
+ secret?: string | readonly string[];
52
+ bridgeSecret?: string;
53
+ cookieName?: string;
54
+ }): MigrationTokenExtractor;
55
+ /**
56
+ * Passport / express-session: the session id out of the signed `connect.sid`
57
+ * cookie (preset `passport`). Pass `secrets` (your `session({ secret })`) to
58
+ * check the signature as express-session does.
59
+ */
60
+ export declare function expressSessionExtractor(options?: {
61
+ cookieName?: string;
62
+ secrets?: string | readonly string[];
63
+ }): MigrationTokenExtractor;
64
+ /**
65
+ * iron-session: unseal the cookie with your session password and assert the
66
+ * user at `userIdPath` (default: `userId`, then `user.id`, then `id`).
67
+ */
68
+ export declare function ironSessionExtractor(options: {
69
+ cookieName: string;
70
+ password: string | Readonly<Record<string, string>>;
71
+ bridgeSecret: string;
72
+ userIdPath?: string;
73
+ emailPath?: string;
74
+ }): MigrationTokenExtractor;
75
+ /**
76
+ * Devise on Rails' cookie store: decrypt `_<app>_session` with
77
+ * `secret_key_base` and assert the Warden user (`scope`, default `user`).
78
+ */
79
+ export declare function deviseExtractor(options: {
80
+ cookieName: string;
81
+ secretKeyBase: string | readonly string[];
82
+ bridgeSecret: string;
83
+ scope?: string;
84
+ keyDigest?: 'SHA-256' | 'SHA-1';
85
+ }): MigrationTokenExtractor;
86
+ /**
87
+ * Django's signed-cookie session backend: verify the cookie with SECRET_KEY
88
+ * (then SECRET_KEY_FALLBACKS) and assert `_auth_user_id`.
89
+ */
90
+ export declare function djangoSignedCookieExtractor(options: {
91
+ secretKey: string | readonly string[];
92
+ bridgeSecret: string;
93
+ cookieName?: string;
94
+ /** SESSION_COOKIE_AGE (default two weeks). */
95
+ maxAgeSeconds?: number;
96
+ }): MigrationTokenExtractor;
97
+ /** The preset ids this module registers (for docs and the settings UI). */
98
+ export declare const FRAMEWORK_BRIDGE_PRESETS: string[];