@better-forge/auth 0.0.0-stage → 0.1.1

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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4 -2
  3. package/dist/apiTokenRoutes.d.ts +8 -0
  4. package/dist/apiTokenRoutes.d.ts.map +1 -0
  5. package/dist/apiTokenRoutes.js +67 -0
  6. package/dist/apiTokenRoutes.js.map +1 -0
  7. package/dist/apiTokens.d.ts +25 -0
  8. package/dist/apiTokens.d.ts.map +1 -0
  9. package/dist/apiTokens.js +50 -0
  10. package/dist/apiTokens.js.map +1 -0
  11. package/dist/authRoutes.d.ts +37 -0
  12. package/dist/authRoutes.d.ts.map +1 -0
  13. package/dist/authRoutes.js +403 -0
  14. package/dist/authRoutes.js.map +1 -0
  15. package/dist/context.d.ts +98 -0
  16. package/dist/context.d.ts.map +1 -0
  17. package/dist/context.js +2 -0
  18. package/dist/context.js.map +1 -0
  19. package/dist/createAuth.d.ts +146 -0
  20. package/dist/createAuth.d.ts.map +1 -0
  21. package/dist/createAuth.js +52 -0
  22. package/dist/createAuth.js.map +1 -0
  23. package/dist/identityConfirmation.d.ts +13 -0
  24. package/dist/identityConfirmation.d.ts.map +1 -0
  25. package/dist/identityConfirmation.js +37 -0
  26. package/dist/identityConfirmation.js.map +1 -0
  27. package/dist/index.d.ts +18 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +14 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/middleware.d.ts +41 -0
  32. package/dist/middleware.d.ts.map +1 -0
  33. package/dist/middleware.js +100 -0
  34. package/dist/middleware.js.map +1 -0
  35. package/dist/migrations.d.ts +4 -0
  36. package/dist/migrations.d.ts.map +1 -0
  37. package/dist/migrations.js +77 -0
  38. package/dist/migrations.js.map +1 -0
  39. package/dist/module.d.ts +8 -0
  40. package/dist/module.d.ts.map +1 -0
  41. package/dist/module.js +5 -0
  42. package/dist/module.js.map +1 -0
  43. package/dist/oidc.d.ts +85 -0
  44. package/dist/oidc.d.ts.map +1 -0
  45. package/dist/oidc.js +213 -0
  46. package/dist/oidc.js.map +1 -0
  47. package/dist/oidcConfig.d.ts +39 -0
  48. package/dist/oidcConfig.d.ts.map +1 -0
  49. package/dist/oidcConfig.js +72 -0
  50. package/dist/oidcConfig.js.map +1 -0
  51. package/dist/oidcSettings.d.ts +15 -0
  52. package/dist/oidcSettings.d.ts.map +1 -0
  53. package/dist/oidcSettings.js +51 -0
  54. package/dist/oidcSettings.js.map +1 -0
  55. package/dist/oidcSettingsRoutes.d.ts +19 -0
  56. package/dist/oidcSettingsRoutes.d.ts.map +1 -0
  57. package/dist/oidcSettingsRoutes.js +133 -0
  58. package/dist/oidcSettingsRoutes.js.map +1 -0
  59. package/dist/roles.d.ts +25 -0
  60. package/dist/roles.d.ts.map +1 -0
  61. package/dist/roles.js +44 -0
  62. package/dist/roles.js.map +1 -0
  63. package/dist/routeScope.d.ts +34 -0
  64. package/dist/routeScope.d.ts.map +1 -0
  65. package/dist/routeScope.js +32 -0
  66. package/dist/routeScope.js.map +1 -0
  67. package/dist/sessions.d.ts +46 -0
  68. package/dist/sessions.d.ts.map +1 -0
  69. package/dist/sessions.js +159 -0
  70. package/dist/sessions.js.map +1 -0
  71. package/dist/setupRoutes.d.ts +37 -0
  72. package/dist/setupRoutes.d.ts.map +1 -0
  73. package/dist/setupRoutes.js +70 -0
  74. package/dist/setupRoutes.js.map +1 -0
  75. package/dist/signInAudit.d.ts +19 -0
  76. package/dist/signInAudit.d.ts.map +1 -0
  77. package/dist/signInAudit.js +16 -0
  78. package/dist/signInAudit.js.map +1 -0
  79. package/dist/tables.d.ts +716 -0
  80. package/dist/tables.d.ts.map +1 -0
  81. package/dist/tables.js +56 -0
  82. package/dist/tables.js.map +1 -0
  83. package/dist/tokenCatalogue.d.ts +21 -0
  84. package/dist/tokenCatalogue.d.ts.map +1 -0
  85. package/dist/tokenCatalogue.js +27 -0
  86. package/dist/tokenCatalogue.js.map +1 -0
  87. package/dist/totp.d.ts +12 -0
  88. package/dist/totp.d.ts.map +1 -0
  89. package/dist/totp.js +93 -0
  90. package/dist/totp.js.map +1 -0
  91. package/dist/twoFactor.d.ts +10 -0
  92. package/dist/twoFactor.d.ts.map +1 -0
  93. package/dist/twoFactor.js +43 -0
  94. package/dist/twoFactor.js.map +1 -0
  95. package/dist/userRoutes.d.ts +13 -0
  96. package/dist/userRoutes.d.ts.map +1 -0
  97. package/dist/userRoutes.js +135 -0
  98. package/dist/userRoutes.js.map +1 -0
  99. package/package.json +66 -4
  100. package/src/apiTokenRoutes.ts +82 -0
  101. package/src/apiTokens.ts +62 -0
  102. package/src/authRoutes.ts +452 -0
  103. package/src/context.ts +85 -0
  104. package/src/createAuth.ts +58 -0
  105. package/src/identityConfirmation.ts +50 -0
  106. package/src/index.ts +17 -0
  107. package/src/middleware.ts +123 -0
  108. package/src/migrations.ts +77 -0
  109. package/src/module.ts +6 -0
  110. package/src/oidc.ts +291 -0
  111. package/src/oidcConfig.ts +96 -0
  112. package/src/oidcSettings.ts +60 -0
  113. package/src/oidcSettingsRoutes.ts +165 -0
  114. package/src/roles.ts +60 -0
  115. package/src/routeScope.ts +50 -0
  116. package/src/sessions.ts +209 -0
  117. package/src/setupRoutes.ts +100 -0
  118. package/src/signInAudit.ts +43 -0
  119. package/src/tables.ts +59 -0
  120. package/src/tokenCatalogue.ts +42 -0
  121. package/src/totp.ts +96 -0
  122. package/src/twoFactor.ts +49 -0
  123. package/src/userRoutes.ts +144 -0
