@aooth/user 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 +38 -0
- package/dist/atscript-db.cjs +44 -0
- package/dist/atscript-db.d.cts +55 -0
- package/dist/atscript-db.d.mts +55 -0
- package/dist/atscript-db.mjs +43 -0
- package/dist/index.cjs +824 -0
- package/dist/index.d.cts +233 -0
- package/dist/index.d.mts +233 -0
- package/dist/index.mjs +799 -0
- package/dist/user-store-B_l9vqlQ.mjs +96 -0
- package/dist/user-store-CdWrTeqR.cjs +149 -0
- package/dist/user-store-Dbc9unW3.d.mts +187 -0
- package/dist/user-store-VJPWNgdp.d.cts +187 -0
- package/package.json +75 -0
- package/src/atscript-db/user-credentials.as +42 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { C as UserStoreUpdate, S as UserServiceConfig, _ as TotpConfig, a as LockoutConfig, b as UserAuthErrorType, c as MfaMethod, d as PasswordData, f as PasswordPolicyContext, g as PolicyCheckResult, h as PasswordPolicyInstance, i as LockStatus, l as MfaMethodInfo, m as PasswordPolicyEvalFn, n as AccountData, o as LoginResult, p as PasswordPolicyDef, r as DeepPartial, s as MfaData, t as UserStore, u as PasswordConfig, v as TransferablePolicy, x as UserCredentials, y as TrustedDeviceRecord } from "./user-store-VJPWNgdp.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/errors.d.ts
|
|
4
|
+
declare class UserAuthError extends Error {
|
|
5
|
+
readonly type: UserAuthErrorType;
|
|
6
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
7
|
+
readonly name = "UserAuthError";
|
|
8
|
+
constructor(type: UserAuthErrorType, message?: string, details?: Record<string, unknown> | undefined);
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/password/hasher.d.ts
|
|
12
|
+
declare class PasswordHasher {
|
|
13
|
+
private readonly pepper;
|
|
14
|
+
private readonly N;
|
|
15
|
+
private readonly r;
|
|
16
|
+
private readonly p;
|
|
17
|
+
private readonly keyLength;
|
|
18
|
+
constructor(config?: PasswordConfig);
|
|
19
|
+
hash(password: string): Promise<string>;
|
|
20
|
+
verify(password: string, encoded: string): Promise<boolean>;
|
|
21
|
+
generatePassword(length?: number): string;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/password/policy.d.ts
|
|
25
|
+
declare function normalizePolicies(policies?: (PasswordPolicyDef | PasswordPolicyInstance)[]): PasswordPolicy[];
|
|
26
|
+
declare class PasswordPolicy implements PasswordPolicyInstance {
|
|
27
|
+
readonly rule: string | PasswordPolicyEvalFn;
|
|
28
|
+
readonly description: string;
|
|
29
|
+
readonly errorMessage: string;
|
|
30
|
+
constructor(config: PasswordPolicyDef);
|
|
31
|
+
protected _evalFn: PasswordPolicyEvalFn;
|
|
32
|
+
evaluate(password: string, context?: PasswordPolicyContext): boolean | Promise<boolean>;
|
|
33
|
+
get transferable(): boolean;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/user-service.d.ts
|
|
37
|
+
interface ResolvedConfig {
|
|
38
|
+
password: Required<Omit<PasswordConfig, "policies">> & {
|
|
39
|
+
policies: PasswordPolicy[];
|
|
40
|
+
};
|
|
41
|
+
lockout: Required<LockoutConfig>;
|
|
42
|
+
clock: () => number;
|
|
43
|
+
deviceTrust?: {
|
|
44
|
+
secret: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
declare class UserService<T extends object = object> {
|
|
48
|
+
protected readonly store: UserStore<T>;
|
|
49
|
+
protected readonly config: ResolvedConfig;
|
|
50
|
+
protected readonly hasher: PasswordHasher;
|
|
51
|
+
constructor(store: UserStore<T>, config?: UserServiceConfig);
|
|
52
|
+
/**
|
|
53
|
+
* @param extras Optional partial user fields merged AFTER the base
|
|
54
|
+
* `UserCredentials` shape, so callers can populate consumer-specific
|
|
55
|
+
* required fields (e.g. `tenantId`) without subclassing the store.
|
|
56
|
+
* Because the merge is shallow and extras win, overlapping top-level
|
|
57
|
+
* keys (`id`, `account`, `mfa`, ...) replace the defaults entirely —
|
|
58
|
+
* pass nested objects with all required sub-fields if you intend to
|
|
59
|
+
* override them.
|
|
60
|
+
*/
|
|
61
|
+
createUser(username: string, password?: string, extras?: Partial<T>): Promise<UserCredentials & T>;
|
|
62
|
+
getUser(username: string): Promise<UserCredentials & T>;
|
|
63
|
+
login(username: string, password: string): Promise<LoginResult<T>>;
|
|
64
|
+
verifyPassword(username: string, password: string): Promise<boolean>;
|
|
65
|
+
changePassword(username: string, currentPassword: string, newPassword: string, repeatPassword?: string): Promise<void>;
|
|
66
|
+
setPassword(username: string, newPassword: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Hard-delete the user row. Returns nothing on success. Throws
|
|
69
|
+
* `UserAuthError("NOT_FOUND")` when no row matches `username`. Used by the
|
|
70
|
+
* invite workflow's `auth.cancelInvite` to revoke a pending invitation.
|
|
71
|
+
*/
|
|
72
|
+
deleteUser(username: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Deep-merge `patch` into the user record (top-level fields are shallow-
|
|
75
|
+
* merged; `account` / `mfa` / `password` are merged per their
|
|
76
|
+
* `@db.patch.strategy 'merge'` declaration). Returns the patched record.
|
|
77
|
+
* Used by the invite workflow's `applyProfile` default fallback.
|
|
78
|
+
*/
|
|
79
|
+
update(username: string, patch: Partial<UserCredentials & T>): Promise<UserCredentials & T>;
|
|
80
|
+
activateAccount(username: string): Promise<void>;
|
|
81
|
+
deactivateAccount(username: string): Promise<void>;
|
|
82
|
+
lockAccount(username: string, reason: string, duration?: number): Promise<void>;
|
|
83
|
+
unlockAccount(username: string): Promise<void>;
|
|
84
|
+
getLockStatus(account: UserCredentials["account"]): LockStatus;
|
|
85
|
+
checkPolicies(password: string, passwordData?: PasswordData): Promise<PolicyCheckResult>;
|
|
86
|
+
getTransferablePolicies(): TransferablePolicy[];
|
|
87
|
+
addMfaMethod(username: string, method: MfaMethod): Promise<void>;
|
|
88
|
+
confirmMfaMethod(username: string, name: string): Promise<void>;
|
|
89
|
+
removeMfaMethod(username: string, name: string): Promise<void>;
|
|
90
|
+
setDefaultMfaMethod(username: string, name: string): Promise<void>;
|
|
91
|
+
setMfaAutoSend(username: string, value: boolean): Promise<void>;
|
|
92
|
+
getAvailableMfaMethods(mfa: MfaData): MfaMethodInfo[];
|
|
93
|
+
/**
|
|
94
|
+
* Generate `count` plaintext backup codes (default 10), persist their
|
|
95
|
+
* hashes (replacing any existing batch), and return the plaintext codes
|
|
96
|
+
* once for the caller to deliver to the user. Plaintext is never
|
|
97
|
+
* recoverable after this call returns.
|
|
98
|
+
*
|
|
99
|
+
* Throws `UserAuthError("NOT_FOUND")` if the user does not exist.
|
|
100
|
+
*/
|
|
101
|
+
generateBackupCodes(username: string, count?: number): Promise<string[]>;
|
|
102
|
+
/**
|
|
103
|
+
* Consume a backup code: returns `true` and removes the matching hash
|
|
104
|
+
* from storage if `code` matches a stored backup code; returns `false`
|
|
105
|
+
* if no match (without modifying storage).
|
|
106
|
+
*
|
|
107
|
+
* Read-then-write is not atomic at this layer: two concurrent consumes
|
|
108
|
+
* of the same code may both succeed, with the last write winning. The
|
|
109
|
+
* underlying store API does not expose an atomic match-and-remove, so
|
|
110
|
+
* this is acceptable at the intended scale (backup codes are a fallback
|
|
111
|
+
* path, not a hot one). Wrap in your store's transaction primitive if a
|
|
112
|
+
* stricter guarantee is required.
|
|
113
|
+
*
|
|
114
|
+
* Throws `UserAuthError("NOT_FOUND")` if the user does not exist.
|
|
115
|
+
*/
|
|
116
|
+
consumeBackupCode(username: string, code: string): Promise<boolean>;
|
|
117
|
+
/**
|
|
118
|
+
* Verify a TOTP code against the user's confirmed `totp` MFA method.
|
|
119
|
+
* Failures bump the same `failedLoginAttempts` counter as `login` so an
|
|
120
|
+
* attacker who knows the password but not the TOTP gets `lockout.threshold`
|
|
121
|
+
* total tries across BOTH factors, not `2 * threshold`.
|
|
122
|
+
*/
|
|
123
|
+
verifyMfa(username: string, code: string, config?: TotpConfig): Promise<void>;
|
|
124
|
+
getPasswordHasher(): PasswordHasher;
|
|
125
|
+
getConfig(): Readonly<ResolvedConfig>;
|
|
126
|
+
/**
|
|
127
|
+
* Mint a freshly-signed trust record (does NOT persist — pair with
|
|
128
|
+
* `addTrustedDevice`). Throws when `deviceTrust.secret` is unset.
|
|
129
|
+
*/
|
|
130
|
+
issueTrustedDevice(userId: string, opts: {
|
|
131
|
+
ip?: string;
|
|
132
|
+
ttlMs: number;
|
|
133
|
+
name?: string;
|
|
134
|
+
}): TrustedDeviceRecord;
|
|
135
|
+
/**
|
|
136
|
+
* Append a trust record to the user's `trustedDevices` list. Read-modify-
|
|
137
|
+
* write — the array shape is preserved end-to-end so DB adapters with a
|
|
138
|
+
* merge strategy replace the whole array.
|
|
139
|
+
*/
|
|
140
|
+
addTrustedDevice(username: string, record: TrustedDeviceRecord): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Returns true when the supplied token (a) signs against the user+ip with
|
|
143
|
+
* the configured secret, AND (b) matches a persisted record that is still
|
|
144
|
+
* within its expiry window and whose bound IP (if any) matches.
|
|
145
|
+
*/
|
|
146
|
+
verifyTrustedDevice(username: string, token: string, ip?: string): Promise<boolean>;
|
|
147
|
+
/**
|
|
148
|
+
* Remove a specific trust record from the user. No-op when the record is
|
|
149
|
+
* absent — mirrors the legacy `DeviceTrustStore.revoke` semantics.
|
|
150
|
+
*/
|
|
151
|
+
revokeTrustedDevice(username: string, token: string): Promise<void>;
|
|
152
|
+
listTrustedDevices(username: string): Promise<TrustedDeviceRecord[]>;
|
|
153
|
+
private requireDeviceTrustSecret;
|
|
154
|
+
private applyPasswordChange;
|
|
155
|
+
private hasConfirmedMfaMethods;
|
|
156
|
+
/**
|
|
157
|
+
* If `account.locked`: auto-unlock when the lock has expired (mutating
|
|
158
|
+
* `account` in place), or throw `LOCKED` otherwise.
|
|
159
|
+
*/
|
|
160
|
+
private ensureNotLockedOrThrow;
|
|
161
|
+
/**
|
|
162
|
+
* Bump `failedLoginAttempts`, locking the account when threshold is hit,
|
|
163
|
+
* and always throw `errorCode` (with `details.lockEnds` when the lockout
|
|
164
|
+
* just tripped). Used by both `login` and `verifyMfa` so the two factors
|
|
165
|
+
* share one counter.
|
|
166
|
+
*/
|
|
167
|
+
private incrementAndMaybeLock;
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/store/memory.d.ts
|
|
171
|
+
declare class UserStoreMemory<T extends object = object> extends UserStore<T> {
|
|
172
|
+
private store;
|
|
173
|
+
constructor(seed?: Record<string, UserCredentials & T>);
|
|
174
|
+
exists(username: string): Promise<boolean>;
|
|
175
|
+
findByUsername(username: string): Promise<(UserCredentials & T) | null>;
|
|
176
|
+
create(data: UserCredentials & T): Promise<void>;
|
|
177
|
+
update(username: string, update: UserStoreUpdate): Promise<boolean>;
|
|
178
|
+
delete(username: string): Promise<boolean>;
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/password/policies.d.ts
|
|
182
|
+
declare const ppHasMinLength: (min?: number) => PasswordPolicyDef;
|
|
183
|
+
declare const ppHasUpperCase: (n?: number) => PasswordPolicyDef;
|
|
184
|
+
declare const ppHasLowerCase: (n?: number) => PasswordPolicyDef;
|
|
185
|
+
declare const ppHasNumber: (n?: number) => PasswordPolicyDef;
|
|
186
|
+
declare const ppHasSpecialChar: (n?: number) => PasswordPolicyDef;
|
|
187
|
+
declare const ppMaxRepeatedChars: (maxRepeated?: number) => PasswordPolicyDef;
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/mfa/totp.d.ts
|
|
190
|
+
declare function generateTotpSecret(bytes?: number): string;
|
|
191
|
+
declare function generateTotpUri(secret: string, issuer: string, account: string, config?: Pick<TotpConfig, "period" | "digits">): string;
|
|
192
|
+
declare function generateTotpCode(secret: string, config?: TotpConfig): string;
|
|
193
|
+
declare function verifyTotpCode(secret: string, code: string, config?: TotpConfig): boolean;
|
|
194
|
+
declare function generateMfaCode(length?: number): string;
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/mfa/codes.d.ts
|
|
197
|
+
/**
|
|
198
|
+
* SHA-256 hash of an MFA code (e.g. one-time email/SMS code or backup code).
|
|
199
|
+
*
|
|
200
|
+
* Hex-encoded for stable, comparable output regardless of input case/format.
|
|
201
|
+
* Use {@link verifyMfaCode} to compare a submitted plaintext code against the
|
|
202
|
+
* stored hash in constant time.
|
|
203
|
+
*/
|
|
204
|
+
declare function hashMfaCode(code: string): string;
|
|
205
|
+
/**
|
|
206
|
+
* Constant-time comparison of a submitted plaintext code against an
|
|
207
|
+
* expected SHA-256 hex hash (as produced by {@link hashMfaCode}).
|
|
208
|
+
*
|
|
209
|
+
* Returns false for malformed/empty expected hashes (timingSafeEqual
|
|
210
|
+
* requires equal-length, non-empty buffers).
|
|
211
|
+
*/
|
|
212
|
+
declare function verifyMfaCode(submitted: string, expectedHash: string): boolean;
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/mfa/backup-codes.d.ts
|
|
215
|
+
/**
|
|
216
|
+
* Generate `count` cryptographically-random backup codes (default 10).
|
|
217
|
+
*
|
|
218
|
+
* Format: 10 characters from the 31-char safe alphabet (uppercase letters +
|
|
219
|
+
* digits, omitting I/O/L/0/1), grouped as `XXXX-XXXX-XX`.
|
|
220
|
+
*
|
|
221
|
+
* Returns plaintext codes for the caller to deliver to the user — these
|
|
222
|
+
* should be hashed via {@link hashMfaCode} before persistence and never
|
|
223
|
+
* shown to the user again.
|
|
224
|
+
*/
|
|
225
|
+
declare function generateBackupCodePlaintext(count?: number): string[];
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/utils.d.ts
|
|
228
|
+
declare function maskEmail(email: string): string;
|
|
229
|
+
declare function maskPhone(phone: string): string;
|
|
230
|
+
declare function maskMfaValue(method: MfaMethod): string;
|
|
231
|
+
declare function setAtPath(obj: object, path: string, value: unknown): void;
|
|
232
|
+
//#endregion
|
|
233
|
+
export { type AccountData, type DeepPartial, type LockStatus, type LockoutConfig, type LoginResult, type MfaData, type MfaMethod, type MfaMethodInfo, type PasswordConfig, type PasswordData, PasswordHasher, PasswordPolicy, type PasswordPolicyContext, type PasswordPolicyDef, type PasswordPolicyEvalFn, type PasswordPolicyInstance, type PolicyCheckResult, type TotpConfig, type TransferablePolicy, type TrustedDeviceRecord, UserAuthError, type UserAuthErrorType, type UserCredentials, UserService, type UserServiceConfig, UserStore, UserStoreMemory, type UserStoreUpdate, generateBackupCodePlaintext, generateMfaCode, generateTotpCode, generateTotpSecret, generateTotpUri, hashMfaCode, maskEmail, maskMfaValue, maskPhone, normalizePolicies, ppHasLowerCase, ppHasMinLength, ppHasNumber, ppHasSpecialChar, ppHasUpperCase, ppMaxRepeatedChars, setAtPath, verifyMfaCode, verifyTotpCode };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { C as UserStoreUpdate, S as UserServiceConfig, _ as TotpConfig, a as LockoutConfig, b as UserAuthErrorType, c as MfaMethod, d as PasswordData, f as PasswordPolicyContext, g as PolicyCheckResult, h as PasswordPolicyInstance, i as LockStatus, l as MfaMethodInfo, m as PasswordPolicyEvalFn, n as AccountData, o as LoginResult, p as PasswordPolicyDef, r as DeepPartial, s as MfaData, t as UserStore, u as PasswordConfig, v as TransferablePolicy, x as UserCredentials, y as TrustedDeviceRecord } from "./user-store-Dbc9unW3.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/errors.d.ts
|
|
4
|
+
declare class UserAuthError extends Error {
|
|
5
|
+
readonly type: UserAuthErrorType;
|
|
6
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
7
|
+
readonly name = "UserAuthError";
|
|
8
|
+
constructor(type: UserAuthErrorType, message?: string, details?: Record<string, unknown> | undefined);
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/password/hasher.d.ts
|
|
12
|
+
declare class PasswordHasher {
|
|
13
|
+
private readonly pepper;
|
|
14
|
+
private readonly N;
|
|
15
|
+
private readonly r;
|
|
16
|
+
private readonly p;
|
|
17
|
+
private readonly keyLength;
|
|
18
|
+
constructor(config?: PasswordConfig);
|
|
19
|
+
hash(password: string): Promise<string>;
|
|
20
|
+
verify(password: string, encoded: string): Promise<boolean>;
|
|
21
|
+
generatePassword(length?: number): string;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/password/policy.d.ts
|
|
25
|
+
declare function normalizePolicies(policies?: (PasswordPolicyDef | PasswordPolicyInstance)[]): PasswordPolicy[];
|
|
26
|
+
declare class PasswordPolicy implements PasswordPolicyInstance {
|
|
27
|
+
readonly rule: string | PasswordPolicyEvalFn;
|
|
28
|
+
readonly description: string;
|
|
29
|
+
readonly errorMessage: string;
|
|
30
|
+
constructor(config: PasswordPolicyDef);
|
|
31
|
+
protected _evalFn: PasswordPolicyEvalFn;
|
|
32
|
+
evaluate(password: string, context?: PasswordPolicyContext): boolean | Promise<boolean>;
|
|
33
|
+
get transferable(): boolean;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/user-service.d.ts
|
|
37
|
+
interface ResolvedConfig {
|
|
38
|
+
password: Required<Omit<PasswordConfig, "policies">> & {
|
|
39
|
+
policies: PasswordPolicy[];
|
|
40
|
+
};
|
|
41
|
+
lockout: Required<LockoutConfig>;
|
|
42
|
+
clock: () => number;
|
|
43
|
+
deviceTrust?: {
|
|
44
|
+
secret: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
declare class UserService<T extends object = object> {
|
|
48
|
+
protected readonly store: UserStore<T>;
|
|
49
|
+
protected readonly config: ResolvedConfig;
|
|
50
|
+
protected readonly hasher: PasswordHasher;
|
|
51
|
+
constructor(store: UserStore<T>, config?: UserServiceConfig);
|
|
52
|
+
/**
|
|
53
|
+
* @param extras Optional partial user fields merged AFTER the base
|
|
54
|
+
* `UserCredentials` shape, so callers can populate consumer-specific
|
|
55
|
+
* required fields (e.g. `tenantId`) without subclassing the store.
|
|
56
|
+
* Because the merge is shallow and extras win, overlapping top-level
|
|
57
|
+
* keys (`id`, `account`, `mfa`, ...) replace the defaults entirely —
|
|
58
|
+
* pass nested objects with all required sub-fields if you intend to
|
|
59
|
+
* override them.
|
|
60
|
+
*/
|
|
61
|
+
createUser(username: string, password?: string, extras?: Partial<T>): Promise<UserCredentials & T>;
|
|
62
|
+
getUser(username: string): Promise<UserCredentials & T>;
|
|
63
|
+
login(username: string, password: string): Promise<LoginResult<T>>;
|
|
64
|
+
verifyPassword(username: string, password: string): Promise<boolean>;
|
|
65
|
+
changePassword(username: string, currentPassword: string, newPassword: string, repeatPassword?: string): Promise<void>;
|
|
66
|
+
setPassword(username: string, newPassword: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Hard-delete the user row. Returns nothing on success. Throws
|
|
69
|
+
* `UserAuthError("NOT_FOUND")` when no row matches `username`. Used by the
|
|
70
|
+
* invite workflow's `auth.cancelInvite` to revoke a pending invitation.
|
|
71
|
+
*/
|
|
72
|
+
deleteUser(username: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Deep-merge `patch` into the user record (top-level fields are shallow-
|
|
75
|
+
* merged; `account` / `mfa` / `password` are merged per their
|
|
76
|
+
* `@db.patch.strategy 'merge'` declaration). Returns the patched record.
|
|
77
|
+
* Used by the invite workflow's `applyProfile` default fallback.
|
|
78
|
+
*/
|
|
79
|
+
update(username: string, patch: Partial<UserCredentials & T>): Promise<UserCredentials & T>;
|
|
80
|
+
activateAccount(username: string): Promise<void>;
|
|
81
|
+
deactivateAccount(username: string): Promise<void>;
|
|
82
|
+
lockAccount(username: string, reason: string, duration?: number): Promise<void>;
|
|
83
|
+
unlockAccount(username: string): Promise<void>;
|
|
84
|
+
getLockStatus(account: UserCredentials["account"]): LockStatus;
|
|
85
|
+
checkPolicies(password: string, passwordData?: PasswordData): Promise<PolicyCheckResult>;
|
|
86
|
+
getTransferablePolicies(): TransferablePolicy[];
|
|
87
|
+
addMfaMethod(username: string, method: MfaMethod): Promise<void>;
|
|
88
|
+
confirmMfaMethod(username: string, name: string): Promise<void>;
|
|
89
|
+
removeMfaMethod(username: string, name: string): Promise<void>;
|
|
90
|
+
setDefaultMfaMethod(username: string, name: string): Promise<void>;
|
|
91
|
+
setMfaAutoSend(username: string, value: boolean): Promise<void>;
|
|
92
|
+
getAvailableMfaMethods(mfa: MfaData): MfaMethodInfo[];
|
|
93
|
+
/**
|
|
94
|
+
* Generate `count` plaintext backup codes (default 10), persist their
|
|
95
|
+
* hashes (replacing any existing batch), and return the plaintext codes
|
|
96
|
+
* once for the caller to deliver to the user. Plaintext is never
|
|
97
|
+
* recoverable after this call returns.
|
|
98
|
+
*
|
|
99
|
+
* Throws `UserAuthError("NOT_FOUND")` if the user does not exist.
|
|
100
|
+
*/
|
|
101
|
+
generateBackupCodes(username: string, count?: number): Promise<string[]>;
|
|
102
|
+
/**
|
|
103
|
+
* Consume a backup code: returns `true` and removes the matching hash
|
|
104
|
+
* from storage if `code` matches a stored backup code; returns `false`
|
|
105
|
+
* if no match (without modifying storage).
|
|
106
|
+
*
|
|
107
|
+
* Read-then-write is not atomic at this layer: two concurrent consumes
|
|
108
|
+
* of the same code may both succeed, with the last write winning. The
|
|
109
|
+
* underlying store API does not expose an atomic match-and-remove, so
|
|
110
|
+
* this is acceptable at the intended scale (backup codes are a fallback
|
|
111
|
+
* path, not a hot one). Wrap in your store's transaction primitive if a
|
|
112
|
+
* stricter guarantee is required.
|
|
113
|
+
*
|
|
114
|
+
* Throws `UserAuthError("NOT_FOUND")` if the user does not exist.
|
|
115
|
+
*/
|
|
116
|
+
consumeBackupCode(username: string, code: string): Promise<boolean>;
|
|
117
|
+
/**
|
|
118
|
+
* Verify a TOTP code against the user's confirmed `totp` MFA method.
|
|
119
|
+
* Failures bump the same `failedLoginAttempts` counter as `login` so an
|
|
120
|
+
* attacker who knows the password but not the TOTP gets `lockout.threshold`
|
|
121
|
+
* total tries across BOTH factors, not `2 * threshold`.
|
|
122
|
+
*/
|
|
123
|
+
verifyMfa(username: string, code: string, config?: TotpConfig): Promise<void>;
|
|
124
|
+
getPasswordHasher(): PasswordHasher;
|
|
125
|
+
getConfig(): Readonly<ResolvedConfig>;
|
|
126
|
+
/**
|
|
127
|
+
* Mint a freshly-signed trust record (does NOT persist — pair with
|
|
128
|
+
* `addTrustedDevice`). Throws when `deviceTrust.secret` is unset.
|
|
129
|
+
*/
|
|
130
|
+
issueTrustedDevice(userId: string, opts: {
|
|
131
|
+
ip?: string;
|
|
132
|
+
ttlMs: number;
|
|
133
|
+
name?: string;
|
|
134
|
+
}): TrustedDeviceRecord;
|
|
135
|
+
/**
|
|
136
|
+
* Append a trust record to the user's `trustedDevices` list. Read-modify-
|
|
137
|
+
* write — the array shape is preserved end-to-end so DB adapters with a
|
|
138
|
+
* merge strategy replace the whole array.
|
|
139
|
+
*/
|
|
140
|
+
addTrustedDevice(username: string, record: TrustedDeviceRecord): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Returns true when the supplied token (a) signs against the user+ip with
|
|
143
|
+
* the configured secret, AND (b) matches a persisted record that is still
|
|
144
|
+
* within its expiry window and whose bound IP (if any) matches.
|
|
145
|
+
*/
|
|
146
|
+
verifyTrustedDevice(username: string, token: string, ip?: string): Promise<boolean>;
|
|
147
|
+
/**
|
|
148
|
+
* Remove a specific trust record from the user. No-op when the record is
|
|
149
|
+
* absent — mirrors the legacy `DeviceTrustStore.revoke` semantics.
|
|
150
|
+
*/
|
|
151
|
+
revokeTrustedDevice(username: string, token: string): Promise<void>;
|
|
152
|
+
listTrustedDevices(username: string): Promise<TrustedDeviceRecord[]>;
|
|
153
|
+
private requireDeviceTrustSecret;
|
|
154
|
+
private applyPasswordChange;
|
|
155
|
+
private hasConfirmedMfaMethods;
|
|
156
|
+
/**
|
|
157
|
+
* If `account.locked`: auto-unlock when the lock has expired (mutating
|
|
158
|
+
* `account` in place), or throw `LOCKED` otherwise.
|
|
159
|
+
*/
|
|
160
|
+
private ensureNotLockedOrThrow;
|
|
161
|
+
/**
|
|
162
|
+
* Bump `failedLoginAttempts`, locking the account when threshold is hit,
|
|
163
|
+
* and always throw `errorCode` (with `details.lockEnds` when the lockout
|
|
164
|
+
* just tripped). Used by both `login` and `verifyMfa` so the two factors
|
|
165
|
+
* share one counter.
|
|
166
|
+
*/
|
|
167
|
+
private incrementAndMaybeLock;
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/store/memory.d.ts
|
|
171
|
+
declare class UserStoreMemory<T extends object = object> extends UserStore<T> {
|
|
172
|
+
private store;
|
|
173
|
+
constructor(seed?: Record<string, UserCredentials & T>);
|
|
174
|
+
exists(username: string): Promise<boolean>;
|
|
175
|
+
findByUsername(username: string): Promise<(UserCredentials & T) | null>;
|
|
176
|
+
create(data: UserCredentials & T): Promise<void>;
|
|
177
|
+
update(username: string, update: UserStoreUpdate): Promise<boolean>;
|
|
178
|
+
delete(username: string): Promise<boolean>;
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/password/policies.d.ts
|
|
182
|
+
declare const ppHasMinLength: (min?: number) => PasswordPolicyDef;
|
|
183
|
+
declare const ppHasUpperCase: (n?: number) => PasswordPolicyDef;
|
|
184
|
+
declare const ppHasLowerCase: (n?: number) => PasswordPolicyDef;
|
|
185
|
+
declare const ppHasNumber: (n?: number) => PasswordPolicyDef;
|
|
186
|
+
declare const ppHasSpecialChar: (n?: number) => PasswordPolicyDef;
|
|
187
|
+
declare const ppMaxRepeatedChars: (maxRepeated?: number) => PasswordPolicyDef;
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/mfa/totp.d.ts
|
|
190
|
+
declare function generateTotpSecret(bytes?: number): string;
|
|
191
|
+
declare function generateTotpUri(secret: string, issuer: string, account: string, config?: Pick<TotpConfig, "period" | "digits">): string;
|
|
192
|
+
declare function generateTotpCode(secret: string, config?: TotpConfig): string;
|
|
193
|
+
declare function verifyTotpCode(secret: string, code: string, config?: TotpConfig): boolean;
|
|
194
|
+
declare function generateMfaCode(length?: number): string;
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/mfa/codes.d.ts
|
|
197
|
+
/**
|
|
198
|
+
* SHA-256 hash of an MFA code (e.g. one-time email/SMS code or backup code).
|
|
199
|
+
*
|
|
200
|
+
* Hex-encoded for stable, comparable output regardless of input case/format.
|
|
201
|
+
* Use {@link verifyMfaCode} to compare a submitted plaintext code against the
|
|
202
|
+
* stored hash in constant time.
|
|
203
|
+
*/
|
|
204
|
+
declare function hashMfaCode(code: string): string;
|
|
205
|
+
/**
|
|
206
|
+
* Constant-time comparison of a submitted plaintext code against an
|
|
207
|
+
* expected SHA-256 hex hash (as produced by {@link hashMfaCode}).
|
|
208
|
+
*
|
|
209
|
+
* Returns false for malformed/empty expected hashes (timingSafeEqual
|
|
210
|
+
* requires equal-length, non-empty buffers).
|
|
211
|
+
*/
|
|
212
|
+
declare function verifyMfaCode(submitted: string, expectedHash: string): boolean;
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/mfa/backup-codes.d.ts
|
|
215
|
+
/**
|
|
216
|
+
* Generate `count` cryptographically-random backup codes (default 10).
|
|
217
|
+
*
|
|
218
|
+
* Format: 10 characters from the 31-char safe alphabet (uppercase letters +
|
|
219
|
+
* digits, omitting I/O/L/0/1), grouped as `XXXX-XXXX-XX`.
|
|
220
|
+
*
|
|
221
|
+
* Returns plaintext codes for the caller to deliver to the user — these
|
|
222
|
+
* should be hashed via {@link hashMfaCode} before persistence and never
|
|
223
|
+
* shown to the user again.
|
|
224
|
+
*/
|
|
225
|
+
declare function generateBackupCodePlaintext(count?: number): string[];
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/utils.d.ts
|
|
228
|
+
declare function maskEmail(email: string): string;
|
|
229
|
+
declare function maskPhone(phone: string): string;
|
|
230
|
+
declare function maskMfaValue(method: MfaMethod): string;
|
|
231
|
+
declare function setAtPath(obj: object, path: string, value: unknown): void;
|
|
232
|
+
//#endregion
|
|
233
|
+
export { type AccountData, type DeepPartial, type LockStatus, type LockoutConfig, type LoginResult, type MfaData, type MfaMethod, type MfaMethodInfo, type PasswordConfig, type PasswordData, PasswordHasher, PasswordPolicy, type PasswordPolicyContext, type PasswordPolicyDef, type PasswordPolicyEvalFn, type PasswordPolicyInstance, type PolicyCheckResult, type TotpConfig, type TransferablePolicy, type TrustedDeviceRecord, UserAuthError, type UserAuthErrorType, type UserCredentials, UserService, type UserServiceConfig, UserStore, UserStoreMemory, type UserStoreUpdate, generateBackupCodePlaintext, generateMfaCode, generateTotpCode, generateTotpSecret, generateTotpUri, hashMfaCode, maskEmail, maskMfaValue, maskPhone, normalizePolicies, ppHasLowerCase, ppHasMinLength, ppHasNumber, ppHasSpecialChar, ppHasUpperCase, ppMaxRepeatedChars, setAtPath, verifyMfaCode, verifyTotpCode };
|