@fonderie/auth 3.0.0 → 5.0.0
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/brain/signatures.md +21 -0
- package/dist/index.cjs +111 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -4
- package/dist/index.d.ts +25 -4
- package/dist/index.js +109 -2
- package/dist/index.js.map +1 -1
- package/dist/middlewares/index.d.cts +1 -1
- package/dist/middlewares/index.d.ts +1 -1
- package/dist/{session-BoJjRB7t.d.cts → session-DxsmQjXX.d.cts} +1 -0
- package/dist/{session-BoJjRB7t.d.ts → session-DxsmQjXX.d.ts} +1 -0
- package/package.json +5 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { IMfaChallenge, ISession, IUser } from './types.cjs';
|
|
2
|
-
import { IFonderieModule, IFonderieApp } from '@fonderie/core';
|
|
2
|
+
import { IFonderieModule, IReadinessProblem, IFonderieApp } from '@fonderie/core';
|
|
3
3
|
import { IStoreAdapter } from '@fonderie/store';
|
|
4
4
|
import { EventBus } from '@fonderie/events';
|
|
5
|
-
import { I as IAuthConfig } from './session-
|
|
6
|
-
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-
|
|
5
|
+
import { I as IAuthConfig } from './session-DxsmQjXX.cjs';
|
|
6
|
+
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-DxsmQjXX.cjs';
|
|
7
7
|
export { IUserDTO, toUserDTO } from './dtos/user.cjs';
|
|
8
8
|
export { requireAuth, validate } from '@fonderie/core/middlewares';
|
|
9
9
|
import { z } from 'zod';
|
|
@@ -15,6 +15,7 @@ declare class AuthModule implements IFonderieModule {
|
|
|
15
15
|
private bus?;
|
|
16
16
|
readonly name = "@fonderie/auth";
|
|
17
17
|
constructor(store: IStoreAdapter, config: IAuthConfig, bus?: EventBus | undefined);
|
|
18
|
+
checkReadiness(): IReadinessProblem[];
|
|
18
19
|
install(app: IFonderieApp): void;
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -99,4 +100,24 @@ declare namespace schemas {
|
|
|
99
100
|
declare function normalizeEmail(email: string): string;
|
|
100
101
|
declare function normalizeEmailSafe(email: string): string | null;
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
interface IImportUser {
|
|
104
|
+
id?: string;
|
|
105
|
+
email: string;
|
|
106
|
+
passwordHash?: string | null;
|
|
107
|
+
firstName?: string | null;
|
|
108
|
+
lastName?: string | null;
|
|
109
|
+
phone?: string | null;
|
|
110
|
+
profileImageUrl?: string | null;
|
|
111
|
+
locale?: string;
|
|
112
|
+
timezone?: string;
|
|
113
|
+
emailVerifiedAt?: Date | null;
|
|
114
|
+
mfaEnabled?: boolean;
|
|
115
|
+
createdAt?: Date;
|
|
116
|
+
}
|
|
117
|
+
declare function importUser(store: IStoreAdapter, user: IImportUser): Promise<{
|
|
118
|
+
id: string;
|
|
119
|
+
}>;
|
|
120
|
+
|
|
121
|
+
declare function validateAuthConfig(config: IAuthConfig): void;
|
|
122
|
+
|
|
123
|
+
export { AuthModule, type ChangePasswordInput, IAuthConfig, type IImportUser, type LoginInput, type RegisterInput, type ResetPasswordInput, importUser, normalizeEmail, normalizeEmailSafe, schemas, validateAuthConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { IMfaChallenge, ISession, IUser } from './types.js';
|
|
2
|
-
import { IFonderieModule, IFonderieApp } from '@fonderie/core';
|
|
2
|
+
import { IFonderieModule, IReadinessProblem, IFonderieApp } from '@fonderie/core';
|
|
3
3
|
import { IStoreAdapter } from '@fonderie/store';
|
|
4
4
|
import { EventBus } from '@fonderie/events';
|
|
5
|
-
import { I as IAuthConfig } from './session-
|
|
6
|
-
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-
|
|
5
|
+
import { I as IAuthConfig } from './session-DxsmQjXX.js';
|
|
6
|
+
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-DxsmQjXX.js';
|
|
7
7
|
export { IUserDTO, toUserDTO } from './dtos/user.js';
|
|
8
8
|
export { requireAuth, validate } from '@fonderie/core/middlewares';
|
|
9
9
|
import { z } from 'zod';
|
|
@@ -15,6 +15,7 @@ declare class AuthModule implements IFonderieModule {
|
|
|
15
15
|
private bus?;
|
|
16
16
|
readonly name = "@fonderie/auth";
|
|
17
17
|
constructor(store: IStoreAdapter, config: IAuthConfig, bus?: EventBus | undefined);
|
|
18
|
+
checkReadiness(): IReadinessProblem[];
|
|
18
19
|
install(app: IFonderieApp): void;
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -99,4 +100,24 @@ declare namespace schemas {
|
|
|
99
100
|
declare function normalizeEmail(email: string): string;
|
|
100
101
|
declare function normalizeEmailSafe(email: string): string | null;
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
interface IImportUser {
|
|
104
|
+
id?: string;
|
|
105
|
+
email: string;
|
|
106
|
+
passwordHash?: string | null;
|
|
107
|
+
firstName?: string | null;
|
|
108
|
+
lastName?: string | null;
|
|
109
|
+
phone?: string | null;
|
|
110
|
+
profileImageUrl?: string | null;
|
|
111
|
+
locale?: string;
|
|
112
|
+
timezone?: string;
|
|
113
|
+
emailVerifiedAt?: Date | null;
|
|
114
|
+
mfaEnabled?: boolean;
|
|
115
|
+
createdAt?: Date;
|
|
116
|
+
}
|
|
117
|
+
declare function importUser(store: IStoreAdapter, user: IImportUser): Promise<{
|
|
118
|
+
id: string;
|
|
119
|
+
}>;
|
|
120
|
+
|
|
121
|
+
declare function validateAuthConfig(config: IAuthConfig): void;
|
|
122
|
+
|
|
123
|
+
export { AuthModule, type ChangePasswordInput, IAuthConfig, type IImportUser, type LoginInput, type RegisterInput, type ResetPasswordInput, importUser, normalizeEmail, normalizeEmailSafe, schemas, validateAuthConfig };
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,8 @@ var __export = (target, all) => {
|
|
|
12
12
|
var password_exports = {};
|
|
13
13
|
__export(password_exports, {
|
|
14
14
|
hashPassword: () => hashPassword,
|
|
15
|
-
verifyPassword: () => verifyPassword
|
|
15
|
+
verifyPassword: () => verifyPassword,
|
|
16
|
+
verifyPasswordForLogin: () => verifyPasswordForLogin
|
|
16
17
|
});
|
|
17
18
|
import bcrypt from "bcryptjs";
|
|
18
19
|
async function hashPassword(plain) {
|
|
@@ -21,6 +22,19 @@ async function hashPassword(plain) {
|
|
|
21
22
|
async function verifyPassword(plain, hash) {
|
|
22
23
|
return bcrypt.compare(plain, hash);
|
|
23
24
|
}
|
|
25
|
+
async function verifyPasswordForLogin(plain, hash, legacyVerify) {
|
|
26
|
+
if (await verifyPassword(plain, hash)) return { valid: true, needsRehash: false };
|
|
27
|
+
if (legacyVerify) {
|
|
28
|
+
let legacyOk = false;
|
|
29
|
+
try {
|
|
30
|
+
legacyOk = await legacyVerify(plain, hash);
|
|
31
|
+
} catch {
|
|
32
|
+
legacyOk = false;
|
|
33
|
+
}
|
|
34
|
+
if (legacyOk) return { valid: true, needsRehash: true };
|
|
35
|
+
}
|
|
36
|
+
return { valid: false, needsRehash: false };
|
|
37
|
+
}
|
|
24
38
|
var ROUNDS;
|
|
25
39
|
var init_password = __esm({
|
|
26
40
|
"src/services/password.ts"() {
|
|
@@ -1205,10 +1219,17 @@ function authController(store, config, bus) {
|
|
|
1205
1219
|
if (!user || !user.passwordHash) {
|
|
1206
1220
|
return setApiResponse3(HTTP3.UNAUTHORIZED, "INVALID_CREDENTIALS", "Invalid credentials");
|
|
1207
1221
|
}
|
|
1208
|
-
const valid = await
|
|
1222
|
+
const { valid, needsRehash } = await verifyPasswordForLogin(
|
|
1223
|
+
password2,
|
|
1224
|
+
user.passwordHash,
|
|
1225
|
+
config.legacyVerify
|
|
1226
|
+
);
|
|
1209
1227
|
if (!valid) {
|
|
1210
1228
|
return setApiResponse3(HTTP3.UNAUTHORIZED, "INVALID_CREDENTIALS", "Invalid credentials");
|
|
1211
1229
|
}
|
|
1230
|
+
if (needsRehash) {
|
|
1231
|
+
await users.updatePassword(user.id, await hashPassword(password2));
|
|
1232
|
+
}
|
|
1212
1233
|
if (user.suspended) {
|
|
1213
1234
|
return setApiResponse3(
|
|
1214
1235
|
HTTP3.FORBIDDEN,
|
|
@@ -1963,6 +1984,53 @@ function buildAuthRoutes(store, config, bus) {
|
|
|
1963
1984
|
return routes;
|
|
1964
1985
|
}
|
|
1965
1986
|
|
|
1987
|
+
// src/services/config-guard.ts
|
|
1988
|
+
var MODULE = "@fonderie/auth";
|
|
1989
|
+
var PLACEHOLDER_SECRET = /dev-secret|test-secret|changeme|change-me|your[-_]secret|placeholder|example|insecure|min-32-chars/i;
|
|
1990
|
+
var MIN_SECRET_LENGTH = 32;
|
|
1991
|
+
function collectAuthConfigProblems(config) {
|
|
1992
|
+
const problems = [];
|
|
1993
|
+
const secret = config.jwtSecret ?? "";
|
|
1994
|
+
if (secret.length < MIN_SECRET_LENGTH) {
|
|
1995
|
+
problems.push({
|
|
1996
|
+
module: MODULE,
|
|
1997
|
+
severity: "error",
|
|
1998
|
+
message: `jwtSecret must be at least ${MIN_SECRET_LENGTH} characters (got ${secret.length})`
|
|
1999
|
+
});
|
|
2000
|
+
} else if (PLACEHOLDER_SECRET.test(secret)) {
|
|
2001
|
+
problems.push({
|
|
2002
|
+
module: MODULE,
|
|
2003
|
+
severity: "error",
|
|
2004
|
+
message: "jwtSecret looks like a placeholder or dev-default value"
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
if (config.secureCookies === false) {
|
|
2008
|
+
problems.push({
|
|
2009
|
+
module: MODULE,
|
|
2010
|
+
severity: "warning",
|
|
2011
|
+
message: "secureCookies is false \u2014 auth cookies may be sent over non-HTTPS connections in production"
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
return problems;
|
|
2015
|
+
}
|
|
2016
|
+
function validateAuthConfig(config) {
|
|
2017
|
+
const isProduction = process.env["NODE_ENV"] === "production";
|
|
2018
|
+
const problems = collectAuthConfigProblems(config);
|
|
2019
|
+
const errors = problems.filter((p) => p.severity === "error");
|
|
2020
|
+
if (isProduction && errors.length > 0) {
|
|
2021
|
+
throw new Error(
|
|
2022
|
+
`[auth] insecure config \u2014 ${errors.map((e) => e.message).join("; ")}. Set a long, random jwtSecret (e.g. \`openssl rand -base64 32\`). Refusing to boot in production.`
|
|
2023
|
+
);
|
|
2024
|
+
}
|
|
2025
|
+
if (isProduction) {
|
|
2026
|
+
for (const p of problems) console.warn(`[auth] ${p.message}`);
|
|
2027
|
+
} else {
|
|
2028
|
+
for (const p of errors) {
|
|
2029
|
+
console.warn(`[auth] ${p.message} (insecure \u2014 would refuse to boot in production)`);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
1966
2034
|
// src/middlewares/session.ts
|
|
1967
2035
|
function withSession(store, config) {
|
|
1968
2036
|
const users = new UserModel(store);
|
|
@@ -2010,11 +2078,16 @@ var AuthModule = class {
|
|
|
2010
2078
|
this.store = store;
|
|
2011
2079
|
this.config = config;
|
|
2012
2080
|
this.bus = bus;
|
|
2081
|
+
validateAuthConfig(config);
|
|
2013
2082
|
}
|
|
2014
2083
|
store;
|
|
2015
2084
|
config;
|
|
2016
2085
|
bus;
|
|
2017
2086
|
name = "@fonderie/auth";
|
|
2087
|
+
// Report config problems for app.checkProductionReadiness() (data, not throw).
|
|
2088
|
+
checkReadiness() {
|
|
2089
|
+
return collectAuthConfigProblems(this.config);
|
|
2090
|
+
}
|
|
2018
2091
|
install(app) {
|
|
2019
2092
|
app.use(withSession(this.store, this.config));
|
|
2020
2093
|
const routes = buildAuthRoutes(this.store, this.config, this.bus);
|
|
@@ -2026,18 +2099,52 @@ var AuthModule = class {
|
|
|
2026
2099
|
|
|
2027
2100
|
// src/middlewares/require-auth.ts
|
|
2028
2101
|
import { requireAuth as requireAuth2, requireAnyAuth as requireAnyAuth2 } from "@fonderie/core/middlewares";
|
|
2102
|
+
|
|
2103
|
+
// src/migrate.ts
|
|
2104
|
+
async function importUser(store, user) {
|
|
2105
|
+
const cols = [];
|
|
2106
|
+
const vals = [];
|
|
2107
|
+
const placeholders = [];
|
|
2108
|
+
const add = (col, val) => {
|
|
2109
|
+
cols.push(col);
|
|
2110
|
+
vals.push(val);
|
|
2111
|
+
placeholders.push(`$${vals.length}`);
|
|
2112
|
+
};
|
|
2113
|
+
if (user.id !== void 0) add("id", user.id);
|
|
2114
|
+
add("email", user.email.toLowerCase().trim());
|
|
2115
|
+
if (user.passwordHash !== void 0) add("password_hash", user.passwordHash);
|
|
2116
|
+
if (user.firstName !== void 0) add("first_name", user.firstName);
|
|
2117
|
+
if (user.lastName !== void 0) add("last_name", user.lastName);
|
|
2118
|
+
if (user.phone !== void 0) add("phone", user.phone);
|
|
2119
|
+
if (user.profileImageUrl !== void 0) add("profile_image_url", user.profileImageUrl);
|
|
2120
|
+
if (user.locale !== void 0) add("locale", user.locale);
|
|
2121
|
+
if (user.timezone !== void 0) add("timezone", user.timezone);
|
|
2122
|
+
if (user.emailVerifiedAt !== void 0) add("email_verified_at", user.emailVerifiedAt);
|
|
2123
|
+
if (user.mfaEnabled !== void 0) add("mfa_enabled", user.mfaEnabled);
|
|
2124
|
+
if (user.createdAt !== void 0) add("created_at", user.createdAt);
|
|
2125
|
+
const [row] = await store.query(
|
|
2126
|
+
`INSERT INTO fonderie_users (${cols.join(", ")})
|
|
2127
|
+
VALUES (${placeholders.join(", ")})
|
|
2128
|
+
RETURNING id`,
|
|
2129
|
+
vals
|
|
2130
|
+
);
|
|
2131
|
+
if (!row) throw new Error("[auth] importUser: insert returned no row");
|
|
2132
|
+
return row;
|
|
2133
|
+
}
|
|
2029
2134
|
export {
|
|
2030
2135
|
AUTH_CONFIG_KEYS,
|
|
2031
2136
|
AuthModule,
|
|
2032
2137
|
MESSAGE_KEYS,
|
|
2033
2138
|
buildAuthAccountLimiter,
|
|
2034
2139
|
buildAuthIpLimiter,
|
|
2140
|
+
importUser,
|
|
2035
2141
|
normalizeEmail,
|
|
2036
2142
|
normalizeEmailSafe,
|
|
2037
2143
|
requireAuth2 as requireAuth,
|
|
2038
2144
|
schemas_exports as schemas,
|
|
2039
2145
|
toUserDTO,
|
|
2040
2146
|
validate,
|
|
2147
|
+
validateAuthConfig,
|
|
2041
2148
|
withSession
|
|
2042
2149
|
};
|
|
2043
2150
|
//# sourceMappingURL=index.js.map
|