package/dist/oidc.d.ts ADDED
@@ -0,0 +1,85 @@
1
+ import { users } from './tables.js';
2
+ import type { AuthOptions } from './context.js';
3
+ import type { OidcConfig } from './oidcConfig.js';
4
+ import type { SignInAudit } from './signInAudit.js';
5
+ type User = typeof users.$inferSelect;
6
+ export interface OidcFlowState {
7
+ state: string;
8
+ nonce: string;
9
+ codeVerifier: string;
10
+ }
11
+ /** An SSO confirmation must come from a sign-in at the provider no older than this (the ID token's auth_time). */
12
+ export declare const OIDC_CONFIRMATION_MAX_AGE_SECONDS: number;
13
+ /** Why an SSO sign-in was refused. The user only sees a generic message; the audit log gets the code and detail. */
14
+ export type OidcDenialCode = 'discovery_failed' | 'flow_expired' | 'idp_error' | 'token_exchange_failed' | 'no_email_claim' | 'email_not_verified' | 'no_matching_account' | 'subject_mismatch' | 'confirmation_mismatch';
15
+ export interface OidcDenial {
16
+ code: OidcDenialCode;
17
+ reason: string;
18
+ email?: string;
19
+ }
20
+ /** An account an SSO sign-in may create, bound to this identity. The application decides who qualifies and the role (see `resolveUnknownSsoUser`). */
21
+ export interface SsoProvisioning {
22
+ email: string;
23
+ subject: string;
24
+ role: string;
25
+ }
26
+ /**
27
+ * The outcome for a set of validated ID token claims. `link` means the user was matched by email and the
28
+ * identity (issuer + subject) should now be bound to them, so later sign-ins no longer depend on the email.
29
+ * A denial for an unknown email carries `provision` when a sign-in may create an account instead.
30
+ */
31
+ export type OidcDecision = {
32
+ user: User;
33
+ link: boolean;
34
+ subject: string;
35
+ denial?: undefined;
36
+ provision?: undefined;
37
+ } | {
38
+ user: null;
39
+ denial: OidcDenial;
40
+ provision?: SsoProvisioning | undefined;
41
+ };
42
+ /**
43
+ * Whether the email claim may be trusted. `email_verified` is the standard claim; Microsoft Entra ID never
44
+ * sends it but offers the optional `xms_edov` claim (email domain owner verified) instead. An explicit
45
+ * `false` from either claim is never overridden by `allowUnverifiedEmail`.
46
+ */
47
+ export declare function checkEmailVerified(claims: Record<string, unknown>, allowUnverifiedEmail: boolean): string | null;
48
+ export interface OidcTestResult {
49
+ issuer: string;
50
+ testedAt: number;
51
+ claims: Record<string, unknown> | null;
52
+ /** `provision`: the sign-in would create an account. */
53
+ outcome: 'sign_in' | 'link' | 'provision' | 'deny';
54
+ user: string | null;
55
+ denial: OidcDenial | null;
56
+ }
57
+ export declare function createOidc(ctx: AuthOptions, signInAudit: SignInAudit): {
58
+ beginOidcLogin: (cfg: OidcConfig, options?: {
59
+ confirm?: boolean;
60
+ }) => Promise<{
61
+ url: string;
62
+ flow: OidcFlowState;
63
+ }>;
64
+ exchangeOidcCode: (cfg: OidcConfig, callbackUrl: URL, flow: OidcFlowState, maxAge?: number) => Promise<{
65
+ claims: Record<string, unknown>;
66
+ denial?: undefined;
67
+ } | {
68
+ claims?: undefined;
69
+ denial: OidcDenial;
70
+ }>;
71
+ evaluateOidcClaims: (cfg: OidcConfig, claims: Record<string, unknown>) => Promise<OidcDecision>;
72
+ linkOidcIdentity: (cfg: OidcConfig, user: User, subject: string) => Promise<void>;
73
+ provisionSsoUser: (cfg: OidcConfig, provision: SsoProvisioning) => Promise<User>;
74
+ auditOidcDenial: (cfg: OidcConfig, denial: OidcDenial) => Promise<void>;
75
+ testOidcDiscovery: (cfg: OidcConfig) => Promise<{
76
+ issuer: string;
77
+ authorizationEndpoint: string | null;
78
+ }>;
79
+ storeOidcTestResult: (adminUserId: number, result: OidcTestResult) => string;
80
+ readOidcTestResult: (id: string, adminUserId: number) => OidcTestResult | null;
81
+ runOidcTest: (cfg: OidcConfig, callbackUrl: URL, flow: OidcFlowState) => Promise<OidcTestResult>;
82
+ };
83
+ export type Oidc = ReturnType<typeof createOidc>;
84
+ export {};
85
+ //# sourceMappingURL=oidc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.d.ts","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"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"}
package/dist/oidc.js ADDED
@@ -0,0 +1,213 @@
1
+ import { randomBytes, randomUUID } from 'node:crypto';
2
+ import bcrypt from 'bcryptjs';
3
+ import * as client from 'openid-client';
4
+ import { and, eq, sql } from 'drizzle-orm';
5
+ import { users } from './tables.js';
6
+ import { ADMIN_ROLE } from './roles.js';
7
+ /** An SSO confirmation must come from a sign-in at the provider no older than this (the ID token's auth_time). */
8
+ export const OIDC_CONFIRMATION_MAX_AGE_SECONDS = 5 * 60;
9
+ // Some providers send booleans as strings ("true").
10
+ const isTrue = (value) => value === true || value === 'true';
11
+ const isFalse = (value) => value === false || value === 'false';
12
+ /**
13
+ * Whether the email claim may be trusted. `email_verified` is the standard claim; Microsoft Entra ID never
14
+ * sends it but offers the optional `xms_edov` claim (email domain owner verified) instead. An explicit
15
+ * `false` from either claim is never overridden by `allowUnverifiedEmail`.
16
+ */
17
+ export function checkEmailVerified(claims, allowUnverifiedEmail) {
18
+ const standard = claims['email_verified'];
19
+ const entra = claims['xms_edov'];
20
+ if (isTrue(standard) || isTrue(entra))
21
+ return null;
22
+ if (isFalse(standard))
23
+ return 'The identity provider reports the email as unverified (email_verified is false).';
24
+ if (isFalse(entra))
25
+ return 'Microsoft Entra ID reports that the email domain is not verified (xms_edov is false).';
26
+ if (allowUnverifiedEmail && standard === undefined && entra === undefined)
27
+ return null;
28
+ return 'The ID token has neither an email_verified nor an xms_edov claim. For Microsoft Entra ID, add the xms_edov optional claim to the ID token. '
29
+ + 'For other providers, enable "Accept identity providers that omit email_verified" only if their email claim is trustworthy.';
30
+ }
31
+ const TEST_RESULT_TTL_MS = 10 * 60 * 1000;
32
+ export function createOidc(ctx, signInAudit) {
33
+ const { db, audit } = ctx;
34
+ const hooks = ctx.hooks ?? {};
35
+ const { auditSignInDenial } = signInAudit;
36
+ let cached = null;
37
+ function runDiscovery(cfg) {
38
+ const issuer = new URL(cfg.issuer);
39
+ // The client library refuses plain http; allow it only for issuers configured that way (e.g. a local Keycloak).
40
+ const options = issuer.protocol === 'http:' ? { execute: [client.allowInsecureRequests] } : undefined;
41
+ return client.discovery(issuer, cfg.clientId, cfg.clientSecret || undefined, undefined, options);
42
+ }
43
+ function discover(cfg) {
44
+ const key = `${cfg.issuer}|${cfg.clientId}|${cfg.clientSecret}`;
45
+ if (cached?.key !== key) {
46
+ const config = runDiscovery(cfg);
47
+ config.catch(() => { if (cached?.key === key)
48
+ cached = null; });
49
+ cached = { key, config };
50
+ }
51
+ return cached.config;
52
+ }
53
+ /**
54
+ * Builds the provider redirect. `confirm` asks the provider to authenticate the user again (`prompt=login`)
55
+ * and to report when it did (`max_age`), so the callback can require a fresh `auth_time`.
56
+ */
57
+ async function beginOidcLogin(cfg, options = {}) {
58
+ const config = await discover(cfg);
59
+ const flow = {
60
+ state: client.randomState(),
61
+ nonce: client.randomNonce(),
62
+ codeVerifier: client.randomPKCECodeVerifier(),
63
+ };
64
+ const url = client.buildAuthorizationUrl(config, {
65
+ redirect_uri: cfg.redirectUri,
66
+ scope: cfg.scopes,
67
+ state: flow.state,
68
+ nonce: flow.nonce,
69
+ code_challenge: await client.calculatePKCECodeChallenge(flow.codeVerifier),
70
+ code_challenge_method: 'S256',
71
+ ...(options.confirm ? { prompt: 'login', max_age: String(OIDC_CONFIRMATION_MAX_AGE_SECONDS) } : {}),
72
+ });
73
+ return { url: url.href, flow };
74
+ }
75
+ /**
76
+ * Completes the code exchange and returns the validated ID token claims, or why that failed. With `maxAge`
77
+ * the ID token must carry an `auth_time` no older than that many seconds.
78
+ */
79
+ async function exchangeOidcCode(cfg, callbackUrl, flow, maxAge) {
80
+ const config = await discover(cfg);
81
+ try {
82
+ const tokens = await client.authorizationCodeGrant(config, callbackUrl, {
83
+ pkceCodeVerifier: flow.codeVerifier,
84
+ expectedState: flow.state,
85
+ expectedNonce: flow.nonce,
86
+ idTokenExpected: true,
87
+ ...(maxAge !== undefined ? { maxAge } : {}),
88
+ });
89
+ return { claims: tokens.claims() ?? {} };
90
+ }
91
+ catch (error) {
92
+ return { denial: describeExchangeError(error) };
93
+ }
94
+ }
95
+ /**
96
+ * Finds the local user for validated claims without changing anything. An identity already bound to a
97
+ * user wins; otherwise the verified email is matched, but never onto a user bound to another identity at
98
+ * the same provider, so an email address given to someone else cannot take over the account.
99
+ */
100
+ async function evaluateOidcClaims(cfg, claims) {
101
+ const subject = typeof claims['sub'] === 'string' ? claims['sub'] : '';
102
+ if (subject) {
103
+ const bound = (await db.select().from(users)
104
+ .where(and(eq(users.oidcIssuer, cfg.issuer), eq(users.oidcSubject, subject))))[0];
105
+ if (bound)
106
+ return { user: bound, link: false, subject };
107
+ }
108
+ const email = typeof claims['email'] === 'string' ? claims['email'].trim().toLowerCase() : '';
109
+ if (!email) {
110
+ return {
111
+ user: null,
112
+ denial: {
113
+ code: 'no_email_claim',
114
+ reason: `The ID token has no email claim. Request the "email" scope and, for Microsoft Entra ID, add the email optional claim. Claims received: ${Object.keys(claims).join(', ') || 'none'}.`,
115
+ },
116
+ };
117
+ }
118
+ const unverified = checkEmailVerified(claims, cfg.allowUnverifiedEmail);
119
+ if (unverified)
120
+ return { user: null, denial: { code: 'email_not_verified', reason: unverified, email } };
121
+ const user = (await db.select().from(users).where(sql `lower(${users.email}) = ${email}`))[0];
122
+ if (!user) {
123
+ const resolved = subject ? await hooks.resolveUnknownSsoUser?.({ email, subject, issuer: cfg.issuer }) : null;
124
+ // A role the application did not register, or admin, is never handed out by a sign-in: no account is created.
125
+ const provision = subject && resolved && ctx.roles.isValid(resolved.role) && resolved.role !== ADMIN_ROLE ? { email, subject, role: resolved.role } : undefined;
126
+ return { user: null, denial: { code: 'no_matching_account', reason: `No user has the email ${email}. Create the user first.`, email }, provision };
127
+ }
128
+ if (user.oidcSubject && user.oidcIssuer === cfg.issuer) {
129
+ return {
130
+ user: null,
131
+ denial: {
132
+ code: 'subject_mismatch',
133
+ reason: `${user.email} is linked to another identity at this provider (subject ${user.oidcSubject}; this sign-in: ${subject || 'none'}). `
134
+ + 'The email address may have been given to someone else.',
135
+ email,
136
+ },
137
+ };
138
+ }
139
+ return { user, link: true, subject };
140
+ }
141
+ /** Binds the provider identity to the user and records it; a binding to a previous issuer is replaced. */
142
+ async function linkOidcIdentity(cfg, user, subject) {
143
+ await db.update(users).set({ oidcIssuer: cfg.issuer, oidcSubject: subject }).where(eq(users.id, user.id));
144
+ await audit.write({ userId: user.id, userEmail: user.email }, 'update', 'user-security', user.id, user.email, { ssoLinked: { from: user.oidcIssuer, to: cfg.issuer }, subject });
145
+ }
146
+ /**
147
+ * Creates the account of a first SSO sign-in, already bound to the identity.
148
+ * Its password is random and never handed out: the account signs in through SSO only.
149
+ */
150
+ async function provisionSsoUser(cfg, provision) {
151
+ const passwordHash = await bcrypt.hash(randomBytes(32).toString('base64url'), 10);
152
+ const [user] = await db.insert(users).values({
153
+ email: provision.email,
154
+ passwordHash,
155
+ role: provision.role,
156
+ createdAt: Date.now(),
157
+ oidcIssuer: cfg.issuer,
158
+ oidcSubject: provision.subject,
159
+ }).returning();
160
+ await audit.write({ userId: user.id, userEmail: user.email }, 'create', 'user', user.id, user.email, { email: user.email, role: provision.role, createdBy: 'sso', issuer: cfg.issuer });
161
+ return user;
162
+ }
163
+ /** Records a refused SSO sign-in or confirmation as a 'sign_in' denial, with the issuer. */
164
+ async function auditOidcDenial(cfg, denial) {
165
+ await auditSignInDenial('sso', denial, { issuer: cfg.issuer });
166
+ }
167
+ function describeExchangeError(error) {
168
+ if (error instanceof client.AuthorizationResponseError) {
169
+ const description = error.error_description ? `: ${error.error_description}` : '';
170
+ return { code: 'idp_error', reason: `The identity provider returned ${error.error}${description}`.slice(0, 1000) };
171
+ }
172
+ const message = error instanceof Error ? error.message : String(error);
173
+ return { code: 'token_exchange_failed', reason: `Code exchange or ID token validation failed: ${message}`.slice(0, 1000) };
174
+ }
175
+ /** Runs OIDC discovery against the issuer without caching, to validate settings before saving them. */
176
+ async function testOidcDiscovery(cfg) {
177
+ const config = await runDiscovery(cfg);
178
+ const meta = config.serverMetadata();
179
+ return { issuer: meta.issuer, authorizationEndpoint: meta.authorization_endpoint ?? null };
180
+ }
181
+ const testResults = new Map();
182
+ function storeOidcTestResult(adminUserId, result) {
183
+ const now = Date.now();
184
+ for (const [id, entry] of testResults)
185
+ if (entry.expiresAt <= now)
186
+ testResults.delete(id);
187
+ const id = randomUUID();
188
+ testResults.set(id, { adminUserId, expiresAt: now + TEST_RESULT_TTL_MS, result });
189
+ return id;
190
+ }
191
+ /** Only the administrator who started the test can read its result. */
192
+ function readOidcTestResult(id, adminUserId) {
193
+ const entry = testResults.get(id);
194
+ if (!entry || entry.expiresAt <= Date.now() || entry.adminUserId !== adminUserId)
195
+ return null;
196
+ return entry.result;
197
+ }
198
+ async function runOidcTest(cfg, callbackUrl, flow) {
199
+ const base = { issuer: cfg.issuer, testedAt: Date.now() };
200
+ const exchanged = await exchangeOidcCode(cfg, callbackUrl, flow)
201
+ .catch((error) => ({ claims: undefined, denial: describeExchangeError(error) }));
202
+ if (exchanged.denial)
203
+ return { ...base, claims: null, outcome: 'deny', user: null, denial: exchanged.denial };
204
+ const decision = await evaluateOidcClaims(cfg, exchanged.claims);
205
+ if (decision.provision)
206
+ return { ...base, claims: exchanged.claims, outcome: 'provision', user: decision.provision.email, denial: null };
207
+ if (decision.denial)
208
+ return { ...base, claims: exchanged.claims, outcome: 'deny', user: null, denial: decision.denial };
209
+ return { ...base, claims: exchanged.claims, outcome: decision.link ? 'link' : 'sign_in', user: decision.user.email, denial: null };
210
+ }
211
+ return { beginOidcLogin, exchangeOidcCode, evaluateOidcClaims, linkOidcIdentity, provisionSsoUser, auditOidcDenial, testOidcDiscovery, storeOidcTestResult, readOidcTestResult, runOidcTest };
212
+ }
213
+ //# sourceMappingURL=oidc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export declare const DEFAULT_OIDC_SCOPES = "openid email profile";
2
+ export declare const DEFAULT_OIDC_BUTTON_LABEL = "Sign in with SSO";
3
+ export interface OidcConfig {
4
+ issuer: string;
5
+ clientId: string;
6
+ clientSecret: string;
7
+ scopes: string;
8
+ redirectUri: string;
9
+ buttonLabel: string;
10
+ allowUnverifiedEmail: boolean;
11
+ disablePasswordLogin: boolean;
12
+ }
13
+ /** Raw, unvalidated OIDC settings from either source (environment or the database). */
14
+ export interface OidcSettingsInput {
15
+ issuer?: string | undefined;
16
+ clientId?: string | undefined;
17
+ clientSecret?: string | undefined;
18
+ scopes?: string | undefined;
19
+ redirectUri?: string | undefined;
20
+ buttonLabel?: string | undefined;
21
+ allowUnverifiedEmail?: boolean | undefined;
22
+ disablePasswordLogin?: boolean | undefined;
23
+ }
24
+ export declare function isHttpUrl(value: string): boolean;
25
+ /**
26
+ * Settings validation and the environment variables, for an application whose public address is `publicUrl(env)` and
27
+ * whose provider redirects back to `callbackPath`.
28
+ */
29
+ export declare function createOidcConfigResolver(options: {
30
+ publicUrl: (env: NodeJS.ProcessEnv) => string;
31
+ callbackPath: string;
32
+ }): {
33
+ defaultOidcRedirectUri: (env?: NodeJS.ProcessEnv) => string;
34
+ buildOidcConfig: (input: OidcSettingsInput, env?: NodeJS.ProcessEnv) => OidcConfig | null;
35
+ resolveOidcConfigFromEnv: (env?: NodeJS.ProcessEnv) => OidcConfig | null;
36
+ };
37
+ /** Password sign-in can only be turned off while OIDC is usable, so nobody is locked out by a typo. */
38
+ export declare function passwordLoginEnabled(config: OidcConfig | null): boolean;
39
+ //# sourceMappingURL=oidcConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidcConfig.d.ts","sourceRoot":"","sources":["../src/oidcConfig.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,yBAAyB,CAAA;AACzD,eAAO,MAAM,yBAAyB,qBAAqB,CAAA;AAE3D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,oBAAoB,EAAE,OAAO,CAAA;CAC9B;AAED,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC1C,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC3C;AAMD,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAOhD;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE;mCAElF,MAAM,CAAC,UAAU,KAAiB,MAAM;6BAM7C,iBAAiB,QAAO,MAAM,CAAC,UAAU,KAAiB,UAAU,GAAG,IAAI;qCAsBpE,MAAM,CAAC,UAAU,KAAiB,UAAU,GAAG,IAAI;EAiB3F;AAED,uGAAuG;AACvG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAEvE"}
@@ -0,0 +1,72 @@
1
+ export const DEFAULT_OIDC_SCOPES = 'openid email profile';
2
+ export const DEFAULT_OIDC_BUTTON_LABEL = 'Sign in with SSO';
3
+ function flag(value) {
4
+ return ['1', 'true', 'yes'].includes((value ?? '').trim().toLowerCase());
5
+ }
6
+ export function isHttpUrl(value) {
7
+ try {
8
+ const url = new URL(value);
9
+ return url.protocol === 'https:' || url.protocol === 'http:';
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ /**
16
+ * Settings validation and the environment variables, for an application whose public address is `publicUrl(env)` and
17
+ * whose provider redirects back to `callbackPath`.
18
+ */
19
+ export function createOidcConfigResolver(options) {
20
+ /** Callback URL derived from the public URL, or '' when that is not set. */
21
+ function defaultOidcRedirectUri(env = process.env) {
22
+ const publicUrl = options.publicUrl(env);
23
+ return publicUrl ? `${publicUrl}${options.callbackPath}` : '';
24
+ }
25
+ /** Validates raw settings and fills defaults. Returns null when they do not describe a usable setup. */
26
+ function buildOidcConfig(input, env = process.env) {
27
+ const issuer = (input.issuer ?? '').trim();
28
+ const clientId = (input.clientId ?? '').trim();
29
+ if (!issuer || !clientId || !isHttpUrl(issuer))
30
+ return null;
31
+ const redirectUri = (input.redirectUri ?? '').trim() || defaultOidcRedirectUri(env);
32
+ if (!redirectUri || !isHttpUrl(redirectUri))
33
+ return null;
34
+ return {
35
+ issuer,
36
+ clientId,
37
+ clientSecret: (input.clientSecret ?? '').trim(),
38
+ scopes: (input.scopes ?? '').trim() || DEFAULT_OIDC_SCOPES,
39
+ redirectUri,
40
+ buttonLabel: (input.buttonLabel ?? '').trim() || DEFAULT_OIDC_BUTTON_LABEL,
41
+ allowUnverifiedEmail: input.allowUnverifiedEmail === true,
42
+ disablePasswordLogin: input.disablePasswordLogin === true,
43
+ };
44
+ }
45
+ /**
46
+ * OIDC settings from environment variables, which override anything saved in the admin UI
47
+ * (for IaC deployments). Returns null when OIDC_ISSUER / OIDC_CLIENT_ID are not both set.
48
+ */
49
+ function resolveOidcConfigFromEnv(env = process.env) {
50
+ if (!(env['OIDC_ISSUER'] ?? '').trim() || !(env['OIDC_CLIENT_ID'] ?? '').trim())
51
+ return null;
52
+ const config = buildOidcConfig({
53
+ issuer: env['OIDC_ISSUER'],
54
+ clientId: env['OIDC_CLIENT_ID'],
55
+ clientSecret: env['OIDC_CLIENT_SECRET'],
56
+ scopes: env['OIDC_SCOPES'],
57
+ redirectUri: env['OIDC_REDIRECT_URI'],
58
+ buttonLabel: env['OIDC_BUTTON_LABEL'],
59
+ allowUnverifiedEmail: flag(env['OIDC_ALLOW_UNVERIFIED_EMAIL']),
60
+ disablePasswordLogin: flag(env['OIDC_DISABLE_PASSWORD_LOGIN']),
61
+ }, env);
62
+ if (!config)
63
+ console.warn('OIDC_* environment variables are set but incomplete or invalid (issuer, client id and a redirect URI or PUBLIC_URL are required) — OIDC sign-in is disabled');
64
+ return config;
65
+ }
66
+ return { defaultOidcRedirectUri, buildOidcConfig, resolveOidcConfigFromEnv };
67
+ }
68
+ /** Password sign-in can only be turned off while OIDC is usable, so nobody is locked out by a typo. */
69
+ export function passwordLoginEnabled(config) {
70
+ return !(config && config.disablePasswordLogin);
71
+ }
72
+ //# sourceMappingURL=oidcConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidcConfig.js","sourceRoot":"","sources":["../src/oidcConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { oidcSettings } from './tables.js';
2
+ import type { AuthOptions } from './context.js';
3
+ import type { OidcConfig, createOidcConfigResolver } from './oidcConfig.js';
4
+ export type OidcSettingsRow = typeof oidcSettings.$inferSelect;
5
+ export declare function createOidcSettings(ctx: AuthOptions, resolver: ReturnType<typeof createOidcConfigResolver>): {
6
+ getOidcSettingsRow: () => Promise<OidcSettingsRow | null>;
7
+ oidcConfigFromRow: (row: OidcSettingsRow) => OidcConfig | null;
8
+ getOidcConfig: () => Promise<{
9
+ config: OidcConfig | null;
10
+ source: "env" | "database" | "none";
11
+ }>;
12
+ getOidcTestConfig: () => Promise<OidcConfig | null>;
13
+ };
14
+ export type OidcSettings = ReturnType<typeof createOidcSettings>;
15
+ //# sourceMappingURL=oidcSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidcSettings.d.ts","sourceRoot":"","sources":["../src/oidcSettings.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAE3E,MAAM,MAAM,eAAe,GAAG,OAAO,YAAY,CAAC,YAAY,CAAA;AAE9D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC;8BAInE,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;6BAIpC,eAAe,KAAG,UAAU,GAAG,IAAI;yBAwBnC,OAAO,CAAC;QAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,KAAK,GAAG,UAAU,GAAG,MAAM,CAAA;KAAE,CAAC;6BAUvE,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;EAQ/D;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA"}
@@ -0,0 +1,51 @@
1
+ import { eq } from 'drizzle-orm';
2
+ import { oidcSettings } from './tables.js';
3
+ export function createOidcSettings(ctx, resolver) {
4
+ const { db, secretBox } = ctx;
5
+ const { buildOidcConfig, resolveOidcConfigFromEnv } = resolver;
6
+ async function getOidcSettingsRow() {
7
+ return (await db.select().from(oidcSettings).where(eq(oidcSettings.id, 1)))[0] ?? null;
8
+ }
9
+ function oidcConfigFromRow(row) {
10
+ return buildOidcConfig({
11
+ issuer: row.issuer,
12
+ clientId: row.clientId,
13
+ clientSecret: row.clientSecret ? secretBox.decrypt(row.clientSecret) : '',
14
+ scopes: row.scopes,
15
+ redirectUri: row.redirectUri,
16
+ buttonLabel: row.buttonLabel,
17
+ allowUnverifiedEmail: !!row.allowUnverifiedEmail,
18
+ disablePasswordLogin: !!row.disablePasswordLogin,
19
+ });
20
+ }
21
+ /** OIDC_FORCE_PASSWORD_LOGIN=true is the break-glass switch when the IdP is down and password sign-in was disabled. */
22
+ function withPasswordOverride(config) {
23
+ return ['1', 'true', 'yes'].includes((process.env['OIDC_FORCE_PASSWORD_LOGIN'] ?? '').trim().toLowerCase())
24
+ ? { ...config, disablePasswordLogin: false }
25
+ : config;
26
+ }
27
+ /**
28
+ * The OIDC configuration in effect: environment variables win (deployment-managed), otherwise the
29
+ * settings saved in the admin UI when enabled. Read per request, so saving takes effect without a restart.
30
+ */
31
+ async function getOidcConfig() {
32
+ const fromEnv = resolveOidcConfigFromEnv();
33
+ if (fromEnv)
34
+ return { config: withPasswordOverride(fromEnv), source: 'env' };
35
+ const row = await getOidcSettingsRow();
36
+ if (!row?.enabled)
37
+ return { config: null, source: 'none' };
38
+ const config = oidcConfigFromRow(row);
39
+ return { config: config && withPasswordOverride(config), source: config ? 'database' : 'none' };
40
+ }
41
+ /** Settings a test sign-in runs against: the environment, or the saved settings even while SSO is switched off. */
42
+ async function getOidcTestConfig() {
43
+ const fromEnv = resolveOidcConfigFromEnv();
44
+ if (fromEnv)
45
+ return fromEnv;
46
+ const row = await getOidcSettingsRow();
47
+ return row ? oidcConfigFromRow(row) : null;
48
+ }
49
+ return { getOidcSettingsRow, oidcConfigFromRow, getOidcConfig, getOidcTestConfig };
50
+ }
51
+ //# sourceMappingURL=oidcSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidcSettings.js","sourceRoot":"","sources":["../src/oidcSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAM1C,MAAM,UAAU,kBAAkB,CAAC,GAAgB,EAAE,QAAqD;IACxG,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,CAAA;IAC7B,MAAM,EAAE,eAAe,EAAE,wBAAwB,EAAE,GAAG,QAAQ,CAAA;IAE9D,KAAK,UAAU,kBAAkB;QAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;IACxF,CAAC;IAED,SAAS,iBAAiB,CAAC,GAAoB;QAC7C,OAAO,eAAe,CAAC;YACrB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE;YACzE,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,oBAAoB,EAAE,CAAC,CAAC,GAAG,CAAC,oBAAoB;YAChD,oBAAoB,EAAE,CAAC,CAAC,GAAG,CAAC,oBAAoB;SACjD,CAAC,CAAA;IACJ,CAAC;IAED,uHAAuH;IACvH,SAAS,oBAAoB,CAAC,MAAkB;QAC9C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACzG,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,oBAAoB,EAAE,KAAK,EAAE;YAC5C,CAAC,CAAC,MAAM,CAAA;IACZ,CAAC;IAED;;;OAGG;IACH,KAAK,UAAU,aAAa;QAC1B,MAAM,OAAO,GAAG,wBAAwB,EAAE,CAAA;QAC1C,IAAI,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,oBAAoB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;QAC5E,MAAM,GAAG,GAAG,MAAM,kBAAkB,EAAE,CAAA;QACtC,IAAI,CAAC,GAAG,EAAE,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;QAC1D,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;QACrC,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,CAAA;IACjG,CAAC;IAED,mHAAmH;IACnH,KAAK,UAAU,iBAAiB;QAC9B,MAAM,OAAO,GAAG,wBAAwB,EAAE,CAAA;QAC1C,IAAI,OAAO;YAAE,OAAO,OAAO,CAAA;QAC3B,MAAM,GAAG,GAAG,MAAM,kBAAkB,EAAE,CAAA;QACtC,OAAO,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5C,CAAC;IAED,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EAAE,CAAA;AACpF,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { FastifyInstance, FastifyReply } from 'fastify';
2
+ import type { AuthOptions } from './context.js';
3
+ import type { IdentityConfirmation } from './identityConfirmation.js';
4
+ import type { Oidc } from './oidc.js';
5
+ import type { OidcSettings } from './oidcSettings.js';
6
+ import type { OidcConfig, createOidcConfigResolver } from './oidcConfig.js';
7
+ export declare function createOidcSettingsRoutes(ctx: AuthOptions, parts: {
8
+ identityConfirmation: IdentityConfirmation;
9
+ oidc: Oidc;
10
+ oidcSettings: OidcSettings;
11
+ resolver: ReturnType<typeof createOidcConfigResolver>;
12
+ startOidcFlow: (app: FastifyInstance, reply: FastifyReply, cfg: OidcConfig, extra: {
13
+ purpose?: 'oidc-flow' | 'oidc-test' | 'oidc-confirm';
14
+ adminUserId?: number;
15
+ }) => Promise<unknown>;
16
+ }): {
17
+ oidcSettingsRoutes: (app: FastifyInstance) => Promise<void>;
18
+ };
19
+ //# sourceMappingURL=oidcSettingsRoutes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidcSettingsRoutes.d.ts","sourceRoot":"","sources":["../src/oidcSettingsRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAG5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAA;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAgB3E,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE;IAChE,oBAAoB,EAAE,oBAAoB,CAAA;IAC1C,IAAI,EAAE,IAAI,CAAA;IACV,YAAY,EAAE,YAAY,CAAA;IAC1B,QAAQ,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAA;IACrD,aAAa,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE;QAAE,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,cAAc,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACvL;8BAqCwC,eAAe;EAkGvD"}