@maestro-js/password 1.0.0-alpha.2 → 1.0.0-alpha.21

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/dist/index.d.ts CHANGED
@@ -4,38 +4,62 @@ import { Hash } from '@maestro-js/hash';
4
4
 
5
5
  declare function create<Id, Credentials extends {
6
6
  password: string;
7
- }>(config: Password.Config<Id, Credentials>): Password.Service<Id, Credentials>;
8
- declare const Password: {
9
- create: typeof create;
7
+ }>(config: Password.Internal.Config<Id, Credentials>): Password.Internal.Service<Id, Credentials>;
8
+ declare function provider<Key extends Password.Provider.Key>(key: Key): Password.PasswordService<Key>;
9
+ /**
10
+ * Password verification and reset with pluggable credential lookups and secure token-based resets.
11
+ * Call methods directly on the default instance, or use `Password.Provider.create()` for named instances.
12
+ */
13
+ declare const PasswordBase: {
14
+ Provider: {
15
+ create: typeof create;
16
+ register: (name: Password.Provider.Key, item: Password.AnyPasswordService) => void;
17
+ };
18
+ provider: typeof provider;
10
19
  errors: {
11
20
  ThrottledError: CustomErrors.CustomErrorConstructor<undefined, {}, "tooManyRequests">;
12
21
  InvalidTokenError: CustomErrors.CustomErrorConstructor<undefined, {}, "unprocessableContent">;
13
22
  ExpiredTokenError: CustomErrors.CustomErrorConstructor<undefined, {}, "unprocessableContent">;
14
23
  };
15
24
  };
