ideal-auth 1.3.2 → 1.3.3
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 +2 -0
- package/dist/auth-instance.js +19 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,6 +163,8 @@ const session = auth();
|
|
|
163
163
|
await session.attempt({ email, password }); // password verified internally
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
+
> **Timing note:** `attempt()` runs `hash.verify()` against a cached dummy hash when the user is not found, so the user-exists and user-missing paths take roughly the same time. For full protection against user enumeration, ensure your `resolveUserByCredentials` lookup is not catastrophically slower for non-existent users (e.g. use an indexed column).
|
|
167
|
+
|
|
166
168
|
**Manual (escape hatch):** Provide `attemptUser` for full control over lookup and verification. Takes precedence over the Laravel-style config if both are provided.
|
|
167
169
|
|
|
168
170
|
```typescript
|
package/dist/auth-instance.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { seal, unseal } from './session/seal';
|
|
2
2
|
import { buildCookieOptions } from './session/cookie';
|
|
3
|
+
// Equalize attempt() timing between user-found and user-missing paths by always
|
|
4
|
+
// running hash.verify(). The dummy hash is cached per HashInstance so repeated
|
|
5
|
+
// misses match the cost of a real verify.
|
|
6
|
+
const dummyHashCache = new WeakMap();
|
|
7
|
+
function getDummyHash(hash) {
|
|
8
|
+
let p = dummyHashCache.get(hash);
|
|
9
|
+
if (!p) {
|
|
10
|
+
p = hash.make('__ideal-auth-dummy__');
|
|
11
|
+
dummyHashCache.set(hash, p);
|
|
12
|
+
}
|
|
13
|
+
return p;
|
|
14
|
+
}
|
|
3
15
|
export function createAuthInstance(deps) {
|
|
4
16
|
let cachedPayload;
|
|
5
17
|
let cachedUser;
|
|
@@ -112,12 +124,14 @@ export function createAuthInstance(deps) {
|
|
|
112
124
|
if (deps.hash && deps.resolveUserByCredentials) {
|
|
113
125
|
const { [deps.credentialKey]: password, ...lookup } = credentials;
|
|
114
126
|
const dbUser = await deps.resolveUserByCredentials(lookup);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
127
|
+
// Run verify even on miss against a dummy hash — prevents user enumeration via timing
|
|
128
|
+
const storedHash = dbUser
|
|
129
|
+
? dbUser[deps.passwordField]
|
|
130
|
+
: undefined;
|
|
131
|
+
const hashToCheck = storedHash ?? (await getDummyHash(deps.hash));
|
|
132
|
+
const ok = await deps.hash.verify(password, hashToCheck);
|
|
133
|
+
if (!dbUser || !storedHash || !ok)
|
|
119
134
|
return false;
|
|
120
|
-
}
|
|
121
135
|
await writeSession(dbUser, options);
|
|
122
136
|
return true;
|
|
123
137
|
}
|