@aooth/auth 0.1.1
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/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/atscript-db.cjs +113 -0
- package/dist/atscript-db.d.cts +85 -0
- package/dist/atscript-db.d.mts +85 -0
- package/dist/atscript-db.mjs +112 -0
- package/dist/index.cjs +741 -0
- package/dist/index.d.cts +386 -0
- package/dist/index.d.mts +386 -0
- package/dist/index.mjs +733 -0
- package/dist/redis.cjs +142 -0
- package/dist/redis.d.cts +91 -0
- package/dist/redis.d.mts +91 -0
- package/dist/redis.mjs +140 -0
- package/dist/store-B1t8KkfA.d.mts +124 -0
- package/dist/store-untAtWQz.d.cts +124 -0
- package/package.json +75 -0
- package/src/atscript-db/auth-credential.as +44 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { a as CredentialState, i as CredentialMetadata, n as DenylistStore, o as IssueResult, r as AuthContext, s as RefreshConfig, t as CredentialStore } from "./store-B1t8KkfA.mjs";
|
|
2
|
+
import { CryptoKey } from "jose";
|
|
3
|
+
|
|
4
|
+
//#region src/errors.d.ts
|
|
5
|
+
type AuthErrorType = "INVALID_TOKEN" | "TOKEN_EXPIRED" | "TOKEN_REVOKED" | "REFRESH_REUSE_DETECTED" | "STATELESS_OPERATION_UNSUPPORTED" | "MAX_CONCURRENT_REACHED" | "INVALID_CONFIG";
|
|
6
|
+
declare class AuthError extends Error {
|
|
7
|
+
readonly type: AuthErrorType;
|
|
8
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
9
|
+
readonly name = "AuthError";
|
|
10
|
+
constructor(type: AuthErrorType, message?: string, details?: Record<string, unknown> | undefined);
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/utils/clock.d.ts
|
|
14
|
+
/**
|
|
15
|
+
* Minimal time abstraction shared across stores and orchestrators.
|
|
16
|
+
*
|
|
17
|
+
* Defaults to wall-clock; tests inject a fake clock to deterministically
|
|
18
|
+
* advance time. Kept tiny (single `now()` method) so any timing primitive
|
|
19
|
+
* — Date, performance.now-ish, monotonic, etc. — can be plugged in.
|
|
20
|
+
*/
|
|
21
|
+
interface Clock {
|
|
22
|
+
now(): number;
|
|
23
|
+
}
|
|
24
|
+
declare const defaultClock: Clock;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/stores/memory.d.ts
|
|
27
|
+
/**
|
|
28
|
+
* In-memory implementation of CredentialStore using random opaque token IDs.
|
|
29
|
+
*
|
|
30
|
+
* - `Map<token, state>` is the primary index.
|
|
31
|
+
* - `Map<userId, Set<token>>` is a secondary index for `revokeAllForUser` /
|
|
32
|
+
* `listForUser` without scanning the full primary map.
|
|
33
|
+
* - Both maps are kept in sync on every mutation.
|
|
34
|
+
*/
|
|
35
|
+
declare class CredentialStoreMemory<TClaims extends object = object> implements CredentialStore<TClaims> {
|
|
36
|
+
private readonly states;
|
|
37
|
+
private readonly byUser;
|
|
38
|
+
private readonly clock;
|
|
39
|
+
constructor(opts?: {
|
|
40
|
+
clock?: Clock;
|
|
41
|
+
});
|
|
42
|
+
persist(state: CredentialState<TClaims>, ttl?: number): Promise<string>;
|
|
43
|
+
retrieve(token: string): Promise<CredentialState<TClaims> | null>;
|
|
44
|
+
consume(token: string): Promise<CredentialState<TClaims> | null>;
|
|
45
|
+
update(token: string, state: CredentialState<TClaims>): Promise<string>;
|
|
46
|
+
revoke(token: string): Promise<void>;
|
|
47
|
+
revokeAllForUser(userId: string): Promise<number>;
|
|
48
|
+
listForUser(userId: string): Promise<Array<CredentialState<TClaims> & {
|
|
49
|
+
token: string;
|
|
50
|
+
}>>;
|
|
51
|
+
private indexAdd;
|
|
52
|
+
private indexRemove;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/stores/denylist-memory.d.ts
|
|
56
|
+
/**
|
|
57
|
+
* In-memory denylist for stateless revocation.
|
|
58
|
+
*
|
|
59
|
+
* Stores a map from JTI (or any opaque identifier) to absolute expiry timestamp.
|
|
60
|
+
* `has` returns false for entries whose TTL has elapsed and lazily removes them.
|
|
61
|
+
* `cleanup` performs a sweep across the map and returns the number of entries
|
|
62
|
+
* removed, useful for periodic compaction in long-lived processes.
|
|
63
|
+
*/
|
|
64
|
+
declare class DenylistStoreMemory implements DenylistStore {
|
|
65
|
+
private readonly entries;
|
|
66
|
+
private readonly clock;
|
|
67
|
+
constructor(opts?: {
|
|
68
|
+
clock?: Clock;
|
|
69
|
+
});
|
|
70
|
+
add(jti: string, expiresAt: number): Promise<void>;
|
|
71
|
+
has(jti: string): Promise<boolean>;
|
|
72
|
+
cleanup(): Promise<number>;
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/stores/jwt.d.ts
|
|
76
|
+
/**
|
|
77
|
+
* Supported JWT signing algorithms.
|
|
78
|
+
* - HS256/HS384/HS512: symmetric (shared secret).
|
|
79
|
+
* - RS*, ES*, EdDSA: asymmetric (private/public keypair).
|
|
80
|
+
*/
|
|
81
|
+
type JwtAlgorithm = "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512" | "ES256" | "ES384" | "ES512" | "EdDSA";
|
|
82
|
+
interface CredentialStoreJwtOptions {
|
|
83
|
+
/** Signing algorithm. Defaults to 'HS256'. */
|
|
84
|
+
algorithm?: JwtAlgorithm;
|
|
85
|
+
/** For HS*: secret string or Uint8Array (>=32 bytes recommended for HS256). */
|
|
86
|
+
secret?: string | Uint8Array | CryptoKey;
|
|
87
|
+
/** For asymmetric algorithms: signing (private) key. */
|
|
88
|
+
privateKey?: CryptoKey | Uint8Array;
|
|
89
|
+
/** For asymmetric algorithms: verifying (public) key. */
|
|
90
|
+
publicKey?: CryptoKey | Uint8Array;
|
|
91
|
+
/** Issuer claim (`iss`). Optional. */
|
|
92
|
+
issuer?: string;
|
|
93
|
+
/** Audience claim (`aud`). Optional. */
|
|
94
|
+
audience?: string;
|
|
95
|
+
/** Optional denylist for revocation support. */
|
|
96
|
+
denylist?: DenylistStore;
|
|
97
|
+
/** Optional clock for testability. */
|
|
98
|
+
clock?: Clock;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Stateless credential store that signs the credential state into a JWT.
|
|
102
|
+
*
|
|
103
|
+
* The token IS the state — there is no internal map. State is reconstructed
|
|
104
|
+
* by verifying signature/claims on every retrieve. Revocation requires a
|
|
105
|
+
* `denylist`; without one, `revoke`/`update`/`consume` throw
|
|
106
|
+
* STATELESS_OPERATION_UNSUPPORTED.
|
|
107
|
+
*/
|
|
108
|
+
declare class CredentialStoreJwt<TClaims extends object = object> implements CredentialStore<TClaims> {
|
|
109
|
+
private readonly algorithm;
|
|
110
|
+
private readonly signingKey;
|
|
111
|
+
private readonly verifyingKey;
|
|
112
|
+
private readonly issuer?;
|
|
113
|
+
private readonly audience?;
|
|
114
|
+
private readonly denylist?;
|
|
115
|
+
private readonly clock;
|
|
116
|
+
/**
|
|
117
|
+
* Per-user revocation epoch (ms). `revokeAllForUser` sets this to
|
|
118
|
+
* `clock.now()`; `retrieve` rejects any token whose `iatMs` predates the
|
|
119
|
+
* user's epoch (same-ms mints are accepted so recovery/invite flows can
|
|
120
|
+
* revoke and re-issue in one tick). Compensates for JWT statelessness so
|
|
121
|
+
* password-change cascades invalidate tokens minted before the change.
|
|
122
|
+
* In-memory: resets on process restart (a known JWT limitation — production
|
|
123
|
+
* deployments needing durability should back this with an external store).
|
|
124
|
+
*/
|
|
125
|
+
private readonly epochs;
|
|
126
|
+
constructor(opts: CredentialStoreJwtOptions);
|
|
127
|
+
persist(state: CredentialState<TClaims>, ttl?: number): Promise<string>;
|
|
128
|
+
retrieve(token: string): Promise<CredentialState<TClaims> | null>;
|
|
129
|
+
consume(token: string): Promise<CredentialState<TClaims> | null>;
|
|
130
|
+
update(token: string, state: CredentialState<TClaims>): Promise<string>;
|
|
131
|
+
revoke(token: string): Promise<void>;
|
|
132
|
+
revokeAllForUser(userId: string): Promise<number>;
|
|
133
|
+
private requireDenylist;
|
|
134
|
+
/** Convert payload `exp` (seconds) to ms; fall back to a 60s window. */
|
|
135
|
+
private payloadExpMs;
|
|
136
|
+
/**
|
|
137
|
+
* Reject tokens minted before the user's revocation epoch. Same-ms mints
|
|
138
|
+
* (`iatMs === epoch`) are accepted so a workflow can revoke and re-issue in
|
|
139
|
+
* one tick. `iatMs` mirrors `iat` at ms precision; we fall back to
|
|
140
|
+
* `iat * 1000` for tokens that predate the mirror field.
|
|
141
|
+
*/
|
|
142
|
+
private passesEpoch;
|
|
143
|
+
private verify;
|
|
144
|
+
private payloadToState;
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/stores/encapsulated.d.ts
|
|
148
|
+
interface CredentialStoreEncapsulatedOptions {
|
|
149
|
+
/**
|
|
150
|
+
* Encryption key material. If a 32-byte Buffer/Uint8Array is provided it is
|
|
151
|
+
* used directly; otherwise it is run through scrypt to derive a 32-byte key.
|
|
152
|
+
*/
|
|
153
|
+
secret: string | Buffer | Uint8Array;
|
|
154
|
+
/** Optional denylist for revocation support. */
|
|
155
|
+
denylist?: DenylistStore;
|
|
156
|
+
/** Optional clock for testability. */
|
|
157
|
+
clock?: Clock;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Stateless credential store that encrypts the credential state with
|
|
161
|
+
* AES-256-GCM. The token IS the encrypted blob, base64url-encoded as
|
|
162
|
+
* `iv (12B) || ciphertext || authTag (16B)`.
|
|
163
|
+
*
|
|
164
|
+
* Same denylist semantics as {@link CredentialStoreJwt}: revocation /
|
|
165
|
+
* single-use consume / update require a denylist; otherwise these operations
|
|
166
|
+
* throw STATELESS_OPERATION_UNSUPPORTED.
|
|
167
|
+
*
|
|
168
|
+
* Key derivation:
|
|
169
|
+
* - A 32-byte `Buffer`/`Uint8Array` `secret` is used as the AES key directly
|
|
170
|
+
* (no KDF).
|
|
171
|
+
* - Any other secret (string, shorter/longer buffer) is run through scrypt
|
|
172
|
+
* with a fixed library-scoped salt to produce a 32-byte key. The salt is
|
|
173
|
+
* intentionally fixed so keys remain stable across process restarts; if it
|
|
174
|
+
* were random, every previously issued token would become undecryptable.
|
|
175
|
+
* For maximum protection against rainbow tables on weak passphrases,
|
|
176
|
+
* provide a 32-byte random buffer as `secret` (the KDF path is skipped).
|
|
177
|
+
*
|
|
178
|
+
* Revocation caveat: `revokeAllForUser` uses an in-memory per-user epoch map.
|
|
179
|
+
* It does not survive process restart and does not sync across instances —
|
|
180
|
+
* the same limitation as `CredentialStoreJwt`. Use `CredentialStoreRedis` or
|
|
181
|
+
* `CredentialStoreAtscriptDb` when you need native enumeration with durable
|
|
182
|
+
* cross-instance cascade semantics.
|
|
183
|
+
*/
|
|
184
|
+
declare class CredentialStoreEncapsulated<TClaims extends object = object> implements CredentialStore<TClaims> {
|
|
185
|
+
private readonly key;
|
|
186
|
+
private readonly denylist?;
|
|
187
|
+
private readonly clock;
|
|
188
|
+
/**
|
|
189
|
+
* Per-user revocation epoch (ms). `revokeAllForUser` sets this to
|
|
190
|
+
* `clock.now()`; `retrieve`/`consume` reject any decrypted state whose
|
|
191
|
+
* `issuedAt` is strictly less than the user's epoch (same-ms mints are
|
|
192
|
+
* accepted so recovery/invite flows can revoke and re-issue in the same
|
|
193
|
+
* tick). Mirrors the JWT store pattern; see class JSDoc for durability
|
|
194
|
+
* caveats.
|
|
195
|
+
*/
|
|
196
|
+
private readonly epochs;
|
|
197
|
+
constructor(opts: CredentialStoreEncapsulatedOptions);
|
|
198
|
+
persist(state: CredentialState<TClaims>, ttl?: number): Promise<string>;
|
|
199
|
+
retrieve(token: string): Promise<CredentialState<TClaims> | null>;
|
|
200
|
+
consume(token: string): Promise<CredentialState<TClaims> | null>;
|
|
201
|
+
update(token: string, state: CredentialState<TClaims>): Promise<string>;
|
|
202
|
+
revoke(token: string): Promise<void>;
|
|
203
|
+
revokeAllForUser(userId: string): Promise<number>;
|
|
204
|
+
/**
|
|
205
|
+
* Reject decrypted payloads whose `issuedAt` predates the user's revocation
|
|
206
|
+
* epoch. Same-ms mints (`issuedAt === epoch`) are accepted so a workflow can
|
|
207
|
+
* revoke and re-issue in one tick. Mirrors `CredentialStoreJwt.passesEpoch`.
|
|
208
|
+
*/
|
|
209
|
+
private passesEpoch;
|
|
210
|
+
private requireDenylist;
|
|
211
|
+
private decrypt;
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/credential/auth-credential.d.ts
|
|
215
|
+
interface AuthCredentialOptions<TClaims extends object = object> {
|
|
216
|
+
/** Pluggable credential store (Memory, JWT, Encapsulated, ...). */
|
|
217
|
+
store: CredentialStore<TClaims>;
|
|
218
|
+
/** Default 'token' — distinguishes session-style from token-style use. */
|
|
219
|
+
method?: "session" | "token";
|
|
220
|
+
/** Access token TTL in milliseconds. Defaults to 1 hour. Must be > 0. */
|
|
221
|
+
accessTtl?: number;
|
|
222
|
+
/** If provided, refresh tokens are enabled. */
|
|
223
|
+
refresh?: RefreshConfig;
|
|
224
|
+
/**
|
|
225
|
+
* Optional denylist consulted by `validate` keyed on the raw token.
|
|
226
|
+
*
|
|
227
|
+
* Note: stateless stores (JWT, Encapsulated) maintain their own denylist
|
|
228
|
+
* keyed on `jti` for `revoke`/`update`/`consume`. Sharing a single
|
|
229
|
+
* `DenylistStore` instance across both is safe (the keyspaces are disjoint:
|
|
230
|
+
* raw tokens vs UUID jti) but conceptually they serve different purposes.
|
|
231
|
+
*/
|
|
232
|
+
denylist?: DenylistStore;
|
|
233
|
+
/** Maximum concurrent active access credentials per user. */
|
|
234
|
+
maxConcurrent?: number;
|
|
235
|
+
/** Behavior when limit reached: 'reject' (default) or 'evict-oldest'. */
|
|
236
|
+
onLimit?: "reject" | "evict-oldest";
|
|
237
|
+
/** Optional clock for testability. */
|
|
238
|
+
clock?: Clock;
|
|
239
|
+
}
|
|
240
|
+
interface IssueOptions<TClaims extends object = object> {
|
|
241
|
+
claims?: TClaims;
|
|
242
|
+
metadata?: CredentialMetadata;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Orchestrates credential issuance, validation, refresh, and revocation
|
|
246
|
+
* on top of a pluggable {@link CredentialStore}.
|
|
247
|
+
*
|
|
248
|
+
* Design notes:
|
|
249
|
+
* - `credentialId` returned in {@link AuthContext} is a SHA-256 fingerprint of
|
|
250
|
+
* the access token, never the token itself. The fingerprint is stable
|
|
251
|
+
* per-token, safe to log/persist, and cannot be replayed against the API.
|
|
252
|
+
* M3 will switch to a `jti` claim for JWT/encapsulated stores.
|
|
253
|
+
* - `kind: 'access' | 'refresh'` on {@link CredentialState} discriminates
|
|
254
|
+
* tokens stored side-by-side in the same store.
|
|
255
|
+
* - `listForUser` and `maxConcurrent` consider only access-kind credentials,
|
|
256
|
+
* matching how callers typically display "active sessions".
|
|
257
|
+
* - On detected refresh-reuse-after-grace, the orchestrator best-effort
|
|
258
|
+
* revokes ALL credentials for the affected user (access + refresh).
|
|
259
|
+
* This is OAuth-best-practice theft response; documented and intentional.
|
|
260
|
+
*/
|
|
261
|
+
declare class AuthCredential<TClaims extends object = object> {
|
|
262
|
+
private readonly store;
|
|
263
|
+
private readonly method;
|
|
264
|
+
private readonly accessTtl;
|
|
265
|
+
private readonly refreshConfig?;
|
|
266
|
+
private readonly denylist?;
|
|
267
|
+
private readonly maxConcurrent?;
|
|
268
|
+
private readonly onLimit;
|
|
269
|
+
private readonly clock;
|
|
270
|
+
/**
|
|
271
|
+
* Recently-consumed refresh tokens, keyed by the raw refresh token string.
|
|
272
|
+
* Lets `'always'` rotation detect reuse: stateful stores forget the token
|
|
273
|
+
* after `consume`, and stateless (JWT) stores hide it behind a denylist hit
|
|
274
|
+
* on `retrieve`. Without this map the orchestrator can no longer distinguish
|
|
275
|
+
* "fake token" from "previously valid token replayed". Pruned lazily on
|
|
276
|
+
* access; bounded by refresh TTL.
|
|
277
|
+
*/
|
|
278
|
+
private readonly consumedRefreshes;
|
|
279
|
+
constructor(opts: AuthCredentialOptions<TClaims>);
|
|
280
|
+
issue(userId: string, options?: IssueOptions<TClaims>): Promise<IssueResult>;
|
|
281
|
+
validate(accessToken: string): Promise<AuthContext<TClaims> | null>;
|
|
282
|
+
refresh(refreshToken: string): Promise<IssueResult>;
|
|
283
|
+
private refreshNone;
|
|
284
|
+
private refreshSliding;
|
|
285
|
+
revoke(token: string): Promise<void>;
|
|
286
|
+
revokeAllForUser(userId: string): Promise<number>;
|
|
287
|
+
listForUser(userId: string): Promise<Array<AuthContext<TClaims>>>;
|
|
288
|
+
private enforceConcurrencyLimit;
|
|
289
|
+
private issueAccessFromRefresh;
|
|
290
|
+
private issueRotatedPair;
|
|
291
|
+
private lookupConsumedRefresh;
|
|
292
|
+
private fireRefreshReuseTheftResponse;
|
|
293
|
+
}
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/email.d.ts
|
|
296
|
+
/**
|
|
297
|
+
* Discriminator for auth-related email events emitted by the workflow stack.
|
|
298
|
+
*
|
|
299
|
+
* `mfa.code` is reserved for v2 — v1 is TOTP only. `login.pincode`,
|
|
300
|
+
* `recovery.pincode`, `invite.pincode` and `notifyNewDevice` are added with
|
|
301
|
+
* BIG 3.1 (login workflow re-implementation).
|
|
302
|
+
*/
|
|
303
|
+
type AuthEmailKind = "recovery.magicLink" | "invite.magicLink" | "mfa.code" | "login.pincode" | "recovery.pincode" | "invite.pincode" | "notifyNewDevice";
|
|
304
|
+
/**
|
|
305
|
+
* Structured event passed to `EmailSender.send()` from inside the auth
|
|
306
|
+
* workflows. Flat and serialisable so consumers can route it to any
|
|
307
|
+
* transport (templated mailer, queue, webhook).
|
|
308
|
+
*/
|
|
309
|
+
interface AuthEmailEvent {
|
|
310
|
+
kind: AuthEmailKind;
|
|
311
|
+
recipient: string;
|
|
312
|
+
/** Resume URL for magic-link events. Absent for code / notify events. */
|
|
313
|
+
url?: string;
|
|
314
|
+
/** Numeric OTP for code-bearing events. Absent for magic links / notify. */
|
|
315
|
+
code?: string;
|
|
316
|
+
/** Unix-ms timestamp at which the token / code expires. */
|
|
317
|
+
expiresAt: number;
|
|
318
|
+
/** Recipient's username when known (omitted for invites to new accounts). */
|
|
319
|
+
username?: string;
|
|
320
|
+
/** Free-form payload (e.g. invite-side `roles: string[]`, notify-side `ip`). */
|
|
321
|
+
metadata?: Record<string, unknown>;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Transport interface implemented by consumers. Aoothjs ships no
|
|
325
|
+
* implementation — wire SendGrid / SES / Twilio / queue here.
|
|
326
|
+
*
|
|
327
|
+
* The workflow `await`s this call, so it should not block on slow downstream
|
|
328
|
+
* transports — push to a queue and return.
|
|
329
|
+
*/
|
|
330
|
+
interface EmailSender {
|
|
331
|
+
send(event: AuthEmailEvent): Promise<void>;
|
|
332
|
+
}
|
|
333
|
+
//#endregion
|
|
334
|
+
//#region src/sms.d.ts
|
|
335
|
+
/**
|
|
336
|
+
* Discriminator for SMS events emitted by the auth workflow stack.
|
|
337
|
+
*
|
|
338
|
+
* Mirrors {@link AuthEmailKind} — kept on a separate union because email and
|
|
339
|
+
* SMS transports do not overlap (email carries `url` for magic links; SMS
|
|
340
|
+
* never does).
|
|
341
|
+
*/
|
|
342
|
+
type AuthSmsKind = "login.pincode" | "recovery.pincode" | "invite.pincode";
|
|
343
|
+
/**
|
|
344
|
+
* Structured event passed to {@link SmsSender}`.send()` from inside the auth
|
|
345
|
+
* workflows. Flat and serialisable so consumers can route it to any
|
|
346
|
+
* SMS gateway (Twilio, SNS, queue).
|
|
347
|
+
*/
|
|
348
|
+
interface AuthSmsEvent {
|
|
349
|
+
kind: AuthSmsKind;
|
|
350
|
+
/** E.164 phone number. */
|
|
351
|
+
recipient: string;
|
|
352
|
+
code: string;
|
|
353
|
+
/** Pincode TTL in ms — convenience for templating "expires in N min". */
|
|
354
|
+
ttlMs: number;
|
|
355
|
+
/** User id when known (omitted for pre-resolved flows). */
|
|
356
|
+
userId?: string;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Transport interface implemented by consumers. Aoothjs ships no concrete
|
|
360
|
+
* SMS adapter (Twilio / SNS / etc. are consumer-specific) — only this
|
|
361
|
+
* interface. Register via `setProvideRegistry([SmsSender, () => mySender])`.
|
|
362
|
+
*
|
|
363
|
+
* Boot-time invariant: when a workflow's options include SMS as an MFA
|
|
364
|
+
* transport, the workflow constructor throws if no `SmsSender` is
|
|
365
|
+
* registered (fail loud, not at first user attempt).
|
|
366
|
+
*/
|
|
367
|
+
interface SmsSender {
|
|
368
|
+
send(event: AuthSmsEvent): Promise<void>;
|
|
369
|
+
}
|
|
370
|
+
//#endregion
|
|
371
|
+
//#region src/magic-link.d.ts
|
|
372
|
+
/**
|
|
373
|
+
* Consumer-supplied URL builder. The consumer chooses route, query
|
|
374
|
+
* convention, and base URL — aoothjs does not assume a domain or scheme.
|
|
375
|
+
*
|
|
376
|
+
* Recommended convention: include the token as `?wfs=<token>` so the
|
|
377
|
+
* frontend can mount `<AsWfForm initialToken="...">` to resume the flow.
|
|
378
|
+
*/
|
|
379
|
+
type BuildMagicLinkUrl = (kind: AuthEmailKind, token: string) => string;
|
|
380
|
+
/**
|
|
381
|
+
* 32 bytes of CSPRNG entropy (256 bits) encoded as base64url — 43 chars,
|
|
382
|
+
* URL-safe. Strong enough to survive short TTLs against online guessing.
|
|
383
|
+
*/
|
|
384
|
+
declare function generateMagicLinkToken(): string;
|
|
385
|
+
//#endregion
|
|
386
|
+
export { type AuthContext, AuthCredential, type AuthCredentialOptions, type AuthEmailEvent, type AuthEmailKind, AuthError, type AuthErrorType, type AuthSmsEvent, type AuthSmsKind, type BuildMagicLinkUrl, type Clock, type CredentialMetadata, type CredentialState, type CredentialStore, CredentialStoreEncapsulated, type CredentialStoreEncapsulatedOptions, CredentialStoreJwt, type CredentialStoreJwtOptions, CredentialStoreMemory, type DenylistStore, DenylistStoreMemory, type EmailSender, type IssueOptions, type IssueResult, type JwtAlgorithm, type RefreshConfig, type SmsSender, defaultClock, generateMagicLinkToken };
|