@vunexa/lixa 0.1.6-alpha.11 → 0.1.6-alpha.13

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 CHANGED
@@ -17,7 +17,7 @@ A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library
17
17
  - **Multi-SSO Account Linking**: Seamlessly merge accounts sharing the same verified email address under a single unified user session.
18
18
  - **Post-Login Resource Connection API**: Connect third-party API providers (GitHub Repositories, Google Drive, Slack) post-authentication and manage resource tokens on the user session.
19
19
  - **Multi-provider OAuth/OIDC support** with unified API.
20
- - **Pre-built Database Storage Adapters**: Official Prisma and Drizzle ORM adapters via `@vunexa/lixa-adapters` with support for PostgreSQL, MySQL, and SQLite.
20
+ - **Official Extensions Package**: Built-in OAuth Providers (Google, GitHub), Database Storage (Prisma, Drizzle), and HTTP Frameworks (Express) via `@vunexa/lixa-extensions`.
21
21
  - **Unified session management** via `SessionHandler` (generation + storage).
22
22
  - **Unified state management** via `StateHandler` (PKCE + CSRF protection).
23
23
  - **Automatic PKCE** (Proof Key for Code Exchange) for all OAuth flows.
@@ -34,9 +34,9 @@ A flexible, provider-agnostic OAuth 2.0 and OpenID Connect (OIDC) client library
34
34
  ## Installation
35
35
 
36
36
  ```bash
37
- npm install @vunexa/lixa @vunexa/lixa-providers
37
+ npm install @vunexa/lixa @vunexa/lixa-extensions
38
38
  # or
39
- yarn add @vunexa/lixa @vunexa/lixa-providers
39
+ yarn add @vunexa/lixa @vunexa/lixa-extensions
40
40
  ```
41
41
 
42
42
  ---
@@ -47,7 +47,7 @@ yarn add @vunexa/lixa @vunexa/lixa-providers
47
47
 
