@absolutejs/auth 0.30.0-beta.0 → 0.30.0-beta.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.
|
@@ -26,6 +26,8 @@ export type CredentialsConfig<UserType> = {
|
|
|
26
26
|
headers: Record<string, string | undefined>;
|
|
27
27
|
ip?: string;
|
|
28
28
|
}) => boolean | Promise<boolean>;
|
|
29
|
+
passwordVerifier?: (plainPassword: string, storedHash: string) => Promise<boolean>;
|
|
30
|
+
rehashOnLogin?: boolean;
|
|
29
31
|
loginRoute?: RouteString;
|
|
30
32
|
onCreateCredentialUser: (identity: CredentialIdentity & Record<string, unknown>) => Promise<Response | StatusReturn | UserType> | Response | StatusReturn | UserType;
|
|
31
33
|
onCredentialsLoginError?: (context: {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CredentialRecord, CredentialStore } from './types';
|
|
2
|
+
export type ImportableUser<UserType> = {
|
|
3
|
+
passwordHash?: string;
|
|
4
|
+
user: UserType;
|
|
5
|
+
};
|
|
6
|
+
export type ImportUserResult<UserType> = {
|
|
7
|
+
error: string;
|
|
8
|
+
ok: false;
|
|
9
|
+
user: UserType;
|
|
10
|
+
} | {
|
|
11
|
+
credential?: CredentialRecord;
|
|
12
|
+
ok: true;
|
|
13
|
+
user: UserType;
|
|
14
|
+
};
|
|
15
|
+
export type ImportUsersOptions<UserType> = {
|
|
16
|
+
onCreateUser: (input: ImportableUser<UserType>) => Promise<{
|
|
17
|
+
email: string;
|
|
18
|
+
emailVerified?: boolean;
|
|
19
|
+
userId?: string;
|
|
20
|
+
} | null>;
|
|
21
|
+
concurrency?: number;
|
|
22
|
+
credentialStore: CredentialStore;
|
|
23
|
+
};
|
|
24
|
+
export declare const importUser: <UserType>(input: ImportableUser<UserType>, options: ImportUsersOptions<UserType>) => Promise<ImportUserResult<UserType>>;
|
|
25
|
+
export declare const importUsers: <UserType>(inputs: readonly ImportableUser<UserType>[], options: ImportUsersOptions<UserType>) => Promise<{
|
|
26
|
+
failed: number;
|
|
27
|
+
results: ImportUserResult<UserType>[];
|
|
28
|
+
succeeded: number;
|
|
29
|
+
}>;
|
|
30
|
+
export declare const rehashCredentialPassword: ({ credentialStore, current, plainPassword }: {
|
|
31
|
+
credentialStore: CredentialStore;
|
|
32
|
+
current: CredentialRecord;
|
|
33
|
+
plainPassword: string;
|
|
34
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const isLegacyHash: (storedHash: string) => boolean;
|
|
2
|
+
export declare const verifyAuth0Pbkdf2: (plainPassword: string, wrappedHash: string) => Promise<boolean>;
|
|
3
|
+
export declare const verifyCognitoSha256: (plainPassword: string, wrappedHash: string) => Promise<boolean>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
2
|
import { type CredentialRouteProps } from './config';
|
|
3
|
-
export declare const credentialsLogin: <UserType>({ authSessionStore, checkBreachesOnLogin, credentialStore, getUserByEmail, isMfaRequired, lockoutGuard, loginRoute, onCredentialsLoginError, onCredentialsLoginSuccess, requireEmailVerification, sessionDurationMs }: CredentialRouteProps<UserType>) => Elysia<"", {
|
|
3
|
+
export declare const credentialsLogin: <UserType>({ authSessionStore, checkBreachesOnLogin, credentialStore, getUserByEmail, isMfaRequired, lockoutGuard, loginRoute, onCredentialsLoginError, onCredentialsLoginSuccess, passwordVerifier, rehashOnLogin, requireEmailVerification, sessionDurationMs }: CredentialRouteProps<UserType>) => Elysia<"", {
|
|
4
4
|
decorator: {};
|
|
5
5
|
store: {
|
|
6
6
|
session: import("..").SessionRecord<UserType>;
|
package/dist/index.d.ts
CHANGED
|
@@ -15050,6 +15050,9 @@ export * from './tenancy';
|
|
|
15050
15050
|
export * from './credentials/config';
|
|
15051
15051
|
export * from './credentials/passwordPolicy';
|
|
15052
15052
|
export * from './credentials/emailValidation';
|
|
15053
|
+
export { importUser, importUsers, rehashCredentialPassword } from './credentials/import';
|
|
15054
|
+
export type { ImportUserResult, ImportUsersOptions, ImportableUser } from './credentials/import';
|
|
15055
|
+
export { isLegacyHash, verifyAuth0Pbkdf2, verifyCognitoSha256 } from './credentials/legacyHashers';
|
|
15053
15056
|
export * from './credentials/types';
|
|
15054
15057
|
export { credentialRoutes } from './credentials/routes';
|
|
15055
15058
|
export { credentialsEmailVerification } from './credentials/emailVerification';
|
package/dist/index.js
CHANGED
|
@@ -3392,6 +3392,110 @@ var credentialsEmailVerification = ({
|
|
|
3392
3392
|
// src/credentials/login.ts
|
|
3393
3393
|
import { Elysia as Elysia6, t as t6 } from "elysia";
|
|
3394
3394
|
|
|
3395
|
+
// src/credentials/import.ts
|
|
3396
|
+
var normalizeEmail = (email) => email.trim().toLowerCase();
|
|
3397
|
+
var buildCredential = (email, emailVerified, passwordHash, userId) => ({
|
|
3398
|
+
createdAt: Date.now(),
|
|
3399
|
+
email,
|
|
3400
|
+
emailVerified,
|
|
3401
|
+
passwordHash,
|
|
3402
|
+
status: "active",
|
|
3403
|
+
updatedAt: Date.now(),
|
|
3404
|
+
userId
|
|
3405
|
+
});
|
|
3406
|
+
var importUser = async (input, options) => {
|
|
3407
|
+
try {
|
|
3408
|
+
const created = await options.onCreateUser(input);
|
|
3409
|
+
if (created === null)
|
|
3410
|
+
return {
|
|
3411
|
+
error: "onCreateUser returned null",
|
|
3412
|
+
ok: false,
|
|
3413
|
+
user: input.user
|
|
3414
|
+
};
|
|
3415
|
+
const email = normalizeEmail(created.email);
|
|
3416
|
+
const emailVerified = created.emailVerified ?? true;
|
|
3417
|
+
if (input.passwordHash === undefined) {
|
|
3418
|
+
return { ok: true, user: input.user };
|
|
3419
|
+
}
|
|
3420
|
+
const credential = buildCredential(email, emailVerified, input.passwordHash, created.userId);
|
|
3421
|
+
await options.credentialStore.saveCredential(credential);
|
|
3422
|
+
return { credential, ok: true, user: input.user };
|
|
3423
|
+
} catch (err) {
|
|
3424
|
+
return {
|
|
3425
|
+
error: err instanceof Error ? err.message : String(err),
|
|
3426
|
+
ok: false,
|
|
3427
|
+
user: input.user
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
};
|
|
3431
|
+
var importUsers = async (inputs, options) => {
|
|
3432
|
+
const concurrency = Math.max(1, options.concurrency ?? 1);
|
|
3433
|
+
const results = [];
|
|
3434
|
+
for (let cursor = 0;cursor < inputs.length; cursor += concurrency) {
|
|
3435
|
+
const slice = inputs.slice(cursor, cursor + concurrency);
|
|
3436
|
+
const batch = await Promise.all(slice.map((input) => importUser(input, options)));
|
|
3437
|
+
results.push(...batch);
|
|
3438
|
+
}
|
|
3439
|
+
return {
|
|
3440
|
+
failed: results.filter((result) => !result.ok).length,
|
|
3441
|
+
results,
|
|
3442
|
+
succeeded: results.filter((result) => result.ok).length
|
|
3443
|
+
};
|
|
3444
|
+
};
|
|
3445
|
+
var rehashCredentialPassword = async ({
|
|
3446
|
+
credentialStore,
|
|
3447
|
+
current,
|
|
3448
|
+
plainPassword
|
|
3449
|
+
}) => {
|
|
3450
|
+
const passwordHash = await hashPassword(plainPassword);
|
|
3451
|
+
await credentialStore.saveCredential({
|
|
3452
|
+
...current,
|
|
3453
|
+
passwordHash,
|
|
3454
|
+
updatedAt: Date.now()
|
|
3455
|
+
});
|
|
3456
|
+
};
|
|
3457
|
+
|
|
3458
|
+
// src/credentials/legacyHashers.ts
|
|
3459
|
+
var PBKDF2_DEFAULT_ITERATIONS = 1e5;
|
|
3460
|
+
var PBKDF2_KEY_LENGTH_BYTES = 32;
|
|
3461
|
+
var constantTimeEqualBytes = (left, right) => {
|
|
3462
|
+
if (left.byteLength !== right.byteLength)
|
|
3463
|
+
return false;
|
|
3464
|
+
let diff = 0;
|
|
3465
|
+
for (let index = 0;index < left.byteLength; index++) {
|
|
3466
|
+
diff |= (left[index] ?? 0) ^ (right[index] ?? 0);
|
|
3467
|
+
}
|
|
3468
|
+
return diff === 0;
|
|
3469
|
+
};
|
|
3470
|
+
var base64Decode = (encoded) => new Uint8Array(Buffer.from(encoded, "base64"));
|
|
3471
|
+
var sha256Bytes = async (input) => new Uint8Array(await crypto.subtle.digest("SHA-256", input));
|
|
3472
|
+
var isLegacyHash = (storedHash) => !storedHash.startsWith("$argon2id$") && !storedHash.startsWith("$2");
|
|
3473
|
+
var verifyAuth0Pbkdf2 = async (plainPassword, wrappedHash) => {
|
|
3474
|
+
const parts = wrappedHash.split(":");
|
|
3475
|
+
if (parts.length !== 4 || parts[0] !== "auth0_pbkdf2")
|
|
3476
|
+
return false;
|
|
3477
|
+
const salt = base64Decode(parts[1] ?? "");
|
|
3478
|
+
const expected = base64Decode(parts[2] ?? "");
|
|
3479
|
+
const iterations = Number(parts[3] ?? PBKDF2_DEFAULT_ITERATIONS);
|
|
3480
|
+
if (Number.isNaN(iterations) || iterations <= 0)
|
|
3481
|
+
return false;
|
|
3482
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(plainPassword), { name: "PBKDF2" }, false, ["deriveBits"]);
|
|
3483
|
+
const derivedBits = await crypto.subtle.deriveBits({ hash: "SHA-256", iterations, name: "PBKDF2", salt }, key, PBKDF2_KEY_LENGTH_BYTES * 8);
|
|
3484
|
+
return constantTimeEqualBytes(new Uint8Array(derivedBits), expected);
|
|
3485
|
+
};
|
|
3486
|
+
var verifyCognitoSha256 = async (plainPassword, wrappedHash) => {
|
|
3487
|
+
const parts = wrappedHash.split(":");
|
|
3488
|
+
if (parts.length !== 3 || parts[0] !== "cognito_sha256")
|
|
3489
|
+
return false;
|
|
3490
|
+
const salt = base64Decode(parts[1] ?? "");
|
|
3491
|
+
const expected = base64Decode(parts[2] ?? "");
|
|
3492
|
+
const payload = new Uint8Array(salt.byteLength + plainPassword.length);
|
|
3493
|
+
payload.set(salt, 0);
|
|
3494
|
+
payload.set(new TextEncoder().encode(plainPassword), salt.byteLength);
|
|
3495
|
+
const derived = await sha256Bytes(payload);
|
|
3496
|
+
return constantTimeEqualBytes(derived, expected);
|
|
3497
|
+
};
|
|
3498
|
+
|
|
3395
3499
|
// src/credentials/passwordPolicy.ts
|
|
3396
3500
|
var DEFAULT_MIN_LENGTH = 12;
|
|
3397
3501
|
var HEX_RADIX = 16;
|
|
@@ -3452,6 +3556,8 @@ var credentialsLogin = ({
|
|
|
3452
3556
|
loginRoute = "/auth/login",
|
|
3453
3557
|
onCredentialsLoginError,
|
|
3454
3558
|
onCredentialsLoginSuccess,
|
|
3559
|
+
passwordVerifier,
|
|
3560
|
+
rehashOnLogin = false,
|
|
3455
3561
|
requireEmailVerification = false,
|
|
3456
3562
|
sessionDurationMs = DEFAULT_CREDENTIAL_SESSION_TTL_MS
|
|
3457
3563
|
}) => new Elysia6().use(sessionStore()).post(loginRoute, async ({
|
|
@@ -3479,7 +3585,8 @@ var credentialsLogin = ({
|
|
|
3479
3585
|
}
|
|
3480
3586
|
const credential = await credentialStore.getCredentialByEmail(normalizedEmail);
|
|
3481
3587
|
const user = await getUserByEmail(normalizedEmail);
|
|
3482
|
-
const
|
|
3588
|
+
const verifyHash = passwordVerifier ?? verifyPassword;
|
|
3589
|
+
const passwordValid = credential === undefined ? false : await verifyHash(password, credential.passwordHash);
|
|
3483
3590
|
if (!credential || !user || credential.status !== "active" || !passwordValid) {
|
|
3484
3591
|
await lockoutGuard?.recordFailure(normalizedEmail);
|
|
3485
3592
|
await onCredentialsLoginError?.({
|
|
@@ -3489,6 +3596,13 @@ var credentialsLogin = ({
|
|
|
3489
3596
|
return status("Unauthorized", "Invalid email or password");
|
|
3490
3597
|
}
|
|
3491
3598
|
await lockoutGuard?.recordSuccess(normalizedEmail);
|
|
3599
|
+
if (rehashOnLogin && isLegacyHash(credential.passwordHash)) {
|
|
3600
|
+
await rehashCredentialPassword({
|
|
3601
|
+
credentialStore,
|
|
3602
|
+
current: credential,
|
|
3603
|
+
plainPassword: password
|
|
3604
|
+
});
|
|
3605
|
+
}
|
|
3492
3606
|
if (requireEmailVerification && !credential.emailVerified) {
|
|
3493
3607
|
return status("Forbidden", { status: "email_not_verified" });
|
|
3494
3608
|
}
|
|
@@ -22997,7 +23111,9 @@ export {
|
|
|
22997
23111
|
verifyHcaptcha,
|
|
22998
23112
|
verifyDpopProof,
|
|
22999
23113
|
verifyDpopNonce,
|
|
23114
|
+
verifyCognitoSha256,
|
|
23000
23115
|
verifyClientAssertion,
|
|
23116
|
+
verifyAuth0Pbkdf2,
|
|
23001
23117
|
verifyAuditChain,
|
|
23002
23118
|
verifyApiKey,
|
|
23003
23119
|
verifyAccessToken,
|
|
@@ -23044,6 +23160,7 @@ export {
|
|
|
23044
23160
|
resolveAuthHtmxRenderers,
|
|
23045
23161
|
resolveApiPrincipal,
|
|
23046
23162
|
removeFromSessionRing,
|
|
23163
|
+
rehashCredentialPassword,
|
|
23047
23164
|
registerClient,
|
|
23048
23165
|
refreshableProviderOptions,
|
|
23049
23166
|
recordLoginAttempt,
|
|
@@ -23104,6 +23221,7 @@ export {
|
|
|
23104
23221
|
isPKCEProviderOption,
|
|
23105
23222
|
isOIDCProviderOption,
|
|
23106
23223
|
isMfaEnrolled,
|
|
23224
|
+
isLegacyHash,
|
|
23107
23225
|
isImpersonating,
|
|
23108
23226
|
isDisposableEmail,
|
|
23109
23227
|
isAuthIntent,
|
|
@@ -23111,6 +23229,8 @@ export {
|
|
|
23111
23229
|
inviteToOrganization,
|
|
23112
23230
|
introspectToken,
|
|
23113
23231
|
instantiateUserSession,
|
|
23232
|
+
importUsers,
|
|
23233
|
+
importUser,
|
|
23114
23234
|
hashToken,
|
|
23115
23235
|
hashPassword,
|
|
23116
23236
|
hashAuditEvent,
|
|
@@ -23330,5 +23450,5 @@ export {
|
|
|
23330
23450
|
AuthIdentityConflictError
|
|
23331
23451
|
};
|
|
23332
23452
|
|
|
23333
|
-
//# debugId=
|
|
23453
|
+
//# debugId=658AE7172110F24564756E2164756E21
|
|
23334
23454
|
//# sourceMappingURL=index.js.map
|