@bevel-software/platform-core-backend 0.1.0 → 0.2.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.
Files changed (68) hide show
  1. package/dist/core/create-core-server.d.ts.map +1 -1
  2. package/dist/core/create-core-server.js +13 -0
  3. package/dist/core/create-core-server.js.map +1 -1
  4. package/dist/core/create-core-services.d.ts.map +1 -1
  5. package/dist/core/create-core-services.js +35 -5
  6. package/dist/core/create-core-services.js.map +1 -1
  7. package/dist/core-config.d.ts +45 -1
  8. package/dist/core-config.d.ts.map +1 -1
  9. package/dist/core-config.js +66 -2
  10. package/dist/core-config.js.map +1 -1
  11. package/dist/modules/admin/admin-access.service.d.ts +17 -1
  12. package/dist/modules/admin/admin-access.service.d.ts.map +1 -1
  13. package/dist/modules/admin/admin-access.service.js +14 -1
  14. package/dist/modules/admin/admin-access.service.js.map +1 -1
  15. package/dist/modules/auth/account.routes.d.ts +17 -0
  16. package/dist/modules/auth/account.routes.d.ts.map +1 -0
  17. package/dist/modules/auth/account.routes.js +47 -0
  18. package/dist/modules/auth/account.routes.js.map +1 -0
  19. package/dist/modules/auth/auth.routes.d.ts +13 -7
  20. package/dist/modules/auth/auth.routes.d.ts.map +1 -1
  21. package/dist/modules/auth/auth.routes.js +51 -3
  22. package/dist/modules/auth/auth.routes.js.map +1 -1
  23. package/dist/modules/auth/auth.service.d.ts +44 -8
  24. package/dist/modules/auth/auth.service.d.ts.map +1 -1
  25. package/dist/modules/auth/auth.service.js +129 -32
  26. package/dist/modules/auth/auth.service.js.map +1 -1
  27. package/dist/modules/auth/oidc-auth-provider.d.ts +51 -0
  28. package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -0
  29. package/dist/modules/auth/oidc-auth-provider.js +189 -0
  30. package/dist/modules/auth/oidc-auth-provider.js.map +1 -0
  31. package/dist/modules/auth/password-hash.d.ts +12 -0
  32. package/dist/modules/auth/password-hash.d.ts.map +1 -0
  33. package/dist/modules/auth/password-hash.js +67 -0
  34. package/dist/modules/auth/password-hash.js.map +1 -0
  35. package/dist/modules/auth/rate-limit.d.ts +32 -0
  36. package/dist/modules/auth/rate-limit.d.ts.map +1 -0
  37. package/dist/modules/auth/rate-limit.js +49 -0
  38. package/dist/modules/auth/rate-limit.js.map +1 -0
  39. package/dist/modules/database/core-schema.d.ts +17 -0
  40. package/dist/modules/database/core-schema.d.ts.map +1 -1
  41. package/dist/modules/database/core-schema.js +6 -0
  42. package/dist/modules/database/core-schema.js.map +1 -1
  43. package/dist/modules/tool-auth/internal-token.service.d.ts +10 -0
  44. package/dist/modules/tool-auth/internal-token.service.d.ts.map +1 -1
  45. package/dist/modules/tool-auth/internal-token.service.js +12 -0
  46. package/dist/modules/tool-auth/internal-token.service.js.map +1 -1
  47. package/migrations/0001_user_password_hash.sql +4 -0
  48. package/migrations/meta/_journal.json +20 -13
  49. package/package.json +2 -2
  50. package/src/core/create-core-server.ts +352 -338
  51. package/src/core/create-core-services.ts +542 -501
  52. package/src/core-config.ts +65 -2
  53. package/src/modules/admin/admin-access.service.ts +10 -0
  54. package/src/modules/auth/__tests__/account.routes.test.ts +98 -0
  55. package/src/modules/auth/__tests__/auth.routes.test.ts +91 -0
  56. package/src/modules/auth/__tests__/auth.service.test.ts +217 -0
  57. package/src/modules/auth/__tests__/oidc-auth-provider.test.ts +174 -0
  58. package/src/modules/auth/__tests__/password-hash.test.ts +53 -0
  59. package/src/modules/auth/__tests__/rate-limit.test.ts +36 -0
  60. package/src/modules/auth/account.routes.ts +59 -0
  61. package/src/modules/auth/auth.routes.ts +68 -10
  62. package/src/modules/auth/auth.service.ts +151 -32
  63. package/src/modules/auth/oidc-auth-provider.ts +229 -0
  64. package/src/modules/auth/password-hash.ts +80 -0
  65. package/src/modules/auth/rate-limit.ts +45 -0
  66. package/src/modules/database/core-schema.ts +437 -431
  67. package/src/modules/tool-auth/__tests__/internal-token.service.test.ts +25 -1
  68. package/src/modules/tool-auth/internal-token.service.ts +13 -0
