@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.
- package/LICENSE +21 -0
- package/README.md +4 -2
- package/dist/apiTokenRoutes.d.ts +8 -0
- package/dist/apiTokenRoutes.d.ts.map +1 -0
- package/dist/apiTokenRoutes.js +67 -0
- package/dist/apiTokenRoutes.js.map +1 -0
- package/dist/apiTokens.d.ts +25 -0
- package/dist/apiTokens.d.ts.map +1 -0
- package/dist/apiTokens.js +50 -0
- package/dist/apiTokens.js.map +1 -0
- package/dist/authRoutes.d.ts +37 -0
- package/dist/authRoutes.d.ts.map +1 -0
- package/dist/authRoutes.js +403 -0
- package/dist/authRoutes.js.map +1 -0
- package/dist/context.d.ts +98 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/createAuth.d.ts +146 -0
- package/dist/createAuth.d.ts.map +1 -0
- package/dist/createAuth.js +52 -0
- package/dist/createAuth.js.map +1 -0
- package/dist/identityConfirmation.d.ts +13 -0
- package/dist/identityConfirmation.d.ts.map +1 -0
- package/dist/identityConfirmation.js +37 -0
- package/dist/identityConfirmation.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +41 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +100 -0
- package/dist/middleware.js.map +1 -0
- package/dist/migrations.d.ts +4 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +77 -0
- package/dist/migrations.js.map +1 -0
- package/dist/module.d.ts +8 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +5 -0
- package/dist/module.js.map +1 -0
- package/dist/oidc.d.ts +85 -0
- package/dist/oidc.d.ts.map +1 -0
- package/dist/oidc.js +213 -0
- package/dist/oidc.js.map +1 -0
- package/dist/oidcConfig.d.ts +39 -0
- package/dist/oidcConfig.d.ts.map +1 -0
- package/dist/oidcConfig.js +72 -0
- package/dist/oidcConfig.js.map +1 -0
- package/dist/oidcSettings.d.ts +15 -0
- package/dist/oidcSettings.d.ts.map +1 -0
- package/dist/oidcSettings.js +51 -0
- package/dist/oidcSettings.js.map +1 -0
- package/dist/oidcSettingsRoutes.d.ts +19 -0
- package/dist/oidcSettingsRoutes.d.ts.map +1 -0
- package/dist/oidcSettingsRoutes.js +133 -0
- package/dist/oidcSettingsRoutes.js.map +1 -0
- package/dist/roles.d.ts +25 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/roles.js +44 -0
- package/dist/roles.js.map +1 -0
- package/dist/routeScope.d.ts +34 -0
- package/dist/routeScope.d.ts.map +1 -0
- package/dist/routeScope.js +32 -0
- package/dist/routeScope.js.map +1 -0
- package/dist/sessions.d.ts +46 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +159 -0
- package/dist/sessions.js.map +1 -0
- package/dist/setupRoutes.d.ts +37 -0
- package/dist/setupRoutes.d.ts.map +1 -0
- package/dist/setupRoutes.js +70 -0
- package/dist/setupRoutes.js.map +1 -0
- package/dist/signInAudit.d.ts +19 -0
- package/dist/signInAudit.d.ts.map +1 -0
- package/dist/signInAudit.js +16 -0
- package/dist/signInAudit.js.map +1 -0
- package/dist/tables.d.ts +716 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +56 -0
- package/dist/tables.js.map +1 -0
- package/dist/tokenCatalogue.d.ts +21 -0
- package/dist/tokenCatalogue.d.ts.map +1 -0
- package/dist/tokenCatalogue.js +27 -0
- package/dist/tokenCatalogue.js.map +1 -0
- package/dist/totp.d.ts +12 -0
- package/dist/totp.d.ts.map +1 -0
- package/dist/totp.js +93 -0
- package/dist/totp.js.map +1 -0
- package/dist/twoFactor.d.ts +10 -0
- package/dist/twoFactor.d.ts.map +1 -0
- package/dist/twoFactor.js +43 -0
- package/dist/twoFactor.js.map +1 -0
- package/dist/userRoutes.d.ts +13 -0
- package/dist/userRoutes.d.ts.map +1 -0
- package/dist/userRoutes.js +135 -0
- package/dist/userRoutes.js.map +1 -0
- package/package.json +66 -4
- package/src/apiTokenRoutes.ts +82 -0
- package/src/apiTokens.ts +62 -0
- package/src/authRoutes.ts +452 -0
- package/src/context.ts +85 -0
- package/src/createAuth.ts +58 -0
- package/src/identityConfirmation.ts +50 -0
- package/src/index.ts +17 -0
- package/src/middleware.ts +123 -0
- package/src/migrations.ts +77 -0
- package/src/module.ts +6 -0
- package/src/oidc.ts +291 -0
- package/src/oidcConfig.ts +96 -0
- package/src/oidcSettings.ts +60 -0
- package/src/oidcSettingsRoutes.ts +165 -0
- package/src/roles.ts +60 -0
- package/src/routeScope.ts +50 -0
- package/src/sessions.ts +209 -0
- package/src/setupRoutes.ts +100 -0
- package/src/signInAudit.ts +43 -0
- package/src/tables.ts +59 -0
- package/src/tokenCatalogue.ts +42 -0
- package/src/totp.ts +96 -0
- package/src/twoFactor.ts +49 -0
- package/src/userRoutes.ts +144 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { AuthOptions } from './context.js';
|
|
2
|
+
/**
|
|
3
|
+
* Accounts, sessions, two-factor sign-in, single sign-on, API tokens and the routes that manage them. Everything is
|
|
4
|
+
* built from `options` and kept in the returned closures; nothing is shared between two calls.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createAuth(options: AuthOptions): {
|
|
7
|
+
sessions: {
|
|
8
|
+
createAuthSession: (app: import("fastify").FastifyInstance, reply: import("fastify").FastifyReply, user: {
|
|
9
|
+
id: number;
|
|
10
|
+
email: string;
|
|
11
|
+
role: string;
|
|
12
|
+
mustChangePassword: number;
|
|
13
|
+
totpEnabled: number;
|
|
14
|
+
}, authMethod?: import("./sessions.js").AuthMethod) => Promise<import("./sessions.js").AuthIdentity>;
|
|
15
|
+
clearAuthCookies: (reply: import("fastify").FastifyReply) => void;
|
|
16
|
+
authenticateRequest: (req: import("fastify").FastifyRequest, options?: import("./sessions.js").AuthenticateOptions) => Promise<import("./sessions.js").AuthIdentity>;
|
|
17
|
+
verifyCsrf: (req: import("fastify").FastifyRequest, identity: import("./sessions.js").AuthIdentity) => Promise<void>;
|
|
18
|
+
revokeSession: (sessionId: string) => Promise<void>;
|
|
19
|
+
revokeUserSessions: (userId: number, exceptSessionId?: string) => Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
middleware: {
|
|
22
|
+
authenticateOrReject: (req: import("fastify").FastifyRequest, reply: import("fastify").FastifyReply, unauthorized?: Record<string, string>) => Promise<import("./sessions.js").AuthIdentity | null>;
|
|
23
|
+
requireAuth: ((req: import("fastify").FastifyRequest, reply: import("fastify").FastifyReply) => Promise<undefined>) & {
|
|
24
|
+
requiresAuth: boolean;
|
|
25
|
+
};
|
|
26
|
+
requireRole: (...allowed: string[]) => ((req: import("fastify").FastifyRequest, reply: import("fastify").FastifyReply) => Promise<undefined>) & {
|
|
27
|
+
allowedRoles: string[];
|
|
28
|
+
requiresAuth: boolean;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
identityConfirmation: {
|
|
32
|
+
confirmIdentity: (identity: import("./sessions.js").AuthIdentity, currentPassword: unknown, reply: import("fastify").FastifyReply) => Promise<boolean>;
|
|
33
|
+
markSessionVerified: (sessionId: string, userId: number) => Promise<boolean>;
|
|
34
|
+
};
|
|
35
|
+
twoFactor: {
|
|
36
|
+
verifySecondFactor: (user: {
|
|
37
|
+
id: number;
|
|
38
|
+
totpSecret: string | null;
|
|
39
|
+
totpEnabled: number;
|
|
40
|
+
totpRecoveryCodes: string | null;
|
|
41
|
+
}, code: string) => Promise<boolean>;
|
|
42
|
+
resetTwoFactor: (userId: number) => Promise<boolean>;
|
|
43
|
+
};
|
|
44
|
+
signInAudit: {
|
|
45
|
+
auditSignIn: (user: {
|
|
46
|
+
id: number;
|
|
47
|
+
email: string;
|
|
48
|
+
}, method: import("./signInAudit.js").SignInMethod, twoFactor: boolean) => Promise<void>;
|
|
49
|
+
auditSignInDenial: (method: import("./signInAudit.js").SignInMethod, denial: import("./signInAudit.js").SignInDenial, extra?: Record<string, unknown>) => Promise<void>;
|
|
50
|
+
};
|
|
51
|
+
oidcConfig: {
|
|
52
|
+
defaultOidcRedirectUri: (env?: NodeJS.ProcessEnv) => string;
|
|
53
|
+
buildOidcConfig: (input: import("./oidcConfig.js").OidcSettingsInput, env?: NodeJS.ProcessEnv) => import("./oidcConfig.js").OidcConfig | null;
|
|
54
|
+
resolveOidcConfigFromEnv: (env?: NodeJS.ProcessEnv) => import("./oidcConfig.js").OidcConfig | null;
|
|
55
|
+
};
|
|
56
|
+
oidcSettings: {
|
|
57
|
+
getOidcSettingsRow: () => Promise<import("./oidcSettings.js").OidcSettingsRow | null>;
|
|
58
|
+
oidcConfigFromRow: (row: import("./oidcSettings.js").OidcSettingsRow) => import("./oidcConfig.js").OidcConfig | null;
|
|
59
|
+
getOidcConfig: () => Promise<{
|
|
60
|
+
config: import("./oidcConfig.js").OidcConfig | null;
|
|
61
|
+
source: "env" | "database" | "none";
|
|
62
|
+
}>;
|
|
63
|
+
getOidcTestConfig: () => Promise<import("./oidcConfig.js").OidcConfig | null>;
|
|
64
|
+
};
|
|
65
|
+
oidc: {
|
|
66
|
+
beginOidcLogin: (cfg: import("./oidcConfig.js").OidcConfig, options?: {
|
|
67
|
+
confirm?: boolean;
|
|
68
|
+
}) => Promise<{
|
|
69
|
+
url: string;
|
|
70
|
+
flow: import("./oidc.js").OidcFlowState;
|
|
71
|
+
}>;
|
|
72
|
+
exchangeOidcCode: (cfg: import("./oidcConfig.js").OidcConfig, callbackUrl: URL, flow: import("./oidc.js").OidcFlowState, maxAge?: number) => Promise<{
|
|
73
|
+
claims: Record<string, unknown>;
|
|
74
|
+
denial?: undefined;
|
|
75
|
+
} | {
|
|
76
|
+
claims?: undefined;
|
|
77
|
+
denial: import("./oidc.js").OidcDenial;
|
|
78
|
+
}>;
|
|
79
|
+
evaluateOidcClaims: (cfg: import("./oidcConfig.js").OidcConfig, claims: Record<string, unknown>) => Promise<import("./oidc.js").OidcDecision>;
|
|
80
|
+
linkOidcIdentity: (cfg: import("./oidcConfig.js").OidcConfig, user: {
|
|
81
|
+
id: number;
|
|
82
|
+
email: string;
|
|
83
|
+
passwordHash: string;
|
|
84
|
+
role: string;
|
|
85
|
+
createdAt: number;
|
|
86
|
+
mustChangePassword: number;
|
|
87
|
+
totpSecret: string | null;
|
|
88
|
+
totpEnabled: number;
|
|
89
|
+
totpRecoveryCodes: string | null;
|
|
90
|
+
oidcIssuer: string | null;
|
|
91
|
+
oidcSubject: string | null;
|
|
92
|
+
}, subject: string) => Promise<void>;
|
|
93
|
+
provisionSsoUser: (cfg: import("./oidcConfig.js").OidcConfig, provision: import("./oidc.js").SsoProvisioning) => Promise<{
|
|
94
|
+
id: number;
|
|
95
|
+
email: string;
|
|
96
|
+
passwordHash: string;
|
|
97
|
+
role: string;
|
|
98
|
+
createdAt: number;
|
|
99
|
+
mustChangePassword: number;
|
|
100
|
+
totpSecret: string | null;
|
|
101
|
+
totpEnabled: number;
|
|
102
|
+
totpRecoveryCodes: string | null;
|
|
103
|
+
oidcIssuer: string | null;
|
|
104
|
+
oidcSubject: string | null;
|
|
105
|
+
}>;
|
|
106
|
+
auditOidcDenial: (cfg: import("./oidcConfig.js").OidcConfig, denial: import("./oidc.js").OidcDenial) => Promise<void>;
|
|
107
|
+
testOidcDiscovery: (cfg: import("./oidcConfig.js").OidcConfig) => Promise<{
|
|
108
|
+
issuer: string;
|
|
109
|
+
authorizationEndpoint: string | null;
|
|
110
|
+
}>;
|
|
111
|
+
storeOidcTestResult: (adminUserId: number, result: import("./oidc.js").OidcTestResult) => string;
|
|
112
|
+
readOidcTestResult: (id: string, adminUserId: number) => import("./oidc.js").OidcTestResult | null;
|
|
113
|
+
runOidcTest: (cfg: import("./oidcConfig.js").OidcConfig, callbackUrl: URL, flow: import("./oidc.js").OidcFlowState) => Promise<import("./oidc.js").OidcTestResult>;
|
|
114
|
+
};
|
|
115
|
+
apiTokens: {
|
|
116
|
+
parseScopes: (stored: string) => string[];
|
|
117
|
+
isApiToken: (value: string) => boolean;
|
|
118
|
+
hashApiToken: (token: string) => string;
|
|
119
|
+
generateApiToken: () => {
|
|
120
|
+
token: string;
|
|
121
|
+
hash: string;
|
|
122
|
+
prefix: string;
|
|
123
|
+
};
|
|
124
|
+
findUsableApiToken: (token: string) => Promise<{
|
|
125
|
+
id: number;
|
|
126
|
+
name: string;
|
|
127
|
+
tokenHash: string;
|
|
128
|
+
prefix: string;
|
|
129
|
+
createdAt: number;
|
|
130
|
+
expiresAt: number | null;
|
|
131
|
+
lastUsedAt: number | null;
|
|
132
|
+
scopes: string;
|
|
133
|
+
} | null>;
|
|
134
|
+
consumeApiTokenBudget: (tokenId: number, now?: number) => boolean;
|
|
135
|
+
resetApiTokenBudgets: () => void;
|
|
136
|
+
};
|
|
137
|
+
/** Plain plugins (not wrapped for encapsulation), so the hooks of the scope they are registered in apply to them. */
|
|
138
|
+
routes: {
|
|
139
|
+
signIn: (app: import("fastify").FastifyInstance) => Promise<void>;
|
|
140
|
+
users: (app: import("fastify").FastifyInstance) => Promise<void>;
|
|
141
|
+
oidcSettings: (app: import("fastify").FastifyInstance) => Promise<void>;
|
|
142
|
+
apiTokens: (app: import("fastify").FastifyInstance) => Promise<void>;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
export type Auth = ReturnType<typeof createAuth>;
|
|
146
|
+
//# sourceMappingURL=createAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAuth.d.ts","sourceRoot":"","sources":["../src/createAuth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAe/C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAuC03E,CAAC;;;;;;;kBAAkjC,CAAC;;kBAAwB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAXl/G,qHAAqH;;;;;;;EAQxH;AAED,MAAM,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createApiTokenRoutes } from './apiTokenRoutes.js';
|
|
2
|
+
import { createApiTokens } from './apiTokens.js';
|
|
3
|
+
import { createIdentityConfirmation } from './identityConfirmation.js';
|
|
4
|
+
import { createMiddleware } from './middleware.js';
|
|
5
|
+
import { createOidc } from './oidc.js';
|
|
6
|
+
import { createOidcConfigResolver } from './oidcConfig.js';
|
|
7
|
+
import { createOidcSettings } from './oidcSettings.js';
|
|
8
|
+
import { createOidcSettingsRoutes } from './oidcSettingsRoutes.js';
|
|
9
|
+
import { createSessions } from './sessions.js';
|
|
10
|
+
import { createSignInAudit } from './signInAudit.js';
|
|
11
|
+
import { createSignInRoutes } from './authRoutes.js';
|
|
12
|
+
import { createTwoFactor } from './twoFactor.js';
|
|
13
|
+
import { createUserRoutes } from './userRoutes.js';
|
|
14
|
+
/**
|
|
15
|
+
* Accounts, sessions, two-factor sign-in, single sign-on, API tokens and the routes that manage them. Everything is
|
|
16
|
+
* built from `options` and kept in the returned closures; nothing is shared between two calls.
|
|
17
|
+
*/
|
|
18
|
+
export function createAuth(options) {
|
|
19
|
+
options.roles.freeze();
|
|
20
|
+
const apiTokens = createApiTokens(options);
|
|
21
|
+
const sessions = createSessions(options, apiTokens);
|
|
22
|
+
const middleware = createMiddleware(sessions, apiTokens);
|
|
23
|
+
const identityConfirmation = createIdentityConfirmation(options);
|
|
24
|
+
const twoFactor = createTwoFactor(options);
|
|
25
|
+
const signInAudit = createSignInAudit(options);
|
|
26
|
+
const oidcConfig = createOidcConfigResolver({ publicUrl: options.publicUrl, callbackPath: `${options.basePath}/oidc/callback` });
|
|
27
|
+
const oidcSettings = createOidcSettings(options, oidcConfig);
|
|
28
|
+
const oidc = createOidc(options, signInAudit);
|
|
29
|
+
const signIn = createSignInRoutes(options, { sessions, middleware, twoFactor, identityConfirmation, signInAudit, oidcSettings, oidc });
|
|
30
|
+
const users = createUserRoutes(options, { sessions, twoFactor, identityConfirmation });
|
|
31
|
+
const oidcSettingsRoutes = createOidcSettingsRoutes(options, { identityConfirmation, oidc, oidcSettings, resolver: oidcConfig, startOidcFlow: signIn.startOidcFlow });
|
|
32
|
+
const tokenRoutes = createApiTokenRoutes(options, apiTokens, identityConfirmation);
|
|
33
|
+
return {
|
|
34
|
+
sessions,
|
|
35
|
+
middleware,
|
|
36
|
+
identityConfirmation,
|
|
37
|
+
twoFactor,
|
|
38
|
+
signInAudit,
|
|
39
|
+
oidcConfig,
|
|
40
|
+
oidcSettings,
|
|
41
|
+
oidc,
|
|
42
|
+
apiTokens,
|
|
43
|
+
/** Plain plugins (not wrapped for encapsulation), so the hooks of the scope they are registered in apply to them. */
|
|
44
|
+
routes: {
|
|
45
|
+
signIn: signIn.authRoutes,
|
|
46
|
+
users: users.userRoutes,
|
|
47
|
+
oidcSettings: oidcSettingsRoutes.oidcSettingsRoutes,
|
|
48
|
+
apiTokens: tokenRoutes.apiTokenRoutes,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=createAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAuth.js","sourceRoot":"","sources":["../src/createAuth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAElD;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,OAAoB;IAC7C,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAA;IAEtB,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IACnD,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;IACxD,MAAM,oBAAoB,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAA;IAChE,MAAM,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;IAC1C,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAC9C,MAAM,UAAU,GAAG,wBAAwB,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,QAAQ,gBAAgB,EAAE,CAAC,CAAA;IAChI,MAAM,YAAY,GAAG,kBAAkB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;IAC5D,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;IAE7C,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAA;IACtI,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,oBAAoB,EAAE,CAAC,CAAA;IACtF,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,OAAO,EAAE,EAAE,oBAAoB,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAA;IACrK,MAAM,WAAW,GAAG,oBAAoB,CAAC,OAAO,EAAE,SAAS,EAAE,oBAAoB,CAAC,CAAA;IAElF,OAAO;QACL,QAAQ;QACR,UAAU;QACV,oBAAoB;QACpB,SAAS;QACT,WAAW;QACX,UAAU;QACV,YAAY;QACZ,IAAI;QACJ,SAAS;QACT,qHAAqH;QACrH,MAAM,EAAE;YACN,MAAM,EAAE,MAAM,CAAC,UAAU;YACzB,KAAK,EAAE,KAAK,CAAC,UAAU;YACvB,YAAY,EAAE,kBAAkB,CAAC,kBAAkB;YACnD,SAAS,EAAE,WAAW,CAAC,cAAc;SACtC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FastifyReply } from 'fastify';
|
|
2
|
+
import type { AuthOptions } from './context.js';
|
|
3
|
+
import type { AuthIdentity } from './sessions.js';
|
|
4
|
+
/** Error code telling the admin UI to confirm the user through the identity provider, then retry. */
|
|
5
|
+
export declare const SSO_CONFIRMATION_REQUIRED = "SSO_CONFIRMATION_REQUIRED";
|
|
6
|
+
/** How long an SSO sign-in or SSO confirmation covers sensitive actions in that session. */
|
|
7
|
+
export declare const SSO_CONFIRMATION_WINDOW_MS: number;
|
|
8
|
+
export declare function createIdentityConfirmation(ctx: AuthOptions): {
|
|
9
|
+
confirmIdentity: (identity: AuthIdentity, currentPassword: unknown, reply: FastifyReply) => Promise<boolean>;
|
|
10
|
+
markSessionVerified: (sessionId: string, userId: number) => Promise<boolean>;
|
|
11
|
+
};
|
|
12
|
+
export type IdentityConfirmation = ReturnType<typeof createIdentityConfirmation>;
|
|
13
|
+
//# sourceMappingURL=identityConfirmation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identityConfirmation.d.ts","sourceRoot":"","sources":["../src/identityConfirmation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,qGAAqG;AACrG,eAAO,MAAM,yBAAyB,8BAA8B,CAAA;AAEpE,4FAA4F;AAC5F,eAAO,MAAM,0BAA0B,QAAiB,CAAA;AAExD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,WAAW;gCAQhB,YAAY,mBAAmB,OAAO,SAAS,YAAY,KAAG,OAAO,CAAC,OAAO,CAAC;qCAezE,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;EAWxF;AAED,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,0BAA0B,CAAC,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import bcrypt from 'bcryptjs';
|
|
2
|
+
import { and, eq, gt } from 'drizzle-orm';
|
|
3
|
+
import { authSessions, users } from './tables.js';
|
|
4
|
+
/** Error code telling the admin UI to confirm the user through the identity provider, then retry. */
|
|
5
|
+
export const SSO_CONFIRMATION_REQUIRED = 'SSO_CONFIRMATION_REQUIRED';
|
|
6
|
+
/** How long an SSO sign-in or SSO confirmation covers sensitive actions in that session. */
|
|
7
|
+
export const SSO_CONFIRMATION_WINDOW_MS = 10 * 60 * 1000;
|
|
8
|
+
export function createIdentityConfirmation(ctx) {
|
|
9
|
+
const { db } = ctx;
|
|
10
|
+
/**
|
|
11
|
+
* Sensitive actions are confirmed the way the session signed in: a password session with the current
|
|
12
|
+
* password, an SSO session with a recent sign-in at the identity provider (the sign-in itself, or a
|
|
13
|
+
* confirmation popup started from the UI). Returns true, or sends the refusal and returns false.
|
|
14
|
+
*/
|
|
15
|
+
async function confirmIdentity(identity, currentPassword, reply) {
|
|
16
|
+
if (identity.authMethod === 'oidc') {
|
|
17
|
+
const session = (await db.select().from(authSessions).where(eq(authSessions.id, identity.sessionId)))[0];
|
|
18
|
+
const verifiedAt = session?.verifiedAt ?? session?.createdAt ?? 0;
|
|
19
|
+
if (Date.now() - verifiedAt <= SSO_CONFIRMATION_WINDOW_MS)
|
|
20
|
+
return true;
|
|
21
|
+
await reply.code(403).send({ error: 'Confirm your identity with single sign-on to continue', code: SSO_CONFIRMATION_REQUIRED });
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const user = (await db.select().from(users).where(eq(users.id, identity.userId)))[0];
|
|
25
|
+
if (user && typeof currentPassword === 'string' && currentPassword && await bcrypt.compare(currentPassword, user.passwordHash))
|
|
26
|
+
return true;
|
|
27
|
+
await reply.code(400).send({ error: 'Current password is incorrect' });
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
/** Records a successful SSO confirmation on a live SSO session of that user. False when there is no such session. */
|
|
31
|
+
async function markSessionVerified(sessionId, userId) {
|
|
32
|
+
const updated = await db.update(authSessions).set({ verifiedAt: Date.now() }).where(and(eq(authSessions.id, sessionId), eq(authSessions.userId, userId), eq(authSessions.authMethod, 'oidc'), gt(authSessions.expiresAt, Date.now()))).returning({ id: authSessions.id });
|
|
33
|
+
return updated.length > 0;
|
|
34
|
+
}
|
|
35
|
+
return { confirmIdentity, markSessionVerified };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=identityConfirmation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identityConfirmation.js","sourceRoot":"","sources":["../src/identityConfirmation.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,UAAU,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAIjD,qGAAqG;AACrG,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAA;AAEpE,4FAA4F;AAC5F,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAExD,MAAM,UAAU,0BAA0B,CAAC,GAAgB;IACzD,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAA;IAElB;;;;OAIG;IACH,KAAK,UAAU,eAAe,CAAC,QAAsB,EAAE,eAAwB,EAAE,KAAmB;QAClG,IAAI,QAAQ,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACxG,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,OAAO,EAAE,SAAS,IAAI,CAAC,CAAA;YACjE,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,IAAI,0BAA0B;gBAAE,OAAO,IAAI,CAAA;YACtE,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uDAAuD,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC,CAAA;YAC/H,OAAO,KAAK,CAAA;QACd,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACpF,IAAI,IAAI,IAAI,OAAO,eAAe,KAAK,QAAQ,IAAI,eAAe,IAAI,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAA;QAC3I,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAA;QACtE,OAAO,KAAK,CAAA;IACd,CAAC;IAED,qHAAqH;IACrH,KAAK,UAAU,mBAAmB,CAAC,SAAiB,EAAE,MAAc;QAClE,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CACrF,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,CAAC,EAC9B,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,EACnC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CACvC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,CAAA;QACrC,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;IAC3B,CAAC;IAED,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,CAAA;AACjD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { createAuth, type Auth } from './createAuth.js';
|
|
2
|
+
export { authDbModule } from './module.js';
|
|
3
|
+
export { apiTokens, authSessions, oidcSettings, users } from './tables.js';
|
|
4
|
+
export { acceptApiTokens, belongsTo, registerAdminApi, type AdminRouteGroup } from './routeScope.js';
|
|
5
|
+
export { createSetupRoutes, type SetupRoutesOptions } from './setupRoutes.js';
|
|
6
|
+
export { ADMIN_ROLE, createRoleRegistry, type RoleRegistry } from './roles.js';
|
|
7
|
+
export { createTokenCatalogue, type TokenCatalogue } from './tokenCatalogue.js';
|
|
8
|
+
export type { AuthHooks, AuthOptions, RateLimitConfig, SessionsRevoked, SignInOutcome, UnknownSsoUser } from './context.js';
|
|
9
|
+
export { ALLOW_PENDING_PASSWORD_CHANGE, PASSWORD_CHANGE_REQUIRED, requestIdentity } from './middleware.js';
|
|
10
|
+
export { API_TOKEN_NOT_ACCEPTED, type AuthenticateOptions, type AuthIdentity, type AuthMethod } from './sessions.js';
|
|
11
|
+
export { SSO_CONFIRMATION_REQUIRED, SSO_CONFIRMATION_WINDOW_MS } from './identityConfirmation.js';
|
|
12
|
+
export { checkEmailVerified, OIDC_CONFIRMATION_MAX_AGE_SECONDS } from './oidc.js';
|
|
13
|
+
export type { OidcDecision, OidcDenial, OidcDenialCode, OidcFlowState, OidcTestResult, SsoProvisioning } from './oidc.js';
|
|
14
|
+
export type { OidcSettingsRow } from './oidcSettings.js';
|
|
15
|
+
export { createOidcConfigResolver, DEFAULT_OIDC_BUTTON_LABEL, DEFAULT_OIDC_SCOPES, isHttpUrl, passwordLoginEnabled, type OidcConfig, type OidcSettingsInput } from './oidcConfig.js';
|
|
16
|
+
export { consumeRecoveryCode, generateRecoveryCodes, generateTotpCode, generateTotpSecret, hashRecoveryCode, totpUri, verifyTotp } from './totp.js';
|
|
17
|
+
export type { CredentialDenialCode, SignInDenial, SignInMethod } from './signInAudit.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACpG,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9E,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/E,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC3H,OAAO,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC1G,OAAO,EAAE,sBAAsB,EAAE,KAAK,mBAAmB,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AACpH,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AACjG,OAAO,EAAE,kBAAkB,EAAE,iCAAiC,EAAE,MAAM,WAAW,CAAA;AACjF,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACzH,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,SAAS,EAAE,oBAAoB,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACpL,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACnJ,YAAY,EAAE,oBAAoB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { createAuth } from './createAuth.js';
|
|
2
|
+
export { authDbModule } from './module.js';
|
|
3
|
+
export { apiTokens, authSessions, oidcSettings, users } from './tables.js';
|
|
4
|
+
export { acceptApiTokens, belongsTo, registerAdminApi } from './routeScope.js';
|
|
5
|
+
export { createSetupRoutes } from './setupRoutes.js';
|
|
6
|
+
export { ADMIN_ROLE, createRoleRegistry } from './roles.js';
|
|
7
|
+
export { createTokenCatalogue } from './tokenCatalogue.js';
|
|
8
|
+
export { ALLOW_PENDING_PASSWORD_CHANGE, PASSWORD_CHANGE_REQUIRED, requestIdentity } from './middleware.js';
|
|
9
|
+
export { API_TOKEN_NOT_ACCEPTED } from './sessions.js';
|
|
10
|
+
export { SSO_CONFIRMATION_REQUIRED, SSO_CONFIRMATION_WINDOW_MS } from './identityConfirmation.js';
|
|
11
|
+
export { checkEmailVerified, OIDC_CONFIRMATION_MAX_AGE_SECONDS } from './oidc.js';
|
|
12
|
+
export { createOidcConfigResolver, DEFAULT_OIDC_BUTTON_LABEL, DEFAULT_OIDC_SCOPES, isHttpUrl, passwordLoginEnabled } from './oidcConfig.js';
|
|
13
|
+
export { consumeRecoveryCode, generateRecoveryCodes, generateTotpCode, generateTotpSecret, hashRecoveryCode, totpUri, verifyTotp } from './totp.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,gBAAgB,EAAwB,MAAM,iBAAiB,CAAA;AACpG,OAAO,EAAE,iBAAiB,EAA2B,MAAM,kBAAkB,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAqB,MAAM,YAAY,CAAA;AAC9E,OAAO,EAAE,oBAAoB,EAAuB,MAAM,qBAAqB,CAAA;AAE/E,OAAO,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC1G,OAAO,EAAE,sBAAsB,EAAgE,MAAM,eAAe,CAAA;AACpH,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AACjG,OAAO,EAAE,kBAAkB,EAAE,iCAAiC,EAAE,MAAM,WAAW,CAAA;AAGjF,OAAO,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,SAAS,EAAE,oBAAoB,EAA2C,MAAM,iBAAiB,CAAA;AACpL,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { FastifyReply, FastifyRequest } from 'fastify';
|
|
2
|
+
import { type AuthIdentity, type Sessions } from './sessions.js';
|
|
3
|
+
import type { ApiTokens } from './apiTokens.js';
|
|
4
|
+
declare module 'fastify' {
|
|
5
|
+
interface FastifyContextConfig {
|
|
6
|
+
allowPendingPasswordChange?: boolean;
|
|
7
|
+
/** Routes that accept an API token. Everything else answers 403 to one, so tokens never reach account security. */
|
|
8
|
+
allowApiToken?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* The part of the admin API a route belongs to (a key of TOKEN_RESOURCES, or `vault`). A token needs `<part>:read`
|
|
11
|
+
* for GET and `<part>:write` for anything else. `custom` means the handler checks the token itself. A route a
|
|
12
|
+
* token may reach but that names no part is refused: tokens fail closed.
|
|
13
|
+
*/
|
|
14
|
+
tokenScope?: string;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export declare const PASSWORD_CHANGE_REQUIRED = "PASSWORD_CHANGE_REQUIRED";
|
|
18
|
+
/**
|
|
19
|
+
* Route config for the few routes a user with a pending forced password change may still call
|
|
20
|
+
* (session lookup, change-password, logout). Every other authenticated route answers 403 until
|
|
21
|
+
* the password is changed, so the requirement is enforced server-side, not only by the admin UI.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ALLOW_PENDING_PASSWORD_CHANGE: {
|
|
24
|
+
readonly allowPendingPasswordChange: true;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The identity attached by requireAuth/requireRole. Only call from handlers behind one of them.
|
|
28
|
+
*/
|
|
29
|
+
export declare function requestIdentity(req: FastifyRequest): AuthIdentity;
|
|
30
|
+
export declare function createMiddleware(sessions: Sessions, apiTokens: ApiTokens): {
|
|
31
|
+
authenticateOrReject: (req: FastifyRequest, reply: FastifyReply, unauthorized?: Record<string, string>) => Promise<AuthIdentity | null>;
|
|
32
|
+
requireAuth: ((req: FastifyRequest, reply: FastifyReply) => Promise<undefined>) & {
|
|
33
|
+
requiresAuth: boolean;
|
|
34
|
+
};
|
|
35
|
+
requireRole: (...allowed: string[]) => ((req: FastifyRequest, reply: FastifyReply) => Promise<undefined>) & {
|
|
36
|
+
allowedRoles: string[];
|
|
37
|
+
requiresAuth: boolean;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export type Middleware = ReturnType<typeof createMiddleware>;
|
|
41
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE3D,OAAO,EAA0B,KAAK,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,oBAAoB;QAC5B,0BAA0B,CAAC,EAAE,OAAO,CAAA;QACpC,mHAAmH;QACnH,aAAa,CAAC,EAAE,OAAO,CAAA;QACvB;;;;WAIG;QACH,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB;CACF;AAED,eAAO,MAAM,wBAAwB,6BAA6B,CAAA;AAElE;;;;GAIG;AACH,eAAO,MAAM,6BAA6B;;CAAgD,CAAA;AAO1F;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,YAAY,CAIjE;AAWD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;gCAUhE,cAAc,SACZ,YAAY,iBACL,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACnC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;wBAqCC,cAAc,SAAS,YAAY;;;8BAIlC,MAAM,EAAE,YACd,cAAc,SAAS,YAAY;;;;EAc/D;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAA"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { requiredScope, tokenAllows } from '@better-forge/contracts';
|
|
2
|
+
import { API_TOKEN_NOT_ACCEPTED } from './sessions.js';
|
|
3
|
+
export const PASSWORD_CHANGE_REQUIRED = 'PASSWORD_CHANGE_REQUIRED';
|
|
4
|
+
/**
|
|
5
|
+
* Route config for the few routes a user with a pending forced password change may still call
|
|
6
|
+
* (session lookup, change-password, logout). Every other authenticated route answers 403 until
|
|
7
|
+
* the password is changed, so the requirement is enforced server-side, not only by the admin UI.
|
|
8
|
+
*/
|
|
9
|
+
export const ALLOW_PENDING_PASSWORD_CHANGE = { allowPendingPasswordChange: true };
|
|
10
|
+
function existingIdentity(req) {
|
|
11
|
+
const value = req.user;
|
|
12
|
+
return value?.sessionId && typeof value.userId === 'number' ? value : null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The identity attached by requireAuth/requireRole. Only call from handlers behind one of them.
|
|
16
|
+
*/
|
|
17
|
+
export function requestIdentity(req) {
|
|
18
|
+
const value = req.user;
|
|
19
|
+
if (typeof value?.userId !== 'number')
|
|
20
|
+
throw new Error('requestIdentity called on an unauthenticated request');
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function passwordChangeBlocks(req, identity) {
|
|
24
|
+
if (!identity.mustChangePassword)
|
|
25
|
+
return false;
|
|
26
|
+
return req.routeOptions.config?.allowPendingPasswordChange !== true;
|
|
27
|
+
}
|
|
28
|
+
function sendPasswordChangeRequired(reply) {
|
|
29
|
+
return reply.code(403).send({ error: 'Password change required', code: PASSWORD_CHANGE_REQUIRED });
|
|
30
|
+
}
|
|
31
|
+
export function createMiddleware(sessions, apiTokens) {
|
|
32
|
+
const { authenticateRequest, verifyCsrf } = sessions;
|
|
33
|
+
const { consumeApiTokenBudget } = apiTokens;
|
|
34
|
+
/**
|
|
35
|
+
* Authenticates the request and checks its CSRF token. Answers 401 (with `unauthorized` as the body) or 403
|
|
36
|
+
* itself and returns null when either fails, or when the user still has to replace a temporary password.
|
|
37
|
+
* A hook that gets null must return the reply.
|
|
38
|
+
*/
|
|
39
|
+
async function authenticateOrReject(req, reply, unauthorized = { error: 'Unauthorized' }) {
|
|
40
|
+
const allowApiToken = req.routeOptions.config?.allowApiToken === true;
|
|
41
|
+
const existing = existingIdentity(req);
|
|
42
|
+
let identity;
|
|
43
|
+
try {
|
|
44
|
+
identity = existing ?? await authenticateRequest(req, { allowApiToken });
|
|
45
|
+
if (identity.apiToken && !allowApiToken)
|
|
46
|
+
throw new Error(API_TOKEN_NOT_ACCEPTED);
|
|
47
|
+
await verifyCsrf(req, identity);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const message = error instanceof Error ? error.message : '';
|
|
51
|
+
if (message === 'Invalid CSRF token')
|
|
52
|
+
reply.code(403).send({ error: 'Invalid CSRF token' });
|
|
53
|
+
else if (message === API_TOKEN_NOT_ACCEPTED)
|
|
54
|
+
reply.code(403).send({ error: 'API tokens cannot call this endpoint' });
|
|
55
|
+
else
|
|
56
|
+
reply.code(401).send(unauthorized);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// Only a request that just authenticated spends budget; the later hooks of the same request reuse the identity.
|
|
60
|
+
if (identity.apiToken && !existing && !consumeApiTokenBudget(identity.apiToken.id)) {
|
|
61
|
+
reply.code(429).header('retry-after', '60').send({ error: 'Too many requests' });
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (identity.apiToken) {
|
|
65
|
+
const resource = req.routeOptions.config?.tokenScope;
|
|
66
|
+
if (resource !== 'custom') {
|
|
67
|
+
const required = resource ? requiredScope(resource, req.method) : null;
|
|
68
|
+
if (!required || !tokenAllows(identity.apiToken.scopes, required)) {
|
|
69
|
+
reply.code(403).send({ error: required ? `This token does not have the "${required}" permission` : 'API tokens cannot call this endpoint' });
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (passwordChangeBlocks(req, identity)) {
|
|
75
|
+
sendPasswordChangeRequired(reply);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return identity;
|
|
79
|
+
}
|
|
80
|
+
async function requireAuth(req, reply) {
|
|
81
|
+
if (!await authenticateOrReject(req, reply))
|
|
82
|
+
return reply;
|
|
83
|
+
}
|
|
84
|
+
function requireRole(...allowed) {
|
|
85
|
+
const hook = async (req, reply) => {
|
|
86
|
+
const identity = await authenticateOrReject(req, reply);
|
|
87
|
+
if (!identity)
|
|
88
|
+
return reply;
|
|
89
|
+
const { role } = identity;
|
|
90
|
+
if (role !== 'admin' && !allowed.includes(role)) {
|
|
91
|
+
return reply.code(403).send({ error: 'Forbidden' });
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
// Readable by the route-table snapshot test; the hook itself never looks at it.
|
|
95
|
+
return Object.assign(hook, { allowedRoles: allowed, requiresAuth: true });
|
|
96
|
+
}
|
|
97
|
+
// Readable by the route-table snapshot test, like `allowedRoles`; the hook itself never looks at it.
|
|
98
|
+
return { authenticateOrReject, requireAuth: Object.assign(requireAuth, { requiresAuth: true }), requireRole };
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAErD,qFAAqF;AACrF,eAAO,MAAM,cAAc,EAAE,SAAS,aAAa,EAuElD,CAAA"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import bcrypt from 'bcryptjs';
|
|
3
|
+
/** Users, sign-in sessions, SSO settings and API tokens. Steps run in this order. */
|
|
4
|
+
export const authMigrations = [
|
|
5
|
+
{ kind: 'exec', sql: `
|
|
6
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
7
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
8
|
+
email TEXT NOT NULL UNIQUE,
|
|
9
|
+
password_hash TEXT NOT NULL,
|
|
10
|
+
role TEXT NOT NULL DEFAULT 'admin',
|
|
11
|
+
created_at INTEGER NOT NULL
|
|
12
|
+
);
|
|
13
|
+
` },
|
|
14
|
+
{ kind: 'exec', sql: `
|
|
15
|
+
CREATE TABLE IF NOT EXISTS oidc_settings (
|
|
16
|
+
id INTEGER PRIMARY KEY,
|
|
17
|
+
enabled INTEGER NOT NULL DEFAULT 0,
|
|
18
|
+
issuer TEXT NOT NULL DEFAULT '',
|
|
19
|
+
client_id TEXT NOT NULL DEFAULT '',
|
|
20
|
+
client_secret TEXT NOT NULL DEFAULT '',
|
|
21
|
+
scopes TEXT NOT NULL DEFAULT '',
|
|
22
|
+
redirect_uri TEXT NOT NULL DEFAULT '',
|
|
23
|
+
button_label TEXT NOT NULL DEFAULT '',
|
|
24
|
+
allow_unverified_email INTEGER NOT NULL DEFAULT 0,
|
|
25
|
+
disable_password_login INTEGER NOT NULL DEFAULT 0,
|
|
26
|
+
updated_at INTEGER NOT NULL
|
|
27
|
+
);
|
|
28
|
+
` },
|
|
29
|
+
{ kind: 'exec', sql: `
|
|
30
|
+
CREATE TABLE IF NOT EXISTS auth_sessions (
|
|
31
|
+
id TEXT PRIMARY KEY,
|
|
32
|
+
user_id INTEGER NOT NULL,
|
|
33
|
+
csrf_token_hash TEXT NOT NULL,
|
|
34
|
+
created_at INTEGER NOT NULL,
|
|
35
|
+
last_seen_at INTEGER NOT NULL,
|
|
36
|
+
expires_at INTEGER NOT NULL,
|
|
37
|
+
auth_method TEXT NOT NULL DEFAULT 'password',
|
|
38
|
+
verified_at INTEGER,
|
|
39
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
40
|
+
);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_auth_sessions_user ON auth_sessions(user_id);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_auth_sessions_expires ON auth_sessions(expires_at);
|
|
43
|
+
` },
|
|
44
|
+
{ kind: 'exec', sql: `
|
|
45
|
+
CREATE TABLE IF NOT EXISTS api_tokens (
|
|
46
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
47
|
+
name TEXT NOT NULL,
|
|
48
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
49
|
+
prefix TEXT NOT NULL,
|
|
50
|
+
created_at INTEGER NOT NULL,
|
|
51
|
+
expires_at INTEGER,
|
|
52
|
+
last_used_at INTEGER,
|
|
53
|
+
scopes TEXT NOT NULL
|
|
54
|
+
);
|
|
55
|
+
` },
|
|
56
|
+
{ kind: 'column', sql: `ALTER TABLE users ADD COLUMN must_change_password INTEGER NOT NULL DEFAULT 0`, desc: 'users.must_change_password' },
|
|
57
|
+
{ kind: 'column', sql: `ALTER TABLE users ADD COLUMN totp_secret TEXT`, desc: 'users.totp_secret' },
|
|
58
|
+
{ kind: 'column', sql: `ALTER TABLE users ADD COLUMN totp_enabled INTEGER NOT NULL DEFAULT 0`, desc: 'users.totp_enabled' },
|
|
59
|
+
{ kind: 'column', sql: `ALTER TABLE users ADD COLUMN totp_recovery_codes TEXT`, desc: 'users.totp_recovery_codes' },
|
|
60
|
+
{ kind: 'column', sql: `ALTER TABLE auth_sessions ADD COLUMN auth_method TEXT NOT NULL DEFAULT 'password'`, desc: 'auth_sessions.auth_method' },
|
|
61
|
+
{ kind: 'column', sql: `ALTER TABLE users ADD COLUMN oidc_issuer TEXT`, desc: 'users.oidc_issuer' },
|
|
62
|
+
{ kind: 'column', sql: `ALTER TABLE users ADD COLUMN oidc_subject TEXT`, desc: 'users.oidc_subject' },
|
|
63
|
+
{ kind: 'column', sql: `ALTER TABLE auth_sessions ADD COLUMN verified_at INTEGER`, desc: 'auth_sessions.verified_at' },
|
|
64
|
+
// One IdP identity can be bound to one user only. Needs the columns added above.
|
|
65
|
+
{ kind: 'exec', sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_users_oidc_identity ON users(oidc_issuer, oidc_subject)` },
|
|
66
|
+
/**
|
|
67
|
+
* Accounts linked to SSO before a sign-in started revoking the temporary password may still have a working one
|
|
68
|
+
* that their administrator knows. Linking only happens on an SSO sign-in, so revoke it the same way.
|
|
69
|
+
*/
|
|
70
|
+
{ kind: 'data', name: 'sso-users-temporary-password-revoked-v1', run(sqlite) {
|
|
71
|
+
const pending = sqlite.prepare('SELECT id FROM users WHERE must_change_password = 1 AND oidc_subject IS NOT NULL').all();
|
|
72
|
+
const revoke = sqlite.prepare('UPDATE users SET password_hash = ?, must_change_password = 0 WHERE id = ?');
|
|
73
|
+
for (const { id } of pending)
|
|
74
|
+
revoke.run(bcrypt.hashSync(randomBytes(32).toString('base64url'), 10), id);
|
|
75
|
+
} },
|
|
76
|
+
];
|
|
77
|
+
//# sourceMappingURL=migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,MAAM,MAAM,UAAU,CAAA;AAG7B,qFAAqF;AACrF,MAAM,CAAC,MAAM,cAAc,GAA6B;IACtD,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;;;;;;;;CAQtB,EAAE;IACD,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;;;;;;;;;;;;;;CActB,EAAE;IACD,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;;;;;;;;;;;;;;CActB,EAAE;IACD,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;;;;;;;;;;;CAWtB,EAAE;IACD,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,8EAA8E,EAAE,IAAI,EAAE,4BAA4B,EAAE;IAC3I,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,+CAA+C,EAAE,IAAI,EAAE,mBAAmB,EAAE;IACnG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,sEAAsE,EAAE,IAAI,EAAE,oBAAoB,EAAE;IAC3H,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,uDAAuD,EAAE,IAAI,EAAE,2BAA2B,EAAE;IACnH,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,mFAAmF,EAAE,IAAI,EAAE,2BAA2B,EAAE;IAC/I,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,+CAA+C,EAAE,IAAI,EAAE,mBAAmB,EAAE;IACnG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,gDAAgD,EAAE,IAAI,EAAE,oBAAoB,EAAE;IACrG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,0DAA0D,EAAE,IAAI,EAAE,2BAA2B,EAAE;IACtH,iFAAiF;IACjF,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,+FAA+F,EAAE;IACtH;;;OAGG;IACH,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yCAAyC,EAAE,GAAG,CAAC,MAAM;YACzE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,kFAAkF,CAAC,CAAC,GAAG,EAA2B,CAAA;YACjJ,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,2EAA2E,CAAC,CAAA;YAC1G,KAAK,MAAM,EAAE,EAAE,EAAE,IAAI,OAAO;gBAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;QAC1G,CAAC,EAAE;CACJ,CAAA"}
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as tables from './tables.js';
|
|
2
|
+
/** The tables of accounts, sessions, API tokens and SSO settings, with their migrations, for the application's list of database modules. */
|
|
3
|
+
export declare const authDbModule: {
|
|
4
|
+
id: string;
|
|
5
|
+
tables: typeof tables;
|
|
6
|
+
migrations: readonly import("@better-forge/db").MigrationStep[];
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AAGrC,4IAA4I;AAC5I,eAAO,MAAM,YAAY;;;;CAAwE,CAAA"}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as tables from './tables.js';
|
|
2
|
+
import { authMigrations } from './migrations.js';
|
|
3
|
+
/** The tables of accounts, sessions, API tokens and SSO settings, with their migrations, for the application's list of database modules. */
|
|
4
|
+
export const authDbModule = { id: 'auth', tables, migrations: authMigrations };
|
|
5
|
+
//# sourceMappingURL=module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,4IAA4I;AAC5I,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAqB,CAAA"}
|