@softwarepatterns/am 0.0.2 → 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/README.md +87 -79
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +93 -281
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/types/auth.d.ts +88 -279
- package/dist/types/types.d.ts +6 -3
- package/package.json +2 -3
package/dist/index.d.ts
CHANGED
|
@@ -80,6 +80,7 @@ type AccountStatus = "active" | "trial" | "past_due" | "suspended" | "closed";
|
|
|
80
80
|
*/
|
|
81
81
|
type AccountResource = {
|
|
82
82
|
id: AccountId;
|
|
83
|
+
parentId: AccountId | null;
|
|
83
84
|
/** Display name chosen by the account owner */
|
|
84
85
|
name: string | null;
|
|
85
86
|
/** URL to the account's avatar image */
|
|
@@ -98,7 +99,7 @@ type AccountResource = {
|
|
|
98
99
|
* - suspended: Access restricted due to billing or policy
|
|
99
100
|
* - closed: Permanently closed
|
|
100
101
|
*/
|
|
101
|
-
type UserStatus = "active" | "
|
|
102
|
+
type UserStatus = "active" | "disabled" | "suspended" | "deleted";
|
|
102
103
|
/**
|
|
103
104
|
* A reference to a user. Will not be deleted even due to GDPR requests or account closures,
|
|
104
105
|
* to maintain referential integrity in audit logs and historical records.
|
|
@@ -174,7 +175,9 @@ type SessionTokens = {
|
|
|
174
175
|
type SessionProfile = UserResource & {
|
|
175
176
|
identity: UserIdentity | null;
|
|
176
177
|
emailCredentials: EmailCredential[];
|
|
177
|
-
memberships: Membership
|
|
178
|
+
memberships: (Membership & {
|
|
179
|
+
account: AccountResource;
|
|
180
|
+
})[];
|
|
178
181
|
/** Currently active membership in the accessToken (determined by preferredMembershipId or context) */
|
|
179
182
|
activeMembership: Membership | null;
|
|
180
183
|
lastUpdatedAt: number;
|
|
@@ -184,7 +187,7 @@ type SessionProfile = UserResource & {
|
|
|
184
187
|
*/
|
|
185
188
|
type Authentication = {
|
|
186
189
|
tokens: SessionTokens;
|
|
187
|
-
profile: SessionProfile
|
|
190
|
+
profile: SessionProfile;
|
|
188
191
|
};
|
|
189
192
|
/**
|
|
190
193
|
* Result of checkEmail() indicating whether the email is registered.
|
|
@@ -213,34 +216,21 @@ type Config = {
|
|
|
213
216
|
baseUrl: string;
|
|
214
217
|
earlyRefreshMs: number;
|
|
215
218
|
fetchFn: FetchFn;
|
|
216
|
-
onRefresh?: (tokens: SessionTokens) => void | Promise<void>;
|
|
217
|
-
onProfileRefetch?: (profile: SessionProfile) => void | Promise<void>;
|
|
218
|
-
onUnauthenticated?: (e: AuthError) => void | Promise<void>;
|
|
219
219
|
profileStorageKey: string;
|
|
220
220
|
storage: StorageConfig;
|
|
221
221
|
tokensStorageKey: string;
|
|
222
222
|
};
|
|
223
|
+
type AuthEventMap = {
|
|
224
|
+
refresh: SessionTokens;
|
|
225
|
+
profileChange: SessionProfile;
|
|
226
|
+
unauthenticated: AuthError;
|
|
227
|
+
sessionChange: AuthSession | null;
|
|
228
|
+
};
|
|
223
229
|
/**
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* Always thrown on non-2xx responses from auth endpoints. Contains structured ProblemDetails
|
|
227
|
-
* from the server when available.
|
|
228
|
-
*
|
|
229
|
-
* Most 400 errors will also contain `invalidParams` for parameters that caused
|
|
230
|
-
* the error, which can be used to display field-level validation messages.
|
|
230
|
+
* AuthError represents structured authentication failures from Accountmaker endpoints.
|
|
231
231
|
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
* Note that HTTP error codes are distinctly:
|
|
236
|
-
* - 400: Client error (bad request, invalid input, etc.)
|
|
237
|
-
* - 401: Unauthenticated (we don't know who you are)
|
|
238
|
-
* - 402: Payment required (e.g. billing issue)
|
|
239
|
-
* - 403: Unauthorized (we know who you are, but you don't have permission)
|
|
240
|
-
* - 404: Not found
|
|
241
|
-
* - 409: Conflict (email already registered, user already invited, etc.)
|
|
242
|
-
* - 429: Too many requests (rate limiting)
|
|
243
|
-
* - 500: Internal server error (server's fault)
|
|
232
|
+
* AuthError wraps RFC 7807 Problem Details. invalidParams may be present for field-level validation.
|
|
233
|
+
* Network failures throw other error types.
|
|
244
234
|
*
|
|
245
235
|
* Also note that the `type` field often contains a URI that points to documentation about the
|
|
246
236
|
* specific error type, including how to resolve it, code samples, and links to the RFCs or other
|
|
@@ -263,6 +253,16 @@ type Config = {
|
|
|
263
253
|
* }
|
|
264
254
|
* }
|
|
265
255
|
* ```
|
|
256
|
+
*
|
|
257
|
+
* Note that HTTP error codes are distinctly:
|
|
258
|
+
* - 400: Client error (bad request, invalid input, etc.)
|
|
259
|
+
* - 401: Unauthenticated (we don't know who you are)
|
|
260
|
+
* - 402: Payment required (e.g. billing issue)
|
|
261
|
+
* - 403: Unauthorized (we know who you are, but you don't have permission)
|
|
262
|
+
* - 404: Not found
|
|
263
|
+
* - 409: Conflict (email already registered, user already invited, etc.)
|
|
264
|
+
* - 429: Too many requests (rate limiting)
|
|
265
|
+
* - 500: Internal server error (server's fault)
|
|
266
266
|
*/
|
|
267
267
|
declare class AuthError extends Error {
|
|
268
268
|
readonly problem: ProblemDetails;
|
|
@@ -275,45 +275,20 @@ declare class AuthError extends Error {
|
|
|
275
275
|
get invalidParams(): ProblemDetails["invalidParams"] | undefined;
|
|
276
276
|
}
|
|
277
277
|
/**
|
|
278
|
-
*
|
|
278
|
+
* AuthSession represents an authenticated user with automatic token refresh and persisted state.
|
|
279
279
|
*
|
|
280
|
-
*
|
|
281
|
-
* requests.
|
|
282
|
-
*
|
|
283
|
-
* Features:
|
|
284
|
-
* - session.fetch() will always use a valid access token (refreshing automatically)
|
|
285
|
-
* - All non-2xx responses throw AuthError
|
|
286
|
-
* - Tokens and profile are automatically persisted to storage (if configured)
|
|
280
|
+
* AuthSession owns tokens, profile data, refresh logic, and authenticated requests.
|
|
287
281
|
*/
|
|
288
282
|
declare class AuthSession {
|
|
289
283
|
constructor(initial: Authentication, config: Partial<Config>);
|
|
290
|
-
/**
|
|
291
|
-
* Restores an AuthSession from persisted storage. Returns null if no valid session
|
|
292
|
-
* is found.
|
|
293
|
-
*
|
|
294
|
-
* @example
|
|
295
|
-
* ```ts
|
|
296
|
-
* const session = AuthSession.restoreSession();
|
|
297
|
-
* if (session) {
|
|
298
|
-
* console.log("Restored session for user:", session.profile);
|
|
299
|
-
* } else {
|
|
300
|
-
* console.log("No valid session found.");
|
|
301
|
-
* }
|
|
302
|
-
* ```
|
|
303
|
-
*/
|
|
304
|
-
static restoreSession(config?: Partial<Config>): AuthSession | null;
|
|
305
284
|
/**
|
|
306
285
|
* Removes all persisted data (tokens, profile) from storage, and prevents future
|
|
307
|
-
*
|
|
286
|
+
* refreshes of token and profile data. Does NOT clear current token or profile data from the
|
|
308
287
|
* session memory, but effectively deactivates the session for future use.
|
|
309
288
|
*/
|
|
310
289
|
clear(): void;
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
idToken(): string | undefined;
|
|
314
|
-
expiresIn(): number;
|
|
315
|
-
expiresAt(): Date;
|
|
316
|
-
profile(): SessionProfile | null;
|
|
290
|
+
get tokens(): SessionTokens;
|
|
291
|
+
get profile(): SessionProfile;
|
|
317
292
|
toJSON(): Authentication;
|
|
318
293
|
/**
|
|
319
294
|
* Creates an AuthSession from existing authentication data. Useful for restoring
|
|
@@ -326,38 +301,31 @@ declare class AuthSession {
|
|
|
326
301
|
*/
|
|
327
302
|
isExpired(): boolean;
|
|
328
303
|
/**
|
|
329
|
-
*
|
|
330
|
-
* session's access token. Any service can validate the token against Am's public
|
|
331
|
-
* keys at https://api.accountmaker.com/.well-known/jwks.json?client_id={clientId}
|
|
332
|
-
*
|
|
333
|
-
* Automatically:
|
|
334
|
-
* - Adds Authorization header
|
|
335
|
-
* - Refreshes token if expired
|
|
336
|
-
* - Retries once on 401 if refresh succeeds
|
|
337
|
-
*
|
|
338
|
-
* Assumes the response is JSON and parses it. Throws AuthError on non-2xx responses.
|
|
304
|
+
* Performs standard fetch() with a Bearer token and automatic refresh.
|
|
339
305
|
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
306
|
+
* Returns Response, does not parse the body, does not throw for HTTP status codes.
|
|
307
|
+
* Throws AuthError when refresh fails, trows runtime errors on network failure.
|
|
342
308
|
*
|
|
343
309
|
* @example
|
|
344
310
|
* ```ts
|
|
345
|
-
* const res = await session.fetch(
|
|
311
|
+
* const res = await session.fetch("/api/projects");
|
|
346
312
|
* const projects = await res.json();
|
|
347
313
|
* ```
|
|
348
314
|
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
315
|
+
* Any service can validate tokens using:
|
|
316
|
+
* https://api.accountmaker.com/.well-known/jwks.json?client_id={clientId}
|
|
351
317
|
*/
|
|
352
|
-
fetch(url: string | URL, init?: RequestInit): Promise<
|
|
318
|
+
fetch(url: string | URL, init?: RequestInit): Promise<Response>;
|
|
353
319
|
/**
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
320
|
+
* Replaces the access token using the refresh token.
|
|
321
|
+
*
|
|
322
|
+
* It is called automatically by all other methods when the access token is expired or near expiry.
|
|
357
323
|
*/
|
|
358
324
|
refresh(): Promise<void>;
|
|
359
325
|
/**
|
|
360
|
-
*
|
|
326
|
+
* refetchProfile() replaces the cached profile with server state.
|
|
327
|
+
*
|
|
328
|
+
* Concurrent calls are deduplicated.
|
|
361
329
|
*
|
|
362
330
|
* @example
|
|
363
331
|
* ```ts
|
|
@@ -370,29 +338,19 @@ declare class AuthSession {
|
|
|
370
338
|
*/
|
|
371
339
|
refetchProfile(): Promise<void>;
|
|
372
340
|
/**
|
|
373
|
-
*
|
|
374
|
-
* called by the currently authenticated user or an account admin.
|
|
341
|
+
* Requests a verification email for the current user.
|
|
375
342
|
*
|
|
376
343
|
* Throws AuthError on network errors, etc.
|
|
377
344
|
* @throws AuthError
|
|
378
345
|
*/
|
|
379
346
|
sendVerificationEmail(): Promise<void>;
|
|
380
|
-
/**
|
|
381
|
-
* Fetches a user by ID.
|
|
382
|
-
*
|
|
383
|
-
* Throws AuthError on invalid user ID, network errors, etc.
|
|
384
|
-
* @throws AuthError
|
|
385
|
-
*/
|
|
386
|
-
user(id: UserId): Promise<UserResource>;
|
|
387
347
|
}
|
|
388
348
|
/**
|
|
389
|
-
*
|
|
390
|
-
* invite acceptance, and other unauthenticated actions.
|
|
349
|
+
* Am runs authentication flows and produces AuthSession.
|
|
391
350
|
*
|
|
392
|
-
*
|
|
393
|
-
* for all subsequent authenticated requests.
|
|
351
|
+
* Use Am before a session exists (sign-in, sign-up, magic link, invites, password reset, CSRF).
|
|
394
352
|
*
|
|
395
|
-
*
|
|
353
|
+
* @example
|
|
396
354
|
* ```ts
|
|
397
355
|
* const am = new Am();
|
|
398
356
|
* const session = await am.signIn({ email: 'user@example.com', password: 'secret' });
|
|
@@ -400,68 +358,55 @@ declare class AuthSession {
|
|
|
400
358
|
* ```
|
|
401
359
|
*/
|
|
402
360
|
declare class Am {
|
|
403
|
-
private options;
|
|
404
361
|
constructor(config?: Partial<Config>);
|
|
405
362
|
/**
|
|
406
|
-
*
|
|
407
|
-
*
|
|
363
|
+
* session returns the current AuthSession or null.
|
|
364
|
+
*
|
|
365
|
+
* Use restoreSession() to load from storage.
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```ts
|
|
369
|
+
* const session = am.session;
|
|
370
|
+
* if (!session) throw new Error("Not authenticated");
|
|
371
|
+
* ```
|
|
408
372
|
*/
|
|
409
|
-
|
|
373
|
+
get session(): AuthSession | null;
|
|
410
374
|
/**
|
|
411
|
-
*
|
|
412
|
-
*
|
|
375
|
+
* Constructs AuthSession from existing tokens and profile.
|
|
376
|
+
*
|
|
377
|
+
* Use this after a server-side auth handshake or custom persistence.
|
|
413
378
|
*/
|
|
414
|
-
|
|
379
|
+
createSession(initial: Authentication): AuthSession;
|
|
415
380
|
/**
|
|
416
|
-
*
|
|
417
|
-
* containing fresh tokens and profile. Tokens are automatically persisted (if
|
|
418
|
-
* storage is enabled).
|
|
381
|
+
* restoreSession loads AuthSession from storage or returns null.
|
|
419
382
|
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
383
|
+
* Invalid or partial stored data is cleared.
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```ts
|
|
387
|
+
* const am = new Am({ storage: 'localStorage' });
|
|
388
|
+
* const session = am.restoreSession();
|
|
389
|
+
* if (session) session.fetch("/api/me");
|
|
390
|
+
* ```
|
|
422
391
|
*/
|
|
423
|
-
|
|
424
|
-
clientId: ClientId;
|
|
425
|
-
token: string;
|
|
426
|
-
}, config?: Partial<Config>): Promise<AuthSession>;
|
|
392
|
+
restoreSession(): AuthSession | null;
|
|
427
393
|
/**
|
|
428
|
-
*
|
|
429
|
-
|
|
430
|
-
|
|
394
|
+
* Subscribes to auth events and returns an unsubscribe function.
|
|
395
|
+
*/
|
|
396
|
+
on<K extends keyof AuthEventMap>(event: K, fn: (v: AuthEventMap[K]) => void): () => boolean;
|
|
397
|
+
/**
|
|
398
|
+
* acceptInvite exchanges an invite token for a fresh AuthSession.
|
|
431
399
|
*
|
|
432
|
-
* Throws AuthError on invalid or
|
|
433
|
-
* @throws AuthError
|
|
400
|
+
* Throws AuthError on invalid, expired, or already-used tokens.
|
|
434
401
|
*/
|
|
435
402
|
acceptInvite(query: {
|
|
436
403
|
clientId: ClientId;
|
|
437
404
|
token: string;
|
|
438
405
|
}): Promise<AuthSession>;
|
|
439
406
|
/**
|
|
440
|
-
*
|
|
441
|
-
* email is associated with an active account, and what login methods are preferred and
|
|
442
|
-
* available for that user. This can be used to enforce login expierences for enterprise SSO or
|
|
443
|
-
* to prefer passwordless login methods.
|
|
444
|
-
*
|
|
445
|
-
* Throws AuthError on invalid client ID, network errors, etc.
|
|
446
|
-
* @throws AuthError
|
|
447
|
-
*/
|
|
448
|
-
static checkEmail(body: {
|
|
449
|
-
clientId: ClientId;
|
|
450
|
-
email: string;
|
|
451
|
-
csrfToken?: string;
|
|
452
|
-
}, config?: Partial<Config>): Promise<{
|
|
453
|
-
status: EmailCheckStatus;
|
|
454
|
-
preferred: LoginMethod[];
|
|
455
|
-
available: LoginMethod[];
|
|
456
|
-
}>;
|
|
457
|
-
/**
|
|
458
|
-
* Checks the status of an email address for authentication purposes. Indicates whether the
|
|
459
|
-
* email is associated with an active account, and what login methods are preferred and
|
|
460
|
-
* available for that user. This can be used to enforce login expierences for enterprise SSO or
|
|
461
|
-
* to prefer passwordless login methods.
|
|
407
|
+
* checkEmail returns how an email should authenticate for this client.
|
|
462
408
|
*
|
|
463
|
-
*
|
|
464
|
-
* @throws AuthError
|
|
409
|
+
* Use this to choose password vs magic link vs SSO before rendering a login form.
|
|
465
410
|
*/
|
|
466
411
|
checkEmail(body: {
|
|
467
412
|
clientId: ClientId;
|
|
@@ -473,66 +418,25 @@ declare class Am {
|
|
|
473
418
|
available: LoginMethod[];
|
|
474
419
|
}>;
|
|
475
420
|
/**
|
|
476
|
-
*
|
|
477
|
-
* to get the signed token to include in the form. When the form is submitted, the server
|
|
478
|
-
* will verify the signed token against the session cookie. This prevents a certain class
|
|
479
|
-
* of CSRF attacks that rely on being able to read values from the target site.
|
|
480
|
-
*/
|
|
481
|
-
static csrfSession(config?: Partial<Config>): Promise<{
|
|
482
|
-
csrfToken: string;
|
|
483
|
-
}>;
|
|
484
|
-
/**
|
|
485
|
-
* When called, sets a httpOnly CSRF session cookie. Then when rendering a form, call csrfToken()
|
|
486
|
-
* to get the signed token to include in the form. When the form is submitted, the server
|
|
487
|
-
* will verify the signed token against the session cookie. This prevents a certain class
|
|
488
|
-
* of CSRF attacks that rely on being able to read values from the target site.
|
|
489
|
-
*/
|
|
490
|
-
csrfSession(): Promise<{
|
|
491
|
-
csrfToken: string;
|
|
492
|
-
}>;
|
|
493
|
-
/**
|
|
494
|
-
* Fetches a signed CSRF token for use in forms. Call csrfSession() first to set a httpOnly CSRF
|
|
495
|
-
* session cookie, then call this method to get the signed token, then include the token in your
|
|
496
|
-
* form submissions. When the form is submitted, the server will verify the signed token against
|
|
497
|
-
* the httpOnly session cookie. This prevents a certain class of CSRF attacks that rely on being
|
|
498
|
-
* able to read values from the target site.
|
|
421
|
+
* Sets the httpOnly CSRF cookie.
|
|
499
422
|
*
|
|
500
|
-
*
|
|
423
|
+
* Call csrfToken() next to fetch a signed token for form posts.
|
|
501
424
|
*/
|
|
502
|
-
|
|
425
|
+
csrfSession(): Promise<{
|
|
503
426
|
csrfToken: string;
|
|
504
427
|
}>;
|
|
505
428
|
/**
|
|
506
|
-
*
|
|
507
|
-
* session cookie, then call this method to get the signed token, then include the token in your
|
|
508
|
-
* form submissions. When the form is submitted, the server will verify the signed token against
|
|
509
|
-
* the httpOnly session cookie. This prevents a certain class of CSRF attacks that rely on being
|
|
510
|
-
* able to read values from the target site.
|
|
429
|
+
* Returns a signed CSRF token for form posts.
|
|
511
430
|
*
|
|
512
|
-
*
|
|
431
|
+
* Call csrfSession() first to set the CSRF cookie.
|
|
513
432
|
*/
|
|
514
433
|
csrfToken(): Promise<{
|
|
515
434
|
csrfToken: string;
|
|
516
435
|
}>;
|
|
517
436
|
/**
|
|
518
|
-
*
|
|
519
|
-
* Tokens are automatically persisted (if storage is enabled).
|
|
520
|
-
*
|
|
521
|
-
* Throws AuthError on invalid credentials, unverified email, etc.
|
|
522
|
-
* @throws AuthError
|
|
523
|
-
*/
|
|
524
|
-
static signIn(body: {
|
|
525
|
-
clientId: ClientId;
|
|
526
|
-
email: string;
|
|
527
|
-
password: string;
|
|
528
|
-
csrfToken?: string;
|
|
529
|
-
}, config?: Partial<Config>): Promise<AuthSession>;
|
|
530
|
-
/**
|
|
531
|
-
* On success, returns an AuthSession containing fresh tokens and profile.
|
|
532
|
-
* Tokens are automatically persisted (if storage is enabled).
|
|
437
|
+
* Authenticates with email and password and returns a new AuthSession.
|
|
533
438
|
*
|
|
534
|
-
*
|
|
535
|
-
* @throws AuthError
|
|
439
|
+
* Tokens and profile are persisted when storage is configured.
|
|
536
440
|
*/
|
|
537
441
|
signIn(body: {
|
|
538
442
|
clientId: ClientId;
|
|
@@ -541,62 +445,15 @@ declare class Am {
|
|
|
541
445
|
csrfToken?: string;
|
|
542
446
|
}): Promise<AuthSession>;
|
|
543
447
|
/**
|
|
544
|
-
* Authenticates
|
|
545
|
-
*
|
|
546
|
-
* On success, returns an AuthSession containing fresh tokens and profile.
|
|
547
|
-
* Tokens are automatically persisted (if storage is enabled).
|
|
548
|
-
*
|
|
549
|
-
* Throws AuthError on invalid or expired token, etc.
|
|
550
|
-
* @throws AuthError
|
|
551
|
-
*/
|
|
552
|
-
static signInWithToken(token: string, config?: Partial<Config>): Promise<AuthSession>;
|
|
553
|
-
/**
|
|
554
|
-
* Authenticates using a one-time token (e.g., magic link or invite token).
|
|
448
|
+
* Authenticates with a one-time token and returns a new AuthSession.
|
|
555
449
|
*
|
|
556
|
-
*
|
|
557
|
-
* Tokens are automatically persisted (if storage is enabled).
|
|
558
|
-
*
|
|
559
|
-
* Throws AuthError on invalid or expired token, etc.
|
|
560
|
-
* @throws AuthError
|
|
450
|
+
* Use this for magic links and similar one-time login flows.
|
|
561
451
|
*/
|
|
562
452
|
signInWithToken(token: string): Promise<AuthSession>;
|
|
563
453
|
/**
|
|
564
|
-
*
|
|
565
|
-
* persist tokens. Use AuthSession.refresh() to refresh and persist tokens in an
|
|
566
|
-
* existing session.
|
|
567
|
-
*
|
|
568
|
-
* Throws AuthError on invalid or expired refresh token, etc.
|
|
569
|
-
* @throws AuthError
|
|
570
|
-
*/
|
|
571
|
-
static refresh(refreshToken: string, config?: Partial<Config>): Promise<SessionTokens>;
|
|
572
|
-
/**
|
|
573
|
-
* Manually refreshes session tokens using the provided refresh token. Does NOT
|
|
574
|
-
* persist tokens. Use AuthSession.refresh() to refresh and persist tokens in an
|
|
575
|
-
* existing session.
|
|
576
|
-
*
|
|
577
|
-
* Throws AuthError on invalid or expired refresh token, etc.
|
|
578
|
-
* @throws AuthError
|
|
579
|
-
*/
|
|
580
|
-
refresh(refreshToken: string): Promise<SessionTokens>;
|
|
581
|
-
/**
|
|
582
|
-
* Successful registration immediately authenticates the user and returns an
|
|
583
|
-
* AuthSession. Tokens are automatically persisted (if storage is enabled).
|
|
584
|
-
*
|
|
585
|
-
* Throws AuthError on invalid data, existing email, etc.
|
|
586
|
-
* @throws AuthError
|
|
587
|
-
*/
|
|
588
|
-
static signUp(body: {
|
|
589
|
-
clientId: ClientId;
|
|
590
|
-
email: string;
|
|
591
|
-
password: string;
|
|
592
|
-
csrfToken?: string;
|
|
593
|
-
}, config?: Partial<Config>): Promise<AuthSession>;
|
|
594
|
-
/**
|
|
595
|
-
* Successful registration immediately authenticates the user and returns an
|
|
596
|
-
* AuthSession. Tokens are automatically persisted (if storage is enabled).
|
|
454
|
+
* Creates a new user and returns a new AuthSession.
|
|
597
455
|
*
|
|
598
|
-
*
|
|
599
|
-
* @throws AuthError
|
|
456
|
+
* Tokens and profile are persisted when storage is configured.
|
|
600
457
|
*/
|
|
601
458
|
signUp(body: {
|
|
602
459
|
clientId: ClientId;
|
|
@@ -605,45 +462,14 @@ declare class Am {
|
|
|
605
462
|
csrfToken?: string;
|
|
606
463
|
}): Promise<AuthSession>;
|
|
607
464
|
/**
|
|
608
|
-
*
|
|
609
|
-
* the user's password is updated.
|
|
610
|
-
*
|
|
611
|
-
* Throws AuthError on invalid or expired token, weak password, etc.
|
|
612
|
-
* @throws AuthError
|
|
613
|
-
*/
|
|
614
|
-
static resetPassword(body: {
|
|
615
|
-
token: string;
|
|
616
|
-
newPassword: string;
|
|
617
|
-
}, config?: Partial<Config>): Promise<void>;
|
|
618
|
-
/**
|
|
619
|
-
* Completes a password reset using the token received via email. On success,
|
|
620
|
-
* the user's password is updated.
|
|
621
|
-
*
|
|
622
|
-
* Throws AuthError on invalid or expired token, weak password, etc.
|
|
623
|
-
* @throws AuthError
|
|
465
|
+
* Sets a new password using a one-time reset token.
|
|
624
466
|
*/
|
|
625
467
|
resetPassword(body: {
|
|
626
468
|
token: string;
|
|
627
469
|
newPassword: string;
|
|
628
470
|
}): Promise<void>;
|
|
629
471
|
/**
|
|
630
|
-
* Sends a
|
|
631
|
-
* passwordless authentication.
|
|
632
|
-
*
|
|
633
|
-
* Throws AuthError on invalid email format, etc.
|
|
634
|
-
* @throws AuthError
|
|
635
|
-
*/
|
|
636
|
-
static sendMagicLink(body: {
|
|
637
|
-
clientId: ClientId;
|
|
638
|
-
email: string;
|
|
639
|
-
csrfToken?: string;
|
|
640
|
-
}, config?: Partial<Config>): Promise<void>;
|
|
641
|
-
/**
|
|
642
|
-
* Sends a magic link email to the specified address. A magic link allows
|
|
643
|
-
* passwordless authentication.
|
|
644
|
-
*
|
|
645
|
-
* Throws AuthError on invalid email format, etc.
|
|
646
|
-
* @throws AuthError
|
|
472
|
+
* Sends a one-time sign-in link to an email address.
|
|
647
473
|
*/
|
|
648
474
|
sendMagicLink(body: {
|
|
649
475
|
clientId: ClientId;
|
|
@@ -651,21 +477,7 @@ declare class Am {
|
|
|
651
477
|
csrfToken?: string;
|
|
652
478
|
}): Promise<void>;
|
|
653
479
|
/**
|
|
654
|
-
* Sends a password reset
|
|
655
|
-
*
|
|
656
|
-
* Throws AuthError on invalid email format, etc.
|
|
657
|
-
* @throws AuthError
|
|
658
|
-
*/
|
|
659
|
-
static sendPasswordReset(body: {
|
|
660
|
-
clientId: ClientId;
|
|
661
|
-
email: string;
|
|
662
|
-
csrfToken?: string;
|
|
663
|
-
}, config?: Partial<Config>): Promise<void>;
|
|
664
|
-
/**
|
|
665
|
-
* Sends a password reset email to the specified address.
|
|
666
|
-
*
|
|
667
|
-
* Throws AuthError on invalid email format, etc.
|
|
668
|
-
* @throws AuthError
|
|
480
|
+
* Sends a password reset link to an email address.
|
|
669
481
|
*/
|
|
670
482
|
sendPasswordReset(body: {
|
|
671
483
|
clientId: ClientId;
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const t=Symbol("
|
|
1
|
+
const t=Symbol("session_state"),e=Symbol("auth_session"),n=Symbol("auth_state"),r=Symbol("emitter");function s(t){return t?"localStorage"===t?function(){try{return"undefined"==typeof window?null:window.localStorage?window.localStorage:null}catch{return null}}():t:null}function o(t,e){return t?function(t){if(!t)return null;try{return JSON.parse(t)}catch{return null}}(t.getItem(e)):null}function i(t,e,n){if(t)try{t.setItem(e,JSON.stringify(n))}catch{}}function a(t,e){if(t)try{t.removeItem(e)}catch{}}function c(t,e,n){if(!t)return;const r=o(t,e),s=l(r)?r:null;null===r||s||a(t,e),s&&s.expiresAt>=n.expiresAt||i(t,e,n)}function u(t,e,n){if(!t)return;const r=o(t,e),s=f(r)?r:null;null===r||s||a(t,e),s&&s.lastUpdatedAt>=n.lastUpdatedAt||i(t,e,n)}function l(t){return!!t&&"string"==typeof t.accessToken&&"string"==typeof t.refreshToken&&"number"==typeof t.expiresAt&&"number"==typeof t.expiresIn&&"Bearer"===t.tokenType}function f(t){return!!t&&"string"==typeof t.id&&"string"==typeof t.accountId&&"string"==typeof t.status&&"number"==typeof t.lastUpdatedAt&&("object"==typeof t.identity||null===t.identity)}function h(e){return e[t]}function p(t){return t[n]}function y(t,n){t[e]=n;const s=h(n);!function(t,e){t[r]=(t,n)=>{const r=p(e).listeners[t];if(r)for(const e of r)try{e(n)}catch{console.warn("Unhandled error in AuthEvent listener for event",t)}}}(s,t),g(s,"sessionChange",n)}function g(t,e,n){(0,t[r])(e,n)}function d(t){return t.replace(/_([a-z])/g,(t,e)=>e.toUpperCase())}function m(t){if(null===t||"object"!=typeof t)return t;if(Array.isArray(t))return t.map(t=>m(t));const e=t,n={};for(const t of Object.keys(e))Object.prototype.hasOwnProperty.call(e,t)&&(n[d(t)]=m(e[t]));return n}function w(t){return t.replace(/[A-Z]/g,t=>`_${t.toLowerCase()}`)}function b(t){if(null===t||"object"!=typeof t)return t;if(Array.isArray(t))return t.map(t=>b(t));const e=t,n={};for(const t of Object.keys(e))Object.prototype.hasOwnProperty.call(e,t)&&(n[w(t)]=b(e[t]));return n}class k extends Error{constructor(t){super(t.title),this.name="AuthError",this.problem=Object.freeze(t)}get type(){return this.problem.type}get title(){return this.problem.title}get status(){return this.problem.status}get code(){return this.problem.code}get detail(){return this.problem.detail}get invalidParams(){return this.problem.invalidParams}}const S={fetchFn:function(){const t=globalThis.fetch;return"function"==typeof t?t.bind(globalThis):async()=>{throw new Error("Missing fetch implementation. Provide config.fetchFn or use a runtime with global fetch.")}}(),baseUrl:"https://api.accountmaker.com",earlyRefreshMs:6e4,storage:null,tokensStorageKey:"am_tokens",profileStorageKey:"am_profile"};const P=async(t,e,n={})=>t.config.fetchFn(e,function(t,e){const n=new Headers(t.headers);return n.set("Authorization",`Bearer ${e}`),{...t,headers:n}}(n,t.tokens.accessToken)),A=async(t,e,n={})=>{U(t)&&await x(t);let r=await P(t,e,n);if(401!==r.status)return r;if(t.cleared)return r;try{return await x(t),r=await P(t,e,n),r}catch(e){throw!t.cleared&&K(e)&&g(t,"unauthenticated",e),e}},j=async t=>{if(204===t.status)return;const e=m(await async function(t){const e=t.headers.get("Content-Type")||"";if(!e.includes("application/json")&&!e.includes("+json"))return null;try{return await t.json()}catch{return null}}(t));if(!t.ok)throw new k(function(t,e){return function(t){return(t.headers.get("Content-Type")||"").includes("application/problem+json")}(t)&&e&&"object"==typeof e&&"string"==typeof e.type&&"string"==typeof e.title&&"number"==typeof e.status?e:function(t,e){return{type:"about:blank",title:t.statusText||"Request failed",status:t.status,detail:"string"==typeof e?e:void 0}}(t,e)}(t,e));return e},T=async(t,e,n={})=>{const r=await A(t,e,n);return j(r)},O=async({fetchFn:t,baseUrl:e},n,r={})=>{const s=new URLSearchParams(b(r)).toString(),o=s?`${e}${n}?${s}`:`${e}${n}`,i=await t(o,{method:"GET",headers:{Accept:"application/json"}});return j(i)},$=async({fetchFn:t,baseUrl:e},n,r)=>{const s=await t(`${e}${n}`,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(b(r))});return j(s)},U=t=>{const e=Math.min(Math.max(t.config.earlyRefreshMs,0),3e5);return Date.now()>=t.tokens.expiresAt-e},x=async t=>{if(!t.cleared){t.refreshPromise||(t.refreshPromise=async function(t){const{fetchFn:e,baseUrl:n}=t.config,r=await e(`${n}/auth/refresh`,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(b({refreshToken:t.tokens.refreshToken}))}),o=v(await j(r));t.tokens=o;const i=s(t.config.storage);c(i,t.config.tokensStorageKey,o),g(t,"refresh",o)}(t));try{await t.refreshPromise}finally{t.refreshPromise=null}}},K=t=>t instanceof k&&401===t.status,v=t=>{const e="number"==typeof t.expiresIn?t.expiresIn:0;return{...t,expiresAt:Date.now()+1e3*e}},I=t=>({...t,lastUpdatedAt:Date.now()});async function C(t){const e=I(await(async(t,e,n={})=>{const r=new URLSearchParams(b(n)).toString(),s=t.config.baseUrl,o=r?`${s}${e}?${r}`:`${s}${e}`;return await T(t,o,{method:"GET",headers:{Accept:"application/json"}})})(t,"/auth/me",{}));t.profile=e;u(s(t.config.storage),t.config.profileStorageKey,e),g(t,"profileChange",e)}const E=t=>({tokens:v(t.tokens),profile:I(t.profile)});class J{constructor(e,n){const r={...S,...n},o={...e,config:r,refreshPromise:null,profilePromise:null,cleared:!1};!function(e,n){e[t]=n}(this,o);const i=s(r.storage);c(i,r.tokensStorageKey,o.tokens),o.profile&&u(i,r.profileStorageKey,o.profile)}clear(){const t=h(this);!function(t){const e=s(t.storage);a(e,t.profileStorageKey),a(e,t.tokensStorageKey)}(t.config),t.cleared=!0}get tokens(){return h(this).tokens}get profile(){return h(this).profile}toJSON(){const t=h(this);return{tokens:t.tokens,profile:t.profile}}static fromJSON(t,e){return new J(t,e)}isExpired(){return U(h(this))}async fetch(t,e={}){return A(h(this),t,e)}async refresh(){return x(h(this))}async refetchProfile(){const t=h(this);if(!t.cleared){t.profilePromise||(t.profilePromise=C(t));try{await t.profilePromise}finally{t.profilePromise=null}}}async sendVerificationEmail(){await(async(t,e,n)=>{const r=t.config.baseUrl;return await T(t,`${r}${e}`,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(b(n))})})(h(this),"/auth/send-verification-email",{})}}class N{constructor(t){var e,r;e=this,r={config:{...S,...t},listeners:{}},e[n]=r}get session(){return this[e]||null}createSession(t){const e=new J(t,p(this).config);return y(this,e),e}restoreSession(){const t=p(this).config,e=s(t.storage);if(!e)return null;const n=o(e,t.tokensStorageKey),r=l(n)?n:null,i=o(e,t.profileStorageKey),c=f(i)?i:null;if(!r||!c)return a(e,t.tokensStorageKey),a(e,t.profileStorageKey),null;const u=new J({tokens:r,profile:c},t);return y(this,u),u}on(t,e){const n=p(this).listeners,r=n[t]??(n[t]=new Set);return r.add(e),()=>r.delete(e)}async acceptInvite(t){const e=p(this).config,n=E(await O(e,"/auth/accept-invite",t)),r=new J(n,e);return y(this,r),r}async checkEmail(t){return $(p(this).config,"/auth/check-email",t)}async csrfSession(){return O(p(this).config,"/auth/csrf-session")}async csrfToken(){return O(p(this).config,"/auth/csrf-token")}async signIn(t){const e=p(this).config,n=E(await $(e,"/auth/sign-in",t)),r=new J(n,e);return y(this,r),r}async signInWithToken(t){const e=p(this).config,n=E(await O(e,"/auth/sign-in-with-token",{token:t})),r=new J(n,e);return y(this,r),r}async signUp(t){const e=p(this).config,n=E(await $(e,"/auth/sign-up",t)),r=new J(n,e);return y(this,r),r}async resetPassword(t){return $(p(this).config,"/auth/reset-password",t)}async sendMagicLink(t){return $(p(this).config,"/auth/send-magic-link",t)}async sendPasswordReset(t){return $(p(this).config,"/auth/send-password-reset",t)}}export{N as Am,k as AuthError,J as AuthSession};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|