@@ -1 +1 @@
1
- {"version":3,"file":"auth.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAmB,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACrE;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,OAAO,CAAC,cAAc,EACtC,SAAS,GAAE,kBAAkB,EAAO,EACpC,oBAAoB,UAAO,GAC1B,OAAO,CAAC,MAAM,CAgEhB"}
1
+ {"version":3,"file":"auth.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAmB,CAAC;AAEtD;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACrE;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,OAAO,CAAC,cAAc,EACtC,SAAS,GAAE,kBAAkB,EAAO,EACpC,oBAAoB,UAAO,GAC1B,OAAO,CAAC,MAAM,CAmHhB"}
@@ -1,5 +1,6 @@
1
1
  import express from 'express';
2
2
  import { AUTH_COOKIE_NAME } from './auth.middleware.js'; // also imports Express Request augmentation
3
+ import { FixedWindowRateLimiter } from './rate-limit.js';
3
4
  /**
4
5
  * Max age of the JWT cookie (seconds). Matches the JWT's own `expiresIn:
5
6
  * '7d'` so the cookie expires when the token does — browsers stop
@@ -9,7 +10,19 @@ import { AUTH_COOKIE_NAME } from './auth.middleware.js'; // also imports Express
9
10
  export const AUTH_COOKIE_MAX_AGE_S = 7 * 24 * 60 * 60;
10
11
  export function createAuthRoutes(authService, authMiddleware, providers = [], passwordLoginEnabled = true) {
11
12
  const router = express.Router();
12
- // POST /api/auth/login — email + shared-password login (unprotected)
13
+ // Brute-force guards on the password endpoints (scrypt also makes each
14
+ // attempt expensive server-side, so these double as DoS protection):
15
+ // - login: a tight per-(ip, email) budget plus a wider per-ip budget so a
16
+ // single address can't spray many emails; the pair key resets on a
17
+ // successful login.
18
+ // - change-password: per authenticated account (a stolen session must not
19
+ // get unlimited guesses at the current password).
20
+ const loginPairLimiter = new FixedWindowRateLimiter(10, 15 * 60_000);
21
+ const loginIpLimiter = new FixedWindowRateLimiter(50, 15 * 60_000);
22
+ const changePasswordLimiter = new FixedWindowRateLimiter(10, 15 * 60_000);
23
+ const clientIp = (req) => req.ip ?? req.socket.remoteAddress ?? 'unknown';
24
+ // POST /api/auth/login — email + password login (unprotected): the env
25
+ // bootstrap admin or a per-user account password (see AuthService).
13
26
  router.post('/auth/login', async (req, res) => {
14
27
  if (!passwordLoginEnabled) {
15
28
  res.status(403).json({ error: 'Password login is disabled' });
@@ -20,8 +33,18 @@ export function createAuthRoutes(authService, authMiddleware, providers = [], pa
20
33
  res.status(400).json({ error: 'email and password are required' });
21
34
  return;
22
35
  }
36
+ const ip = clientIp(req);
37
+ const pairKey = `${ip}|${email.trim().toLowerCase()}`;
38
+ if (!loginPairLimiter.consume(pairKey) || !loginIpLimiter.consume(ip)) {
39
+ res.status(429).json({ error: 'Too many attempts. Try again later.' });
40
+ return;
41
+ }
23
42
  try {
24
43
  const result = await authService.loginWithPassword(email, password);
44
+ // A real login clears the pair budget — a user who fumbled a few
45
+ // attempts isn't rate-limited on their next visit. (The wider per-ip
46
+ // window intentionally keeps counting.)
47
+ loginPairLimiter.reset(pairKey);
25
48
  // Also set the JWT as an HttpOnly cookie so transports that can't
26
49
  // carry headers (EventSource for SSE, image tags) authenticate
27
50
  // automatically. JSON callers continue to use the Bearer header
@@ -43,15 +66,40 @@ export function createAuthRoutes(authService, authMiddleware, providers = [], pa
43
66
  }
44
67
  });
45
68
  // GET /api/auth/providers — unprotected capability probe so the login screen
46
- // knows which login methods to render. SSO providers appear under their key.
69
+ // knows which login methods to render: the password form, plus one button
70
+ // per SSO provider (label + the path that starts its flow).
47
71
  router.get('/auth/providers', (_req, res) => {
48
72
  res.json({
49
73
  password: passwordLoginEnabled,
50
- ...Object.fromEntries(providers.map((p) => [p.key, true])),
74
+ sso: providers.map((p) => ({ key: p.key, label: p.label, startPath: p.startPath })),
51
75
  });
52
76
  });
53
77
  for (const provider of providers)
54
78
  provider.mountRoutes(router, authService);
79
+ // POST /api/auth/change-password — self-service (protected). Requires the
80
+ // current password whenever one is set; an SSO-only account sets its first
81
+ // password without one.
82
+ router.post('/auth/change-password', authMiddleware, async (req, res) => {
83
+ const { currentPassword, newPassword } = req.body;
84
+ if (!newPassword) {
85
+ res.status(400).json({ error: 'newPassword is required' });
86
+ return;
87
+ }
88
+ if (!changePasswordLimiter.consume(req.userId)) {
89
+ res.status(429).json({ error: 'Too many attempts. Try again later.' });
90
+ return;
91
+ }
92
+ try {
93
+ await authService.changePassword(req.userId, currentPassword, newPassword);
94
+ res.json({ ok: true });
95
+ }
96
+ catch (error) {
97
+ const msg = error instanceof Error ? error.message : 'Unknown error';
98
+ // Policy violations and a wrong current password are caller errors, not
99
+ // server faults — and the message is safe to show verbatim.
100
+ res.status(400).json({ error: msg });
101
+ }
102
+ });
55
103
  // GET /api/auth/me — validate stored JWT, return user info (protected)
56
104
  router.get('/auth/me', authMiddleware, async (req, res) => {
57
105
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"auth.routes.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.routes.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.routes.ts"],"names":[],"mappings":"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"}
@@ -5,22 +5,59 @@ export declare class AuthService {
5
5
  private readonly db;
6
6
  private readonly config;
7
7
  constructor(db: Database, config: CoreConfig);
8
+ /**
9
+ * Password login. Two credential sources, in order:
10
+ *
11
+ * 1. The env bootstrap admin (`ADMIN_EMAIL` + `ADMIN_PASSWORD`), checked
12
+ * directly against the environment — never stored, so unsetting either
13
+ * variable disables it immediately. Lets a fresh deployment sign in
14
+ * before any account exists.
15
+ * 2. A per-user account: the `users` row's scrypt `password_hash`
16
+ * (created by an admin from Roles & Members, or set by the user on
17
+ * their Account page). Accounts that only ever signed in via SSO have
18
+ * no hash and are refused here.
19
+ *
20
+ * A generic "Invalid credentials" error for every failure mode — never
21
+ * reveal whether the email exists or has a password.
22
+ */
8
23
  loginWithPassword(email: string, password: string): Promise<{
9
24
  token: string;
10
25
  user: AuthUser;
11
26
  }>;