48
48
  ```typescript
49
49
  import { Lixa, AccountLinkingStrategy } from "@vunexa/lixa";
50
- import { GoogleProvider, GithubProvider } from "@vunexa/lixa-providers";
50
+ import { GoogleProvider, GithubProvider } from "@vunexa/lixa-extensions/providers";
51
51
 
52
52
  export const lixa = new Lixa({
53
53
  // Configure Multi-SSO Account Linking Strategy
@@ -0,0 +1,65 @@
1
+ import type { CredentialsConfig, CredentialsStorage, IPasswordHasher, UserCredentials, SignUpParams, VerifyCredentialsParams, ChangePasswordParams } from "./types";
2
+ /**
3
+ * Coordinates user registration, authentication, password verification, policy enforcement,
4
+ * and timing attack protection.
5
+ *
6
+ * @public
7
+ */
8
+ export declare class CredentialsManager {
9
+ private readonly storage;
10
+ private readonly hasher;
11
+ private readonly policy;
12
+ private readonly identifierType;
13
+ private readonly requireUsername;
14
+ private readonly requireEmail;
15
+ private readonly timingAttackProtection;
16
+ private dummyHash;
17
+ constructor(config?: CredentialsConfig);
18
+ private initDummyHash;
19
+ /**
20
+ * Normalizes an identifier string.
21
+ */
22
+ private normalizeIdentifier;
23
+ /**
24
+ * Validates identifier format based on configured identifierType.
25
+ */
26
+ private validateIdentifierFormat;
27
+ /**
28
+ * Registers a new user with password hashing and policy enforcement.
29
+ *
30
+ * @param params - Registration parameters
31
+ * @returns Created user credentials without password hash
32
+ */
33
+ signUp(params: SignUpParams): Promise<Omit<UserCredentials, "passwordHash">>;
34
+ /**
35
+ * Verifies credentials against storage with timing attack protection.
36
+ *
37
+ * @param params - Verification parameters
38
+ * @returns User credentials without password hash, or null if verification fails
39
+ */
40
+ verifyCredentials(params: VerifyCredentialsParams): Promise<Omit<UserCredentials, "passwordHash"> | null>;
41
+ /**
42
+ * Changes a user's password with old password verification and new password policy enforcement.
43
+ *
44
+ * @param params - Change password parameters
45
+ * @returns True if password was successfully updated
46
+ */
47
+ changePassword(params: ChangePasswordParams): Promise<boolean>;
48
+ /**
49
+ * Finds a user by ID and returns safe user data.
50
+ */
51
+ getUserById(id: string): Promise<Omit<UserCredentials, "passwordHash"> | null>;
52
+ /**
53
+ * Finds a user by identifier and returns safe user data.
54
+ */
55
+ getUserByIdentifier(identifier: string): Promise<Omit<UserCredentials, "passwordHash"> | null>;
56
+ /**
57
+ * Gets the underlying storage instance.
58
+ */
59
+ getStorage(): CredentialsStorage;
60
+ /**
61
+ * Gets the underlying hasher instance.
62
+ */
63
+ getHasher(): IPasswordHasher;
64
+ }
65
+ //# sourceMappingURL=credentials-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials-manager.d.ts","sourceRoot":"","sources":["../../src/credentials/credentials-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EAEf,eAAe,EACf,YAAY,EACZ,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAWjB;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgC;IAC/D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;IACvC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAU;IACjD,OAAO,CAAC,SAAS,CAAc;gBAEnB,MAAM,GAAE,iBAAsB;YAqB5B,aAAa;IAI3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAchC;;;;;OAKG;IACU,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;IAsDzF;;;;;OAKG;IACU,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC;IAiCtH;;;;;OAKG;IACU,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IAqC3E;;OAEG;IACU,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC;IAO3F;;OAEG;IACU,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC;IAO3G;;OAEG;IACI,UAAU,IAAI,kBAAkB;IAIvC;;OAEG;IACI,SAAS,IAAI,eAAe;CAGpC"}
@@ -0,0 +1,88 @@
1
+ import type { IPasswordHasher } from "./types";
2
+ /**
3
+ * Options for Scrypt password hashing.
4
+ *
5
+ * @public
6
+ */
7
+ export interface ScryptHasherOptions {
8
+ /** CPU/memory cost parameter (must be power of 2). Default: 16384 (2^14) */
9
+ cost?: number;
10
+ /** Block size parameter. Default: 8 */
11
+ blockSize?: number;
12
+ /** Parallelization parameter. Default: 1 */
13
+ parallelization?: number;
14
+ /** Salt length in bytes. Default: 16 */
15
+ saltLength?: number;
16
+ /** Derived key length in bytes. Default: 64 */
17
+ keyLength?: number;
18
+ /** Max memory allocated in bytes. Default: 32MB */
19
+ maxmem?: number;
20
+ }
21
+ /**
22
+ * Default secure password hasher utilizing Node.js native `crypto.scrypt`.
23
+ *
24
+ * @remarks
25
+ * Produces PHC-formatted strings: `$scrypt$N=16384,r=8,p=1$<saltHex>$<keyHex>`.
26
+ * Verification uses constant-time `crypto.timingSafeEqual` to prevent timing attacks.
27
+ *
28
+ * @public
29
+ */
30
+ export declare class ScryptPasswordHasher implements IPasswordHasher {
31
+ private readonly cost;
32
+ private readonly blockSize;
33
+ private readonly parallelization;
34
+ private readonly saltLength;
35
+ private readonly keyLength;
36
+ private readonly maxmem;
37
+ constructor(options?: ScryptHasherOptions);
38
+ /**
39
+ * Hashes a plaintext password using Scrypt.
40
+ *
41
+ * @param password - Plaintext password
42
+ * @returns Formatted Scrypt hash string
43
+ */
44
+ hash(password: string): Promise<string>;
45
+ /**
46
+ * Verifies a password against a stored Scrypt hash.
47
+ *
48
+ * @param password - Plaintext password
49
+ * @param hash - Formatted Scrypt hash string
50
+ * @returns True if password matches hash
51
+ */
52
+ verify(password: string, hash: string): Promise<boolean>;
53
+ private deriveKey;
54
+ }
55
+ /**
56
+ * Options for PBKDF2 password hashing.
57
+ *
58
+ * @public
59
+ */
60
+ export interface Pbkdf2HasherOptions {
61
+ /** Iteration count. Default: 100000 */
62
+ iterations?: number;
63
+ /** HMAC digest algorithm. Default: 'sha512' */
64
+ digest?: string;
65
+ /** Salt length in bytes. Default: 16 */
66
+ saltLength?: number;
67
+ /** Derived key length in bytes. Default: 64 */
68
+ keyLength?: number;
69
+ }
70
+ /**
71
+ * Alternative password hasher using Node.js native `crypto.pbkdf2`.
72
+ *
73
+ * @remarks
74
+ * Produces PHC-formatted strings: `$pbkdf2$i=100000,d=sha512$<saltHex>$<keyHex>`.
75
+ *
76
+ * @public
77
+ */
78
+ export declare class Pbkdf2PasswordHasher implements IPasswordHasher {
79
+ private readonly iterations;
80
+ private readonly digest;
81
+ private readonly saltLength;
82
+ private readonly keyLength;
83
+ constructor(options?: Pbkdf2HasherOptions);
84
+ hash(password: string): Promise<string>;
85
+ verify(password: string, hash: string): Promise<boolean>;
86
+ private deriveKey;
87
+ }
88
+ //# sourceMappingURL=hasher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hasher.d.ts","sourceRoot":"","sources":["../../src/credentials/hasher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,GAAE,mBAAwB;IAS7C;;;;;OAKG;IACU,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAcpD;;;;;;OAMG;IACU,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgDrE,OAAO,CAAC,SAAS;CA6BlB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,GAAE,mBAAwB;IAOhC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAavC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA8CrE,OAAO,CAAC,SAAS;CAiBlB"}
@@ -0,0 +1,6 @@
1
+ export * from "./types";
2
+ export * from "./hasher";
3
+ export * from "./policy";
4
+ export * from "./local-storage";
5
+ export * from "./credentials-manager";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { CredentialsStorage, UserCredentials } from "./types";
2
+ /**
3
+ * In-memory credentials storage for development, testing, and isolated environments.
4
+ *
5
+ * @remarks
6
+ * Uses instance-isolated Maps to prevent state leakage across tests or Lixa instances.
7
+ *
8
+ * @public
9
+ */
10
+ export declare class LocalCredentialsStorage implements CredentialsStorage {
11
+ private usersById;
12
+ private identifierToId;
13
+ saveUser(user: UserCredentials): Promise<void>;
14
+ findUserByIdentifier(identifier: string): Promise<UserCredentials | null>;
15
+ findUserById(id: string): Promise<UserCredentials | null>;
16
+ updatePassword(id: string, newPasswordHash: string): Promise<void>;
17
+ deleteUser(id: string): Promise<void>;
18
+ clear(): void;
19
+ }
20
+ //# sourceMappingURL=local-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-storage.d.ts","sourceRoot":"","sources":["../../src/credentials/local-storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEnE;;;;;;;GAOG;AACH,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE,OAAO,CAAC,SAAS,CAA2C;IAC5D,OAAO,CAAC,cAAc,CAAkC;IAE3C,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9C,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAUzE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAKzD,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASlE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU3C,KAAK,IAAI,IAAI;CAIrB"}
@@ -0,0 +1,12 @@
1
+ import type { PasswordPolicyConfig, PasswordPolicyResult } from "./types";
2
+ /**
3
+ * Validates a plaintext password against configured password policy rules.
4
+ *
5
+ * @param password - Plaintext password to evaluate
6
+ * @param policy - Optional custom policy configuration
7
+ * @returns PasswordPolicyResult containing boolean valid flag and error messages
8
+ *
9
+ * @public
10
+ */
11
+ export declare function validatePasswordPolicy(password: string, policy?: PasswordPolicyConfig): Promise<PasswordPolicyResult>;
12
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/credentials/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE1E;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,oBAAyB,GAChC,OAAO,CAAC,oBAAoB,CAAC,CAsD/B"}
@@ -0,0 +1,314 @@
1
+ import type { Session } from "../models/session";
2
+ /**
3
+ * User credentials entity representing stored account login information.
4
+ *
5
+ * @public
6
+ */
7
+ export interface UserCredentials {
8
+ /** Unique user identifier (UUID or Nanoid) */
9
+ id: string;
10
+ /** Primary unique lookup identifier (normalized lowercase username or email) */
11
+ identifier: string;
12
+ /** Username of the account */
13
+ username?: string | undefined;
14
+ /** Associated email address */
15
+ email?: string | undefined;
16
+ /** Securely hashed password string (e.g. Scrypt, Argon2, PBKDF2, Bcrypt) */
17
+ passwordHash: string;
18
+ /** Unix timestamp in milliseconds when user credentials were created */
19
+ createdAt: number;
20
+ /** Unix timestamp in milliseconds when user credentials were last updated */
21
+ updatedAt: number;
22
+ /** Optional custom user metadata or profile attributes */
23
+ metadata?: Record<string, unknown> | undefined;
24
+ }
25
+ /**
26
+ * Storage interface for managing user credentials.
27
+ *
28
+ * @remarks
29
+ * Implement this interface to persist credentials in custom databases
30
+ * (e.g. PostgreSQL, MySQL, SQLite, MongoDB, DynamoDB) or use provided adapters
31
+ * (PrismaCredentialsStorage, DrizzleCredentialsStorage).
32
+ *
33
+ * @public
34
+ */
35
+ export interface CredentialsStorage {
36
+ /**
37
+ * Persists a new user credential record.
38
+ *
39
+ * @param user - User credentials entity to save
40
+ */
41
+ saveUser(user: UserCredentials): Promise<void>;
42
+ /**
43
+ * Retrieves user credentials by normalized identifier (username or email).
44
+ *
45
+ * @param identifier - Normalized identifier string
46
+ * @returns UserCredentials record or null if not found
47
+ */
48
+ findUserByIdentifier(identifier: string): Promise<UserCredentials | null>;
49
+ /**
50
+ * Retrieves user credentials by unique user ID.
51
+ *
52
+ * @param id - Unique user ID
53
+ * @returns UserCredentials record or null if not found
54
+ */
55
+ findUserById(id: string): Promise<UserCredentials | null>;
56
+ /**
57
+ * Updates the password hash for a user by user ID.
58
+ *
59
+ * @param id - Unique user ID
60
+ * @param newPasswordHash - Newly computed password hash
61
+ */
62
+ updatePassword(id: string, newPasswordHash: string): Promise<void>;
63
+ /**
64
+ * Optional helper to delete a user by user ID.
65
+ *
66
+ * @param id - Unique user ID
67
+ */
68
+ deleteUser?(id: string): Promise<void>;
69
+ /**
70
+ * Optional helper to find user directly by username.
71
+ *
72
+ * @param username - Username string
73
+ */
74
+ findUserByUsername?(username: string): Promise<UserCredentials | null>;
75
+ }
76
+ /**
77
+ * Interface for password hashing and verification algorithms.
78
+ *
79
+ * @remarks
80
+ * Lixa defaults to ScryptPasswordHasher (Node.js crypto.scrypt, 0 dependencies).
81
+ * Implement this interface to use custom algorithms (e.g. bcrypt, argon2, pbkdf2).
82
+ *
83
+ * @public
84
+ */
85
+ export interface IPasswordHasher {
86
+ /**
87
+ * Computes a secure cryptographic hash for a plaintext password.
88
+ *
89
+ * @param password - Plaintext password
90
+ * @returns Formatted hash string containing salt and algorithm parameters
91
+ */
92
+ hash(password: string): Promise<string>;
93
+ /**
94
+ * Verifies a plaintext password against a stored hash string.
95
+ * Must use constant-time comparison to prevent timing attacks.
96
+ *
97
+ * @param password - Plaintext password to verify
98
+ * @param hash - Stored hash string
99
+ * @returns True if password matches hash, false otherwise
100
+ */
101
+ verify(password: string, hash: string): Promise<boolean>;
102
+ }
103
+ /**
104
+ * Configuration options for password validation rules.
105
+ *
106
+ * @public
107
+ */
108
+ export interface PasswordPolicyConfig {
109
+ /**
110
+ * Minimum password length in characters.
111
+ *
112
+ * @default 8
113
+ */
114
+ minLength?: number | undefined;
115
+ /**
116
+ * Maximum password length in characters to prevent hashing DoS attacks.
117
+ *
118
+ * @default 128
119
+ */
120
+ maxLength?: number | undefined;
121
+ /**
122
+ * Require at least one uppercase letter [A-Z].
123
+ *
124
+ * @default false
125
+ */
126
+ requireUppercase?: boolean | undefined;
127
+ /**
128
+ * Require at least one lowercase letter [a-z].
129
+ *
130
+ * @default false
131
+ */
132
+ requireLowercase?: boolean | undefined;
133
+ /**
134
+ * Require at least one numeric digit [0-9].
135
+ *
136
+ * @default false
137
+ */
138
+ requireNumbers?: boolean | undefined;
139
+ /**
140
+ * Require at least one special symbol character.
141
+ *
142
+ * @default false
143
+ */
144
+ requireSpecialChars?: boolean | undefined;
145
+ /**
146
+ * Optional custom validator function for enterprise or custom rules.
147
+ * Return `true` if valid, or `false` / custom error message string if invalid.
148
+ */
149
+ customValidator?: ((password: string) => boolean | string | Promise<boolean | string>) | undefined;
150
+ }
151
+ /**
152
+ * Result of password policy validation.
153
+ *
154
+ * @public
155
+ */
156
+ export interface PasswordPolicyResult {
157
+ /** Whether the password satisfied all configured rules */
158
+ valid: boolean;
159
+ /** List of human-readable error descriptions if validation failed */
160
+ errors: string[];
161
+ }
162
+ /**
163
+ * Configuration options for Credentials authentication in Lixa.
164
+ *
165
+ * @public
166
+ */
167
+ export interface CredentialsConfig {
168
+ /**
169
+ * Explicitly enable or disable credentials authentication.
170
+ *
171
+ * @default true
172
+ */
173
+ enabled?: boolean | undefined;
174
+ /**
175
+ * Custom storage implementation for persisting user credentials.
176
+ * Defaults to LocalCredentialsStorage (in-memory, suitable for dev & testing).
177
+ */
178
+ storage?: CredentialsStorage | undefined;
179
+ /**
180
+ * Custom password hashing strategy.
181
+ * Defaults to ScryptPasswordHasher (Node.js crypto.scrypt).
182
+ */
183
+ hasher?: IPasswordHasher | undefined;
184
+ /**
185
+ * Password strength policy configuration.
186
+ */
187
+ policy?: PasswordPolicyConfig | undefined;
188
+ /**
189
+ * Supported identifier types for registration and sign-in:
190
+ * - 'email': Identifier must be a valid email format
191
+ * - 'username': Identifier can be any username string
192
+ * - 'both': Automatically detect email or username
193
+ *
194
+ * @default 'both'
195
+ */
196
+ identifierType?: ("email" | "username" | "both") | undefined;
197
+ /**
198
+ * Whether a username is strictly required during registration (signUp).
199
+ *
200
+ * @default false
201
+ */
202
+ requireUsername?: boolean | undefined;
203
+ /**
204
+ * Whether a valid email address is strictly required during registration (signUp).
205
+ *
206
+ * @default false
207
+ */
208
+ requireEmail?: boolean | undefined;
209
+ /**
210
+ * Whether to perform dummy hash verification on unknown users to mitigate timing attacks.
211
+ *
212
+ * @default true
213
+ */
214
+ timingAttackProtection?: boolean | undefined;
215
+ /**
216
+ * Whether to automatically generate and save a session upon successful registration (signUp).
217
+ *
218
+ * @default true
219
+ */
220
+ autoCreateSessionOnSignUp?: boolean | undefined;
221
+ /**
222
+ * Session expiration time in seconds for credentials-generated sessions.
223
+ *
224
+ * @default 86400 (24 hours)
225
+ */
226
+ sessionTtlSeconds?: number | undefined;
227
+ }
228
+ /**
229
+ * Parameters for user registration (signUp).
230
+ *
231
+ * @public
232
+ */
233
+ export interface SignUpParams {
234
+ /** Username for the user (can be used directly instead of identifier) */
235
+ username?: string | undefined;
236
+ /** Primary identifier (username or email) */
237
+ identifier?: string | undefined;
238
+ /** Plaintext password */
239
+ password: string;
240
+ /** Optional explicit email address */
241
+ email?: string | undefined;
242
+ /** Optional custom user metadata */
243
+ metadata?: Record<string, unknown> | undefined;
244
+ }
245
+ /**
246
+ * Result of user registration (signUp).
247
+ *
248
+ * @public
249
+ */
250
+ export interface SignUpResult<TSession = Session> {
251
+ /** Created user credentials (with passwordHash omitted for safety) */
252
+ user: Omit<UserCredentials, "passwordHash">;
253
+ /** Created session ID if autoCreateSessionOnSignUp is enabled */
254
+ sessionId?: string | undefined;
255
+ /** Created session object if autoCreateSessionOnSignUp is enabled */
256
+ session?: TSession | undefined;
257
+ }
258
+ /**
259
+ * Parameters for user authentication (signIn).
260
+ *
261
+ * @public
262
+ */
263
+ export interface SignInParams {
264
+ /** Username or email of the account */
265
+ username?: string | undefined;
266
+ /** Primary identifier (username or email) */
267
+ identifier?: string | undefined;
268
+ /** Plaintext password */
269
+ password: string;
270
+ }
271
+ /**
272
+ * Result of user authentication (signIn).
273
+ *
274
+ * @public
275
+ */
276
+ export interface SignInResult<TSession = Session> {
277
+ /** Authenticated user credentials (with passwordHash omitted for safety) */
278
+ user: Omit<UserCredentials, "passwordHash">;
279
+ /** Active session ID */
280
+ sessionId: string;
281
+ /** Active session object */
282
+ session: TSession;
283
+ }
284
+ /**
285
+ * Parameters for raw credential verification without session creation.
286
+ *
287
+ * @public
288
+ */
289
+ export interface VerifyCredentialsParams {
290
+ /** Username or email of the account */
291
+ username?: string | undefined;
292
+ /** Primary identifier (username or email) */
293
+ identifier?: string | undefined;
294
+ /** Plaintext password */
295
+ password: string;
296
+ }
297
+ /**
298
+ * Parameters for changing a user password.
299
+ *
300
+ * @public
301
+ */
302
+ export interface ChangePasswordParams {
303
+ /** Username (optional if identifier or userId is provided) */
304
+ username?: string | undefined;
305
+ /** Primary identifier (optional if username or userId is provided) */
306
+ identifier?: string | undefined;
307
+ /** Unique user ID (optional if identifier or username is provided) */
308
+ userId?: string | undefined;
309
+ /** Current plaintext password */
310
+ oldPassword: string;
311
+ /** New plaintext password */
312
+ newPassword: string;
313
+ }
314
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/credentials/types.ts"],"names":[],"mappings":"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"}
package/dist/errors.d.ts CHANGED
@@ -87,4 +87,46 @@ export declare class AccountUnlinkError extends LixaError {
87
87
  export declare class RefreshTokenError extends LixaError {
88
88
  constructor(message: string, details?: Record<string, unknown>);
89
89
  }
90
+ /**
91
+ * Thrown when user credentials (username/email and password) are invalid during authentication.
92
+ *
93
+ * @public
94
+ */
95
+ export declare class InvalidCredentialsError extends LixaError {
96
+ constructor(message?: string, details?: Record<string, unknown>);
97
+ }
98
+ /**
99
+ * Thrown when attempting to register a user with an identifier that already exists.
100
+ *
101
+ * @public
102
+ */
103
+ export declare class UserAlreadyExistsError extends LixaError {
104
+ constructor(identifier: string, details?: Record<string, unknown>);
105
+ }
106
+ /**
107
+ * Thrown when a user account cannot be found for credential verification or password change.
108
+ *
109
+ * @public
110
+ */
111
+ export declare class UserNotFoundError extends LixaError {
112
+ constructor(identifierOrId?: string, details?: Record<string, unknown>);
113
+ }
114
+ /**
115
+ * Thrown when a password does not satisfy configured password policy rules.
116
+ *
117
+ * @public
118
+ */
119
+ export declare class WeakPasswordError extends LixaError {
120
+ readonly validationErrors: string[];
121
+ constructor(validationErrors?: string[], details?: Record<string, unknown>);
122
+ }
123
+ /**
124
+ * Thrown when credentials operations (signUp, signIn, etc.) are invoked on a Lixa instance
125
+ * where credentials authentication is not enabled.
126
+ *
127
+ * @public
128
+ */
129
+ export declare class CredentialsNotConfiguredError extends LixaError {
130
+ constructor(message?: string, details?: Record<string, unknown>);
131
+ }
90
132
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC;;OAEG;IACH,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,SAAgB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;gBAEjD,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAqB,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAS5F;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;gBAClC,OAAO,GAAE,MAAmC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG5F;AAED;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,SAAS;gBAC3C,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAShE;AAED;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,SAAS;gBAC3C,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG/D;AAED;;;;GAIG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;gBAC1C,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG/D;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,SAAgB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;gBAE/B,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAIhF;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,SAAS;gBACrC,OAAO,GAAE,MAAkD,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG3G;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;gBACtC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAO9D;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG/D;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG/D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}