25
+ type KeysWithFallback = keyof Password.Provider.Keys extends never ? {
26
+ default: {
27
+ Id: unknown;
28
+ Credentials: {
29
+ password: string;
30
+ } & Record<string, unknown>;
31
+ };
32
+ } : Password.Provider.Keys;
33
+ declare const Password: Password.PasswordService<'default'> & typeof PasswordBase;
16
34
  declare namespace Password {
17
- interface Service<Id, Credentials extends {
18
- password: string;
19
- }> {
20
- verifyCredentials(credentials: Credentials): Promise<Password.Authenticatable<Id> | null>;
21
- createPasswordResetToken: (authenticatableId: Id) => Promise<string>;
22
- resetPassword: (options: {
23
- authenticatableId: Id;
24
- token: string;
25
- newPassword: string;
26
- }) => Promise<void>;
27
- }
28
- interface Config<Id, Credentials extends {
29
- password: string;
30
- }> {
31
- getAuthenticatableByCredentials(credentials: Omit<Credentials, 'password'>): Promise<(Password.Authenticatable<Id> & {
32
- passwordHash: string;
33
- }) | null>;
34
- updatePasswordHash(id: Id, passwordHash: string): Promise<void>;
35
- updateCredentialsInvalidBefore(id: Id, date: Iso.Instant): Promise<void>;
36
- /** Used for password hashing and verification. `Hash.drivers.bcrypt()` is recommended. */
37
- hash: Hash.HashService;
38
- passwordResetOptions?: Password.ResetOptions<Id>;
35
+ type PasswordService<ProviderKey extends Password.Provider.Key = 'default'> = Internal.Service<KeysWithFallback[ProviderKey]['Id'], KeysWithFallback[ProviderKey]['Credentials']>;
36
+ type AnyPasswordService = Internal.Service<any, any>;
37
+ type PasswordServiceConfig<ProviderKey extends Password.Provider.Key = 'default'> = Internal.Config<KeysWithFallback[ProviderKey]['Id'], KeysWithFallback[ProviderKey]['Credentials']>;
38
+ namespace Internal {
39
+ interface Service<Id, Credentials extends {
40
+ password: string;
41
+ }> {
42
+ verifyCredentials(credentials: Credentials): Promise<Password.Authenticatable<Id> | null>;
43
+ createPasswordResetToken: (authenticatableId: Id, options: {
44
+ expirationMinutes: number;
45
+ }) => Promise<string>;
46
+ resetPassword: (options: {
47
+ token: string;
48
+ newPassword: string;
49
+ }) => Promise<Id>;
50
+ }
51
+ interface Config<Id, Credentials extends {
52
+ password: string;
53
+ }> {
54
+ getAuthenticatableByCredentials(credentials: Omit<Credentials, 'password'>): Promise<(Password.Authenticatable<Id> & {
55
+ passwordHash: string;
56
+ }) | null>;
57
+ updatePasswordHash(id: Id, passwordHash: string): Promise<void>;
58
+ updateCredentialsInvalidBefore(id: Id, date: Iso.Instant): Promise<void>;
59
+ /** Used for password hashing and verification. `Hash.drivers.bcrypt()` is recommended. */
60
+ hash: Hash.HashService;
61
+ passwordResetOptions?: Password.ResetOptions<Id>;
62
+ }
39
63
  }
40
64
  interface Authenticatable<Id> {
41
65
  id: Id;
@@ -53,11 +77,14 @@ declare namespace Password {
53
77
  getBySelector(selector: string): Promise<Password.ResetTokenRecord<Id> | null>;
54
78
  getByAuthenticatableId(authenticatableId: Id): Promise<Password.ResetTokenRecord<Id> | null>;
55
79
  deleteByAuthenticatableId(authenticatableId: Id): Promise<void>;
56
- /** Token lifetime in minutes. Default: 60 */
57
- tokenExpirationMinutes?: number;
58
80
  /** Minimum seconds between createToken calls for the same authenticatable. Default: 60 */
59
81
  throttleSeconds?: number;
60
82
  }
83
+ namespace Provider {
84
+ type Key = keyof KeysWithFallback;
85
+ interface Keys {
86
+ }
87
+ }
61
88
  }
62
89
 
63
90
  export { Password };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/index.ts
2
2
  import crypto from "crypto";
3
3
  import { CustomErrors } from "@maestro-js/custom-errors";
4
+ import { ServiceRegistry } from "@maestro-js/service-registry";
4
5
  import { instantFns } from "iso-fns2";
5
6
  function create(config) {
6
7
  const hashService = config.hash;
@@ -23,7 +24,7 @@ function create(config) {
23
24
  }
24
25
  return { id: authenticatable.id, credentialsInvalidBefore: authenticatable.credentialsInvalidBefore };
25
26
  }
26
- async function createPasswordResetToken(authenticatableId) {
27
+ async function createPasswordResetToken(authenticatableId, options) {
27
28
  if (!passwordResetOptions) {
28
29
  throw new Error("Password reset is not configured. Provide `passwordResetOptions` when creating the password service.");
29
30
  }
@@ -38,7 +39,7 @@ function create(config) {
38
39
  const verifier = crypto.randomBytes(32).toString("base64url");
39
40
  const verifierHash = await config.hash.hash(verifier);
40
41
  const now = instantFns.now();
41
- const expirationDate = instantFns.add(now, { minutes: passwordResetOptions.tokenExpirationMinutes ?? 60 });
42
+ const expirationDate = instantFns.add(now, { minutes: options.expirationMinutes });
42
43
  await passwordResetOptions.create({
43
44
  authenticatableId,
44
45
  selector,
@@ -48,11 +49,7 @@ function create(config) {
48
49
  });
49
50
  return `${selector}|${verifier}`;
50
51
  }
51
- async function resetPassword({
52
- authenticatableId,
53
- token,
54
- newPassword
55
- }) {
52
+ async function resetPassword({ token, newPassword }) {
56
53
  if (!passwordResetOptions) {
57
54
  throw new Error("Password reset is not configured. Provide `passwordResetOptions` when creating the password service.");
58
55
  }
@@ -71,13 +68,11 @@ function create(config) {
71
68
  if (record.expirationDate < instantFns.now()) {
72
69
  throw new PasswordResetExpiredTokenError();
73
70
  }
74
- if (record.authenticatableId !== authenticatableId) {
75
- throw new PasswordResetInvalidTokenError();
76
- }
77
71
  const passwordHash = await config.hash.hash(newPassword);
78
- await config.updatePasswordHash(authenticatableId, passwordHash);
79
- await config.updateCredentialsInvalidBefore(authenticatableId, instantFns.now());
80
- await passwordResetOptions.deleteByAuthenticatableId(authenticatableId);
72
+ await config.updatePasswordHash(record.authenticatableId, passwordHash);
73
+ await config.updateCredentialsInvalidBefore(record.authenticatableId, instantFns.now());
74
+ await passwordResetOptions.deleteByAuthenticatableId(record.authenticatableId);
75
+ return record.authenticatableId;
81
76
  }
82
77
  return {
83
78
  verifyCredentials,
@@ -85,6 +80,21 @@ function create(config) {
85
80
  resetPassword
86
81
  };
87
82
  }
83
+ var registry = ServiceRegistry.createRegistry(
84
+ ServiceRegistry.proxyFunctionsForObject
85
+ );
86
+ var Provider = {
87
+ create,
88
+ register: registry.register
89
+ };
90
+ function provider(key) {
91
+ const service = registry.resolve(key);
92
+ return {
93
+ verifyCredentials: service.verifyCredentials,
94
+ createPasswordResetToken: service.createPasswordResetToken,
95
+ resetPassword: service.resetPassword
96
+ };
97
+ }
88
98
  var PasswordResetThrottledError = CustomErrors.create({
89
99
  name: "PasswordResetThrottledError",
90
100
  message: "Password reset requests are being throttled. Please try again later.",
@@ -100,14 +110,19 @@ var PasswordResetExpiredTokenError = CustomErrors.create({
100
110
  message: "The password reset token has expired.",
101
111
  httpStatusCode: "unprocessableContent"
102
112
  });
103
- var Password = {
104
- create,
113
+ var PasswordBase = {
114
+ Provider,
115
+ provider,
105
116
  errors: {
106
117
  ThrottledError: PasswordResetThrottledError,
107
118
  InvalidTokenError: PasswordResetInvalidTokenError,
108
119
  ExpiredTokenError: PasswordResetExpiredTokenError
109
120
  }
110
121
  };
122
+ var Password = {
123
+ ...PasswordBase,
124
+ ...provider("default")
125
+ };
111
126
  function decodePasswordResetToken(token) {
112
127
  const [selector, verifier] = token.split("|");
113
128
  if (!selector || !verifier) return null;
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "default": "./dist/index.js"
8
8
  }
9
9
  },
10
- "version": "1.0.0-alpha.2",
10
+ "version": "1.0.0-alpha.21",
11
11
  "publishConfig": {
12
12
  "access": "restricted"
13
13
  },
@@ -16,11 +16,15 @@
16
16
  ],
17
17
  "dependencies": {
18
18
  "iso-fns2": "npm:iso-fns@2.0.0-alpha.26",
19
- "@maestro-js/custom-errors": "1.0.0-alpha.2",
20
- "@maestro-js/hash": "1.0.0-alpha.2"
19
+ "@maestro-js/custom-errors": "1.0.0-alpha.21",
20
+ "@maestro-js/service-registry": "1.0.0-alpha.21"
21
+ },
22
+ "peerDependencies": {
23
+ "@maestro-js/hash": "1.0.0-alpha.21"
21
24
  },
22
25
  "devDependencies": {
23
- "@types/node": "^22.19.11"
26
+ "@types/node": "^22.19.11",
27
+ "@maestro-js/hash": "1.0.0-alpha.21"
24
28
  },
25
29
  "license": "UNLICENSED",
26
30
  "engines": {