12
27
  /**
13
- * Establish a session for a user who authenticated via "Sign in with
14
- * Microsoft". Same upsert-by-email + JWT path as password login — the only
15
- * difference is identity came from a verified Microsoft id_token instead of
16
- * the shared password. Email is the idempotency key, so a user who first
17
- * used password login and later signs in with Microsoft (same email) keeps
18
- * the same account/id.
28
+ * Establish a session for a user whose identity an SSO provider (the
29
+ * generic OIDC plugin, or the enterprise "Sign in with Microsoft") already
30
+ * verified. Same upsert-by-email + JWT path as password login. Email is the
31
+ * idempotency key, so a user who first used password login and later signs
32
+ * in via SSO (same email) keeps the same account/id.
19
33
  */
20
- loginWithMicrosoft(email: string, name: string): Promise<{
34
+ loginWithSso(email: string, name: string): Promise<{
21
35
  token: string;
22
36
  user: AuthUser;
23
37
  }>;
38
+ /**
39
+ * Admin-driven account provisioning (Roles & Members → Accounts). Upserts
40
+ * the user by email and sets their password — re-provisioning an existing
41
+ * account (e.g. one that first arrived via SSO, or a reset for a locked-out
42
+ * user) is deliberate admin behavior, not an error.
43
+ */
44
+ createAccount(email: string, name: string | undefined, password: string): Promise<AuthUser>;
45
+ /**
46
+ * Self-service password change (the Account page). The current password is
47
+ * required whenever one is set; an SSO-only account (no hash yet) may set
48
+ * its first password without one. The env bootstrap-admin credential is not
49
+ * affected — it lives in the environment, not in this row.
50
+ */
51
+ changePassword(userId: string, currentPassword: string | undefined, newPassword: string): Promise<void>;
52
+ /** Accounts overview for the admin management screen (never exposes hashes). */
53
+ listAccounts(): Promise<Array<{
54
+ id: string;
55
+ email: string;
56
+ name: string;
57
+ hasPassword: boolean;
58
+ createdAt: Date;
59
+ }>>;
60
+ private assertPasswordPolicy;
24
61
  private signToken;
25
62
  /**
26
63
  * Get-or-create a user record by email, without issuing a session token.
@@ -71,6 +108,5 @@ export declare class AuthService {
71
108
  * regex-validated) email.
72
109
  */
73
110
  private assertAllowedDomain;
74
- private checkPassword;
75
111
  }
76
112
  //# sourceMappingURL=auth.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAmBhE,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,UAAU;IAG/B,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAiB7C;;;;;;;OAOG;IACG,kBAAkB,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAW7C,OAAO,CAAC,SAAS;IAIjB;;;;;;;OAOG;IACG,kBAAkB,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;YAQhD,iBAAiB;IAS/B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAQ7D;;;;;;;;;OASG;IACG,oBAAoB,CACxB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAClB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAY9D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAiB3D;;;;;;;OAOG;IACH,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAQ5C;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,aAAa;CAiBtB"}
1
+ {"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAsChE,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,UAAU;IAGrC;;;;;;;;;;;;;;OAcG;IACG,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAsC7C;;;;;;OAMG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAW7C;;;;;OAKG;IACG,aAAa,CACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC;IA2BpB;;;;;OAKG;IACG,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAgBhB,gFAAgF;IAC1E,YAAY,IAAI,OAAO,CAC3B,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,IAAI,CAAA;KAAE,CAAC,CAC1F;IAWD,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,SAAS;IAIjB;;;;;;;OAOG;IACG,kBAAkB,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;YAQhD,iBAAiB;IAS/B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAQ7D;;;;;;;;;OASG;IACG,oBAAoB,CACxB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAClB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAY9D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAiB3D;;;;;;;OAOG;IACH,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAQ5C;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;CAM5B"}
@@ -1,9 +1,21 @@
1
- import { timingSafeEqual } from 'node:crypto';
2
1
  import jwt from 'jsonwebtoken';
3
2
  import { eq } from 'drizzle-orm';
4
3
  import { users } from '../database/schema.js';
5
4
  import { hashEmail } from '../../shared/hash-email.js';
5
+ import { hashPassword, verifyPassword, timingSafeStringEqual, MIN_PASSWORD_LENGTH, } from './password-hash.js';
6
6
  const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
7
+ /**
8
+ * Decoy hash verified when the email is unknown or has no password set, so
9
+ * those paths cost the same scrypt work as a real wrong-password attempt —
10
+ * without it, response timing would reveal which emails have accounts.
11
+ * Lazily computed once; the compared password never matches it (verification
12
+ * result is discarded — those paths always refuse).
13
+ */
14
+ let decoyHashPromise = null;
15
+ function decoyHash() {
16
+ decoyHashPromise ??= hashPassword('bevel-decoy-password-never-matches');
17
+ return decoyHashPromise;
18
+ }
7
19
  function toAuthUser(user) {
8
20
  return {
9
21
  id: user.id,
@@ -19,38 +31,140 @@ export class AuthService {
19
31
  this.db = db;
20
32
  this.config = config;
21
33
  }
34
+ /**
35
+ * Password login. Two credential sources, in order:
36
+ *
37
+ * 1. The env bootstrap admin (`ADMIN_EMAIL` + `ADMIN_PASSWORD`), checked
38
+ * directly against the environment — never stored, so unsetting either
39
+ * variable disables it immediately. Lets a fresh deployment sign in
40
+ * before any account exists.
41
+ * 2. A per-user account: the `users` row's scrypt `password_hash`
42
+ * (created by an admin from Roles & Members, or set by the user on
43
+ * their Account page). Accounts that only ever signed in via SSO have
44
+ * no hash and are refused here.
45
+ *
46
+ * A generic "Invalid credentials" error for every failure mode — never
47
+ * reveal whether the email exists or has a password.
48
+ */
22
49
  async loginWithPassword(email, password) {
23
50
  const normalizedEmail = (email ?? '').trim().toLowerCase();
24
51
  if (!EMAIL_REGEX.test(normalizedEmail)) {
25
- throw new Error('Invalid email');
52
+ throw new Error('Invalid credentials');
26
53
  }
27
54
  this.assertAllowedDomain(normalizedEmail);
28
- if (!this.checkPassword(password ?? '')) {
29
- throw new Error('Invalid password');
55
+ const provided = password ?? '';
56
+ const isEnvAdmin = this.config.adminEmail.length > 0 &&
57
+ this.config.adminPassword.length > 0 &&
58
+ normalizedEmail === this.config.adminEmail &&
59
+ timingSafeStringEqual(provided, this.config.adminPassword);
60
+ if (isEnvAdmin) {
61
+ const defaultName = normalizedEmail.split('@')[0] || normalizedEmail;
62
+ const user = await this.upsertUserByEmail(normalizedEmail, defaultName);
63
+ return { token: this.signToken(user.id, user.email), user: toAuthUser(user) };
64
+ }
65
+ const [user] = await this.db
66
+ .select()
67
+ .from(users)
68
+ .where(eq(users.email, normalizedEmail))
69
+ .limit(1);
70
+ // Always run one scrypt verification — against the stored hash when there
71
+ // is one, against the decoy otherwise — so unknown emails and
72
+ // password-less (SSO-only) accounts take the same time as a wrong
73
+ // password and can't be enumerated via response timing.
74
+ const stored = user?.passwordHash ?? (await decoyHash());
75
+ const matches = await verifyPassword(provided, stored);
76
+ if (!user?.passwordHash || !matches) {
77
+ throw new Error('Invalid credentials');
30
78
  }
31
- // Derive a default display name from the local-part of the email
32
- const defaultName = normalizedEmail.split('@')[0] || normalizedEmail;
33
- const user = await this.upsertUserByEmail(normalizedEmail, defaultName);
34
79
  return { token: this.signToken(user.id, user.email), user: toAuthUser(user) };
35
80
  }
36
81
  /**
37
- * Establish a session for a user who authenticated via "Sign in with
38
- * Microsoft". Same upsert-by-email + JWT path as password login — the only
39
- * difference is identity came from a verified Microsoft id_token instead of
40
- * the shared password. Email is the idempotency key, so a user who first
41
- * used password login and later signs in with Microsoft (same email) keeps
42
- * the same account/id.
82
+ * Establish a session for a user whose identity an SSO provider (the
83
+ * generic OIDC plugin, or the enterprise "Sign in with Microsoft") already
84
+ * verified. Same upsert-by-email + JWT path as password login. Email is the
85
+ * idempotency key, so a user who first used password login and later signs
86
+ * in via SSO (same email) keeps the same account/id.
43
87
  */
44
- async loginWithMicrosoft(email, name) {
88
+ async loginWithSso(email, name) {
45
89
  const normalizedEmail = (email ?? '').trim().toLowerCase();
46
90
  if (!EMAIL_REGEX.test(normalizedEmail)) {
47
- throw new Error('Microsoft sign-in returned an invalid email');
91
+ throw new Error('Sign-in returned an invalid email');
48
92
  }
49
93
  this.assertAllowedDomain(normalizedEmail);
50
94
  const displayName = (name ?? '').trim() || normalizedEmail.split('@')[0] || normalizedEmail;
51
95
  const user = await this.upsertUserByEmail(normalizedEmail, displayName);
52
96
  return { token: this.signToken(user.id, user.email), user: toAuthUser(user) };
53
97
  }
98
+ /**
99
+ * Admin-driven account provisioning (Roles & Members → Accounts). Upserts
100
+ * the user by email and sets their password — re-provisioning an existing
101
+ * account (e.g. one that first arrived via SSO, or a reset for a locked-out
102
+ * user) is deliberate admin behavior, not an error.
103
+ */
104
+ async createAccount(email, name, password) {
105
+ const normalizedEmail = (email ?? '').trim().toLowerCase();
106
+ if (!EMAIL_REGEX.test(normalizedEmail)) {
107
+ throw new Error('Invalid email');
108
+ }
109
+ this.assertAllowedDomain(normalizedEmail);
110
+ this.assertPasswordPolicy(password);
111
+ const suppliedName = (name ?? '').trim();
112
+ const displayName = suppliedName || normalizedEmail.split('@')[0] || normalizedEmail;
113
+ const passwordHash = await hashPassword(password);
114
+ // One atomic upsert. On conflict (re-provisioning an existing account) an
115
+ // EXPLICITLY supplied name is persisted too; a blank name keeps the
116
+ // existing display name rather than clobbering it with the local-part
117
+ // fallback. `returning()` yields the authoritative row either way.
118
+ const [row] = await this.db
119
+ .insert(users)
120
+ .values({ email: normalizedEmail, name: displayName, passwordHash })
121
+ .onConflictDoUpdate({
122
+ target: users.email,
123
+ set: suppliedName
124
+ ? { passwordHash, name: suppliedName, updatedAt: new Date() }
125
+ : { passwordHash, updatedAt: new Date() },
126
+ })
127
+ .returning();
128
+ return toAuthUser(row);
129
+ }
130
+ /**
131
+ * Self-service password change (the Account page). The current password is
132
+ * required whenever one is set; an SSO-only account (no hash yet) may set
133
+ * its first password without one. The env bootstrap-admin credential is not
134
+ * affected — it lives in the environment, not in this row.
135
+ */
136
+ async changePassword(userId, currentPassword, newPassword) {
137
+ this.assertPasswordPolicy(newPassword);
138
+ const [user] = await this.db.select().from(users).where(eq(users.id, userId)).limit(1);
139
+ if (!user)
140
+ throw new Error('User not found');
141
+ if (user.passwordHash) {
142
+ if (!currentPassword || !(await verifyPassword(currentPassword, user.passwordHash))) {
143
+ throw new Error('Current password is incorrect');
144
+ }
145
+ }
146
+ const passwordHash = await hashPassword(newPassword);
147
+ await this.db
148
+ .update(users)
149
+ .set({ passwordHash, updatedAt: new Date() })
150
+ .where(eq(users.id, userId));
151
+ }
152
+ /** Accounts overview for the admin management screen (never exposes hashes). */
153
+ async listAccounts() {
154
+ const rows = await this.db.select().from(users).orderBy(users.email);
155
+ return rows.map((row) => ({
156
+ id: row.id,
157
+ email: row.email,
158
+ name: row.name,
159
+ hasPassword: row.passwordHash != null,
160
+ createdAt: row.createdAt,
161
+ }));
162
+ }
163
+ assertPasswordPolicy(password) {
164
+ if ((password ?? '').length < MIN_PASSWORD_LENGTH) {
165
+ throw new Error(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`);
166
+ }
167
+ }
54
168
  signToken(userId, email) {
55
169
  return jwt.sign({ userId, email }, this.config.jwtSecret, { expiresIn: '7d' });
56
170
  }
@@ -147,22 +261,5 @@ export class AuthService {
147
261
  throw new Error('Email domain is not allowed');
148
262
  }
149
263
  }
150
- checkPassword(provided) {
151
- const expected = this.config.testPassword;
152
- // Reject if no password is configured on the server
153
- if (!expected)
154
- return false;
155
- const a = Buffer.from(provided, 'utf8');
156
- const b = Buffer.from(expected, 'utf8');
157
- // Pad both buffers to the same length so timingSafeEqual runs unconditionally —
158
- // returning early on length mismatch would leak the expected password's length via timing.
159
- const len = Math.max(a.length, b.length);
160
- const aPadded = Buffer.alloc(len);
161
- const bPadded = Buffer.alloc(len);
162
- a.copy(aPadded);
163
- b.copy(bPadded);
164
- const equal = timingSafeEqual(aPadded, bPadded);
165
- return equal && a.length === b.length;
166
- }
167
264
  }
168
265
  //# sourceMappingURL=auth.service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type express from 'express';
2
+ import type { AuthProviderPlugin } from './auth.routes.js';
3
+ import type { AuthService } from './auth.service.js';
4
+ export interface OidcAuthProviderOptions {
5
+ /** Issuer base URL; `<issuer>/.well-known/openid-configuration` must exist. */
6
+ issuerUrl: string;
7
+ clientId: string;
8
+ clientSecret: string;
9
+ /** Space-separated; must include `openid` (and `email` for most providers). */
10
+ scopes: string;
11
+ /** Login-button label shown by the login screen. */
12
+ label: string;
13
+ publicBackendUrl: string;
14
+ publicFrontendUrl: string;
15
+ /** See MicrosoftAuthDeps.cookieSecure — scheme-derived, not NODE_ENV. */
16
+ cookieSecure: boolean;
17
+ /** Injectable for tests; defaults to global fetch. */
18
+ fetchImpl?: typeof fetch;
19
+ }
20
+ /**
21
+ * Generic OIDC single sign-on as an {@link AuthProviderPlugin} — works with
22
+ * any spec-compliant provider (Entra, Okta, Auth0, Keycloak, Google, …),
23
+ * configured purely from the environment (see CoreConfig `oidc*`).
24
+ *
25
+ * Flow: authorization-code with PKCE (S256) as a confidential client.
26
+ * Identity claims come from the `userinfo` endpoint called with the freshly
27
+ * exchanged access token — the token arrived over TLS directly from the
28
+ * issuer's token endpoint, so no local id_token signature verification (and
29
+ * no JWKS handling) is needed to trust it.
30
+ *
31
+ * Register `<PUBLIC_BACKEND_URL>/api/auth/oidc/callback` as the redirect URI
32
+ * with the provider.
33
+ */
34
+ export declare class OidcAuthProvider implements AuthProviderPlugin {
35
+ private readonly opts;
36
+ readonly key = "oidc";
37
+ readonly label: string;
38
+ readonly startPath = "/api/auth/oidc/login";
39
+ private readonly fetchImpl;
40
+ private discovery;
41
+ constructor(opts: OidcAuthProviderOptions);
42
+ /**
43
+ * Fetch + cache the issuer's discovery document. Resolved lazily (not at
44
+ * boot) so a temporarily unreachable issuer delays the first login instead
45
+ * of failing the whole deployment; a failed attempt is not cached.
46
+ */
47
+ private discover;
48
+ private redirectUri;
49
+ mountRoutes(router: express.Router, authService: AuthService): void;
50
+ }
51
+ //# sourceMappingURL=oidc-auth-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc-auth-provider.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/oidc-auth-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAcrD,MAAM,WAAW,uBAAuB;IACtC,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,YAAY,EAAE,OAAO,CAAC;IACtB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAoBD;;;;;;;;;;;;;GAaG;AACH,qBAAa,gBAAiB,YAAW,kBAAkB;IAQ7C,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPjC,QAAQ,CAAC,GAAG,UAAU;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,0BAA0B;IAE5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,SAAS,CAA8B;gBAElB,IAAI,EAAE,uBAAuB;IAK1D;;;;OAIG;YACW,QAAQ;IAiBtB,OAAO,CAAC,WAAW;IAInB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI;CAyHpE"}