@azlib/identity 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 (69) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +387 -0
  3. package/dist/errors-BGMwaW5s.d.mts +481 -0
  4. package/dist/errors-BGMwaW5s.d.mts.map +1 -0
  5. package/dist/errors-Bcjx9o6g.cjs +111 -0
  6. package/dist/errors-C2xZAatu.d.cts +481 -0
  7. package/dist/errors-C2xZAatu.d.cts.map +1 -0
  8. package/dist/errors-CEmnZxIn.mjs +66 -0
  9. package/dist/errors-CEmnZxIn.mjs.map +1 -0
  10. package/dist/express.cjs +405 -0
  11. package/dist/express.d.cts +87 -0
  12. package/dist/express.d.cts.map +1 -0
  13. package/dist/express.d.mts +87 -0
  14. package/dist/express.d.mts.map +1 -0
  15. package/dist/express.mjs +401 -0
  16. package/dist/express.mjs.map +1 -0
  17. package/dist/identity-4eP45YIP.cjs +461 -0
  18. package/dist/identity-Bz9RDOvT.mjs +410 -0
  19. package/dist/identity-Bz9RDOvT.mjs.map +1 -0
  20. package/dist/identity-router-DBL20UWT.d.mts +115 -0
  21. package/dist/identity-router-DBL20UWT.d.mts.map +1 -0
  22. package/dist/identity-router-Dib30Waj.d.cts +115 -0
  23. package/dist/identity-router-Dib30Waj.d.cts.map +1 -0
  24. package/dist/identity-service-B9zrvE9z.d.mts +128 -0
  25. package/dist/identity-service-B9zrvE9z.d.mts.map +1 -0
  26. package/dist/identity-service-CLzKx8Z7.d.cts +128 -0
  27. package/dist/identity-service-CLzKx8Z7.d.cts.map +1 -0
  28. package/dist/identity-store-BRRahxcS.d.cts +272 -0
  29. package/dist/identity-store-BRRahxcS.d.cts.map +1 -0
  30. package/dist/identity-store-BRRahxcS.d.mts +272 -0
  31. package/dist/identity-store-BRRahxcS.d.mts.map +1 -0
  32. package/dist/index-CpufYgyn.d.cts +30 -0
  33. package/dist/index-CpufYgyn.d.cts.map +1 -0
  34. package/dist/index-CpufYgyn.d.mts +30 -0
  35. package/dist/index-CpufYgyn.d.mts.map +1 -0
  36. package/dist/index.cjs +18 -0
  37. package/dist/index.d.cts +4 -0
  38. package/dist/index.d.mts +4 -0
  39. package/dist/index.mjs +3 -0
  40. package/dist/logger-Be1wDzBC.cjs +48 -0
  41. package/dist/logger-CcCHJVVe.mjs +33 -0
  42. package/dist/logger-CcCHJVVe.mjs.map +1 -0
  43. package/dist/nestjs.cjs +516 -0
  44. package/dist/nestjs.d.cts +102 -0
  45. package/dist/nestjs.d.cts.map +1 -0
  46. package/dist/nestjs.d.mts +102 -0
  47. package/dist/nestjs.d.mts.map +1 -0
  48. package/dist/nestjs.mjs +500 -0
  49. package/dist/nestjs.mjs.map +1 -0
  50. package/dist/node.cjs +946 -0
  51. package/dist/node.d.cts +260 -0
  52. package/dist/node.d.cts.map +1 -0
  53. package/dist/node.d.mts +260 -0
  54. package/dist/node.d.mts.map +1 -0
  55. package/dist/node.mjs +890 -0
  56. package/dist/node.mjs.map +1 -0
  57. package/dist/test-utils.cjs +169 -0
  58. package/dist/test-utils.d.cts +12 -0
  59. package/dist/test-utils.d.cts.map +1 -0
  60. package/dist/test-utils.d.mts +12 -0
  61. package/dist/test-utils.d.mts.map +1 -0
  62. package/dist/test-utils.mjs +170 -0
  63. package/dist/test-utils.mjs.map +1 -0
  64. package/package.json +92 -0
  65. package/schema/model.ts +100 -0
  66. package/schema/mysql.sql +102 -0
  67. package/schema/postgres.sql +92 -0
  68. package/schema/prisma.schema +122 -0
  69. package/schema/sqlite.sql +92 -0
@@ -0,0 +1,405 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_errors = require("./errors-Bcjx9o6g.cjs");
3
+ const require_logger = require("./logger-Be1wDzBC.cjs");
4
+ let express = require("express");
5
+ //#region core/express/middleware.ts
6
+ const extractBearerToken = (req) => {
7
+ const header = req.headers.authorization;
8
+ if (!header || !header.startsWith("Bearer ")) return null;
9
+ const token = header.slice(7).trim();
10
+ return token.length > 0 ? token : null;
11
+ };
12
+ /**
13
+ * Express middleware that authenticates the request using a Bearer access token and
14
+ * attaches the hydrated principal to `req.identity`. Forwards identity errors to the
15
+ * error-handling middleware.
16
+ */
17
+ function requireAuth(service) {
18
+ return async (req, _res, next) => {
19
+ try {
20
+ const token = extractBearerToken(req);
21
+ if (!token) throw new require_errors.UnauthenticatedError();
22
+ req.identity = await service.authenticate(token);
23
+ next();
24
+ } catch (error) {
25
+ next(error);
26
+ }
27
+ };
28
+ }
29
+ /**
30
+ * Express middleware enforcing an authorization requirement (deny-by-default). Must run
31
+ * after {@link requireAuth}. Optionally loads a resource for ownership policies.
32
+ */
33
+ function requireAuthorization(service, requirement, loadResource) {
34
+ return async (req, _res, next) => {
35
+ try {
36
+ const principal = req.identity;
37
+ if (!principal) throw new require_errors.UnauthenticatedError();
38
+ const resource = loadResource ? await loadResource(req) : void 0;
39
+ if (!(await service.authorize(requirement, {
40
+ principal,
41
+ action: requirement.permission ?? "custom",
42
+ resource
43
+ })).allowed) throw new require_errors.ForbiddenError();
44
+ next();
45
+ } catch (error) {
46
+ next(error);
47
+ }
48
+ };
49
+ }
50
+ /** Convenience: require a single permission. */
51
+ function requirePermission(service, permission) {
52
+ return requireAuthorization(service, { permission });
53
+ }
54
+ //#endregion
55
+ //#region core/express/oauth-routes.ts
56
+ /**
57
+ * Returns an Express route handler that redirects the user to the OAuth provider's
58
+ * authorization page. Generates and persists a CSRF state value via `options.setState`.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * app.get("/auth/google", oauthAuthorize(identity.oauth!, "google", {
63
+ * redirectUri: `${BASE_URL}/auth/google/callback`,
64
+ * getState: (req) => req.session?.oauthState ?? null,
65
+ * setState: (req, _res, state) => { req.session!.oauthState = state; },
66
+ * onSuccess: (_req, res, result) => res.json(result),
67
+ * }));
68
+ * ```
69
+ */
70
+ function oauthAuthorize(oauthService, providerName, options) {
71
+ return async (req, res, next) => {
72
+ try {
73
+ const { url, state } = oauthService.buildAuthorizationUrl(providerName, options.redirectUri);
74
+ await options.setState(req, res, state);
75
+ res.redirect(url);
76
+ } catch (error) {
77
+ next(error);
78
+ }
79
+ };
80
+ }
81
+ /**
82
+ * Returns an Express route handler that handles the OAuth provider callback. Verifies
83
+ * state, exchanges the code, and calls `options.onSuccess` with the auth result.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * app.get("/auth/google/callback", oauthCallback(identity.oauth!, "google", {
88
+ * redirectUri: `${BASE_URL}/auth/google/callback`,
89
+ * getState: (req) => req.session?.oauthState ?? null,
90
+ * setState: (req, _res, state) => { req.session!.oauthState = state; },
91
+ * onSuccess: (_req, res, result) => res.json(result),
92
+ * }));
93
+ * ```
94
+ */
95
+ function oauthCallback(oauthService, providerName, options) {
96
+ return async (req, res, next) => {
97
+ try {
98
+ const code = typeof req.query["code"] === "string" ? req.query["code"] : null;
99
+ const state = typeof req.query["state"] === "string" ? req.query["state"] : null;
100
+ const error = typeof req.query["error"] === "string" ? req.query["error"] : null;
101
+ if (error) throw new require_errors.IdentityError("identity/oauth-error", `Provider error: ${typeof req.query["error_description"] === "string" ? req.query["error_description"] : error}`, 400);
102
+ if (!code || !state) throw new require_errors.IdentityError("identity/oauth-invalid-callback", "Missing code or state in OAuth callback.", 400);
103
+ const expectedState = await options.getState(req);
104
+ if (!expectedState) throw new require_errors.IdentityError("identity/oauth-state-missing", "No OAuth state found in session. The request may have expired.", 400);
105
+ const result = await oauthService.handleCallback(providerName, {
106
+ code,
107
+ state,
108
+ expectedState,
109
+ redirectUri: options.redirectUri
110
+ });
111
+ await options.onSuccess(req, res, result);
112
+ } catch (error) {
113
+ next(error);
114
+ }
115
+ };
116
+ }
117
+ //#endregion
118
+ //#region core/express/identity-router.ts
119
+ const OAUTH_STATE_COOKIE = "azlib_oauth_state";
120
+ function resolveCookieName(opts) {
121
+ return opts.name ?? "azlib_rt";
122
+ }
123
+ /** Parses cookies from the raw `Cookie` header without a dependency on `cookie-parser`. */
124
+ function readCookie(req, name) {
125
+ const raw = req.headers.cookie ?? "";
126
+ for (const part of raw.split(";")) {
127
+ const eq = part.indexOf("=");
128
+ if (eq === -1) continue;
129
+ if (part.slice(0, eq).trim() === name) try {
130
+ return decodeURIComponent(part.slice(eq + 1).trim());
131
+ } catch {
132
+ return part.slice(eq + 1).trim();
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * Writes the refresh token into a `Set-Cookie` header and returns a body-safe tokens
138
+ * object that omits the refresh token.
139
+ */
140
+ function setCookieAndStripRefreshToken(res, tokens, opts) {
141
+ const maxAge = tokens.refreshTokenExpiresAt.getTime() - Date.now();
142
+ res.cookie(resolveCookieName(opts), tokens.refreshToken, {
143
+ httpOnly: opts.httpOnly ?? true,
144
+ secure: opts.secure ?? process.env.NODE_ENV === "production",
145
+ sameSite: opts.sameSite ?? "lax",
146
+ path: opts.path ?? "/",
147
+ domain: opts.domain,
148
+ maxAge
149
+ });
150
+ const { refreshToken: _rt, refreshTokenExpiresAt: _exp, ...bodyTokens } = tokens;
151
+ return bodyTokens;
152
+ }
153
+ /**
154
+ * Sends the authentication result as JSON. In cookie mode the refresh token is stored
155
+ * in a `Set-Cookie` header and excluded from the body.
156
+ *
157
+ * When the result is an MFA challenge (`{ kind: "mfa_required" }`), it is forwarded as-is
158
+ * with HTTP 200 so the client knows to complete the TOTP step.
159
+ */
160
+ function sendLoginResult(res, result, cookieOpts) {
161
+ if ("mfaToken" in result) {
162
+ res.json(result);
163
+ return;
164
+ }
165
+ sendAuthResult(res, result, cookieOpts);
166
+ }
167
+ function sendAuthResult(res, result, cookieOpts) {
168
+ if (cookieOpts) {
169
+ const tokens = setCookieAndStripRefreshToken(res, result.tokens, cookieOpts);
170
+ res.json({
171
+ user: result.user,
172
+ tokens
173
+ });
174
+ } else res.json(result);
175
+ }
176
+ /**
177
+ * Reads the refresh token from the request. In cookie mode it is read from the cookie;
178
+ * in body mode it is expected in `req.body.refreshToken`.
179
+ */
180
+ function readRefreshToken(req, cookieOpts) {
181
+ if (cookieOpts) return readCookie(req, resolveCookieName(cookieOpts));
182
+ const token = req.body?.["refreshToken"];
183
+ return typeof token === "string" ? token : void 0;
184
+ }
185
+ /**
186
+ * Creates a pre-wired Express `Router` with all standard identity endpoints.
187
+ *
188
+ * Mount it once on your application:
189
+ * ```ts
190
+ * import express from "express";
191
+ * import { createIdentityService } from "@azlib/identity/node";
192
+ * import { createIdentityRouter, identityErrorHandler } from "@azlib/identity/express";
193
+ *
194
+ * const service = createIdentityService(config, store);
195
+ * const app = express();
196
+ *
197
+ * app.use(express.json());
198
+ * // Routes are served at /account/login, /account/register, etc. (default prefix)
199
+ * app.use(createIdentityRouter(service));
200
+ * // Or use a custom prefix:
201
+ * app.use(createIdentityRouter(service, { prefix: "auth" }));
202
+ * app.use(identityErrorHandler()); // optional convenience error handler
203
+ * ```
204
+ *
205
+ * Pre-wired routes:
206
+ *
207
+ * | Method | Path | Description |
208
+ * |--------|------|-------------|
209
+ * | POST | `/register` | Create a new account |
210
+ * | POST | `/login` | Email + password login |
211
+ * | POST | `/refresh` | Rotate the refresh token |
212
+ * | POST | `/logout` | Revoke the current session |
213
+ * | GET | `/me` | Return the authenticated principal |
214
+ * | GET | `/:provider` | Start an OAuth 2.0 authorisation flow *(optional)* |
215
+ * | GET | `/:provider/callback` | Handle an OAuth 2.0 callback *(optional)* |
216
+ */
217
+ function createIdentityRouter(service, options = {}) {
218
+ const prefix = options.prefix ?? "account";
219
+ const log = require_logger.resolveLogger(options.logger);
220
+ const router = (0, express.Router)();
221
+ const cookieOpts = typeof options.refreshToken === "object" && "cookie" in options.refreshToken ? options.refreshToken.cookie : void 0;
222
+ router.use((_req, res, next) => {
223
+ const start = Date.now();
224
+ res.on("finish", () => {
225
+ const ms = Date.now() - start;
226
+ log.info(`${_req.method} ${_req.originalUrl} ${res.statusCode}`, { durationMs: ms });
227
+ });
228
+ next();
229
+ });
230
+ router.post("/register", async (req, res, next) => {
231
+ try {
232
+ sendAuthResult(res, await service.register(req.body), cookieOpts);
233
+ } catch (err) {
234
+ next(err);
235
+ }
236
+ });
237
+ router.post("/login", async (req, res, next) => {
238
+ try {
239
+ sendLoginResult(res, await service.login(req.body), cookieOpts);
240
+ } catch (err) {
241
+ next(err);
242
+ }
243
+ });
244
+ router.post("/refresh", async (req, res, next) => {
245
+ try {
246
+ const token = readRefreshToken(req, cookieOpts);
247
+ if (!token) throw new require_errors.UnauthenticatedError("Refresh token is missing.");
248
+ sendAuthResult(res, await service.refresh(token), cookieOpts);
249
+ } catch (err) {
250
+ next(err);
251
+ }
252
+ });
253
+ router.post("/logout", async (req, res, next) => {
254
+ try {
255
+ const token = readRefreshToken(req, cookieOpts);
256
+ if (token) await service.logout(token);
257
+ if (cookieOpts) res.clearCookie(resolveCookieName(cookieOpts), { path: cookieOpts.path ?? "/" });
258
+ res.status(204).end();
259
+ } catch (err) {
260
+ next(err);
261
+ }
262
+ });
263
+ router.get("/me", requireAuth(service), (_req, res) => {
264
+ res.json(_req.identity);
265
+ });
266
+ router.post("/request-email-verification", requireAuth(service), async (req, res, next) => {
267
+ try {
268
+ await service.requestEmailVerification(req.identity.userId);
269
+ res.status(204).end();
270
+ } catch (err) {
271
+ next(err);
272
+ }
273
+ });
274
+ router.post("/verify-email", async (req, res, next) => {
275
+ try {
276
+ const { token } = req.body;
277
+ if (!token) throw new require_errors.UnauthenticatedError("token is required.");
278
+ await service.verifyEmail(token);
279
+ res.status(204).end();
280
+ } catch (err) {
281
+ next(err);
282
+ }
283
+ });
284
+ router.post("/request-password-reset", async (req, res, next) => {
285
+ try {
286
+ const { email } = req.body;
287
+ if (!email) throw new require_errors.UnauthenticatedError("email is required.");
288
+ await service.requestPasswordReset(email);
289
+ res.status(204).end();
290
+ } catch (err) {
291
+ next(err);
292
+ }
293
+ });
294
+ router.post("/reset-password", async (req, res, next) => {
295
+ try {
296
+ const { token, newPassword } = req.body;
297
+ if (!token || !newPassword) throw new require_errors.UnauthenticatedError("token and newPassword are required.");
298
+ await service.resetPassword(token, newPassword);
299
+ res.status(204).end();
300
+ } catch (err) {
301
+ next(err);
302
+ }
303
+ });
304
+ router.post("/mfa/verify", async (req, res, next) => {
305
+ try {
306
+ const { mfaToken, code } = req.body;
307
+ if (!mfaToken || !code) throw new require_errors.UnauthenticatedError("mfaToken and code are required.");
308
+ sendAuthResult(res, await service.verifyMfaChallenge(mfaToken, code), cookieOpts);
309
+ } catch (err) {
310
+ next(err);
311
+ }
312
+ });
313
+ router.post("/2fa/setup", requireAuth(service), async (req, res, next) => {
314
+ try {
315
+ const result = await service.setup2FA(req.identity.userId);
316
+ res.json(result);
317
+ } catch (err) {
318
+ next(err);
319
+ }
320
+ });
321
+ router.post("/2fa/enable", requireAuth(service), async (req, res, next) => {
322
+ try {
323
+ const { code } = req.body;
324
+ if (!code) throw new require_errors.UnauthenticatedError("code is required.");
325
+ await service.enable2FA(req.identity.userId, code);
326
+ res.status(204).end();
327
+ } catch (err) {
328
+ next(err);
329
+ }
330
+ });
331
+ router.post("/2fa/disable", requireAuth(service), async (req, res, next) => {
332
+ try {
333
+ const { code } = req.body;
334
+ if (!code) throw new require_errors.UnauthenticatedError("code is required.");
335
+ await service.disable2FA(req.identity.userId, code);
336
+ res.status(204).end();
337
+ } catch (err) {
338
+ next(err);
339
+ }
340
+ });
341
+ router.post("/admin/lock/:userId", async (req, res, next) => {
342
+ try {
343
+ await service.lockAccount(req.params["userId"]);
344
+ res.status(204).end();
345
+ } catch (err) {
346
+ next(err);
347
+ }
348
+ });
349
+ router.post("/admin/unlock/:userId", async (req, res, next) => {
350
+ try {
351
+ await service.unlockAccount(req.params["userId"]);
352
+ res.status(204).end();
353
+ } catch (err) {
354
+ next(err);
355
+ }
356
+ });
357
+ router.post("/admin/disable/:userId", async (req, res, next) => {
358
+ try {
359
+ await service.disableAccount(req.params["userId"]);
360
+ res.status(204).end();
361
+ } catch (err) {
362
+ next(err);
363
+ }
364
+ });
365
+ if (service.oauth) {
366
+ const oauthService = service.oauth;
367
+ const baseUrl = options.oauthBaseUrl ?? "";
368
+ const stateCookieOptions = {
369
+ httpOnly: true,
370
+ sameSite: "lax",
371
+ secure: process.env.NODE_ENV === "production",
372
+ maxAge: 600 * 1e3
373
+ };
374
+ for (const providerName of oauthService.providers) {
375
+ const redirectUri = `${baseUrl}/${providerName}/callback`;
376
+ router.get(`/${providerName}`, oauthAuthorize(oauthService, providerName, {
377
+ redirectUri,
378
+ setState: (_req, oauthRes, state) => {
379
+ oauthRes.cookie(OAUTH_STATE_COOKIE, state, stateCookieOptions);
380
+ }
381
+ }));
382
+ router.get(`/${providerName}/callback`, oauthCallback(oauthService, providerName, {
383
+ redirectUri,
384
+ getState: (oauthReq) => readCookie(oauthReq, OAUTH_STATE_COOKIE) ?? null,
385
+ setState: (_oauthReq, oauthRes, _state) => {
386
+ oauthRes.clearCookie(OAUTH_STATE_COOKIE);
387
+ },
388
+ onSuccess: (_oauthReq, oauthRes, result) => {
389
+ sendAuthResult(oauthRes, result, cookieOpts);
390
+ }
391
+ }));
392
+ }
393
+ }
394
+ const outer = (0, express.Router)();
395
+ if (prefix) outer.use(`/${prefix}`, router);
396
+ else outer.use(router);
397
+ return outer;
398
+ }
399
+ //#endregion
400
+ exports.createIdentityRouter = createIdentityRouter;
401
+ exports.oauthAuthorize = oauthAuthorize;
402
+ exports.oauthCallback = oauthCallback;
403
+ exports.requireAuth = requireAuth;
404
+ exports.requireAuthorization = requireAuthorization;
405
+ exports.requirePermission = requirePermission;
@@ -0,0 +1,87 @@
1
+ import { c as AuthResult, d as AuthenticatedIdentity } from "./identity-store-BRRahxcS.cjs";
2
+ import { V as AuthorizationRequirement, f as OAuthService } from "./errors-C2xZAatu.cjs";
3
+ import { t as IdentityService } from "./identity-service-CLzKx8Z7.cjs";
4
+ import { n as IdentityRouterOptions, r as createIdentityRouter, t as CookieRefreshOptions } from "./identity-router-Dib30Waj.cjs";
5
+ import { Request, RequestHandler, Response } from "express";
6
+
7
+ //#region core/express/middleware.d.ts
8
+ declare global {
9
+ namespace Express {
10
+ interface Request {
11
+ /** The authenticated principal, set by {@link requireAuth}. */
12
+ identity?: AuthenticatedIdentity;
13
+ }
14
+ }
15
+ }
16
+ /** Loads the resource a policy will be evaluated against. */
17
+ type ResourceLoader<TResource> = (req: Request) => TResource | Promise<TResource>;
18
+ /**
19
+ * Express middleware that authenticates the request using a Bearer access token and
20
+ * attaches the hydrated principal to `req.identity`. Forwards identity errors to the
21
+ * error-handling middleware.
22
+ */
23
+ declare function requireAuth(service: IdentityService): RequestHandler;
24
+ /**
25
+ * Express middleware enforcing an authorization requirement (deny-by-default). Must run
26
+ * after {@link requireAuth}. Optionally loads a resource for ownership policies.
27
+ */
28
+ declare function requireAuthorization<TResource = unknown>(service: IdentityService, requirement: AuthorizationRequirement<TResource>, loadResource?: ResourceLoader<TResource>): RequestHandler;
29
+ /** Convenience: require a single permission. */
30
+ declare function requirePermission(service: IdentityService, permission: string): RequestHandler;
31
+ //#endregion
32
+ //#region core/express/oauth-routes.d.ts
33
+ /**
34
+ * Options for {@link oauthAuthorize} and {@link oauthCallback}.
35
+ */
36
+ interface OAuthRouteOptions {
37
+ /** The full redirect URI registered with the provider (must match exactly). */
38
+ redirectUri: string;
39
+ /**
40
+ * Retrieves the stored CSRF state value from the current request context (e.g. from a
41
+ * signed cookie or server session). Return `null` if no state has been stored yet.
42
+ */
43
+ getState(req: Request): string | null | Promise<string | null>;
44
+ /**
45
+ * Persists the generated CSRF state value before redirecting the user to the provider.
46
+ * Use a signed cookie or server session.
47
+ */
48
+ setState(req: Request, res: Response, state: string): void | Promise<void>;
49
+ /**
50
+ * Called on a successful OAuth callback with the auth result. Typically sets a session
51
+ * cookie and redirects to the app.
52
+ */
53
+ onSuccess(req: Request, res: Response, result: AuthResult): void | Promise<void>;
54
+ }
55
+ /**
56
+ * Returns an Express route handler that redirects the user to the OAuth provider's
57
+ * authorization page. Generates and persists a CSRF state value via `options.setState`.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * app.get("/auth/google", oauthAuthorize(identity.oauth!, "google", {
62
+ * redirectUri: `${BASE_URL}/auth/google/callback`,
63
+ * getState: (req) => req.session?.oauthState ?? null,
64
+ * setState: (req, _res, state) => { req.session!.oauthState = state; },
65
+ * onSuccess: (_req, res, result) => res.json(result),
66
+ * }));
67
+ * ```
68
+ */
69
+ declare function oauthAuthorize(oauthService: OAuthService, providerName: string, options: Pick<OAuthRouteOptions, "redirectUri" | "setState">): RequestHandler;
70
+ /**
71
+ * Returns an Express route handler that handles the OAuth provider callback. Verifies
72
+ * state, exchanges the code, and calls `options.onSuccess` with the auth result.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * app.get("/auth/google/callback", oauthCallback(identity.oauth!, "google", {
77
+ * redirectUri: `${BASE_URL}/auth/google/callback`,
78
+ * getState: (req) => req.session?.oauthState ?? null,
79
+ * setState: (req, _res, state) => { req.session!.oauthState = state; },
80
+ * onSuccess: (_req, res, result) => res.json(result),
81
+ * }));
82
+ * ```
83
+ */
84
+ declare function oauthCallback(oauthService: OAuthService, providerName: string, options: OAuthRouteOptions): RequestHandler;
85
+ //#endregion
86
+ export { CookieRefreshOptions, IdentityRouterOptions, OAuthRouteOptions, ResourceLoader, createIdentityRouter, oauthAuthorize, oauthCallback, requireAuth, requireAuthorization, requirePermission };
87
+ //# sourceMappingURL=express.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.cts","names":[],"sources":["../core/express/middleware.ts","../core/express/oauth-routes.ts"],"mappings":";;;;;;;QAOQ,MAAA;EAAA,UAEI,OAAA;IAAA,UACE,OAAA;MALwC;MAOhD,QAAA,GAAW,qBAAqB;IAAA;EAAA;AAAA;;KAM1B,cAAA,eAA6B,GAAA,EAAK,OAAA,KAAY,SAAA,GAAY,OAAA,CAAQ,SAAA;;;;;AANxC;iBAoBtB,WAAA,CAAY,OAAA,EAAS,eAAA,GAAkB,cAAc;;;;;iBAmBrD,oBAAA,qBAAA,CACd,OAAA,EAAS,eAAA,EACT,WAAA,EAAa,wBAAA,CAAyB,SAAA,GACtC,YAAA,GAAe,cAAA,CAAe,SAAA,IAC7B,cAAA;;iBAwBa,iBAAA,CACd,OAAA,EAAS,eAAA,EACT,UAAA,WACC,cAAc;;;;;;UC1EA,iBAAA;;EAEf,WAAA;EDLoD;;;;ECUpD,QAAA,CAAS,GAAA,EAAK,OAAA,mBAA0B,OAAA;EDL5B;;;;ECUZ,QAAA,CAAS,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,KAAA,kBAAuB,OAAA;EDRzB;AAMtC;;;ECOE,SAAA,CAAU,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,MAAA,EAAF,UAAA,UAAiD,OAAA;AAAA;;;;;;;;;;;;ADPD;AAcvF;;iBCUgB,cAAA,CACd,YAAA,EAAc,YAAA,EACd,YAAA,UACA,OAAA,EAAS,IAAA,CAAK,iBAAA,gCACb,cAAA;;;;;;ADdkE;AAmBrE;;;;;;;;iBCqBgB,aAAA,CACd,YAAA,EAAc,YAAA,EACd,YAAA,UACA,OAAA,EAAS,iBAAA,GACR,cAAA"}
@@ -0,0 +1,87 @@
1
+ import { c as AuthResult, d as AuthenticatedIdentity } from "./identity-store-BRRahxcS.mjs";
2
+ import { V as AuthorizationRequirement, f as OAuthService } from "./errors-BGMwaW5s.mjs";
3
+ import { t as IdentityService } from "./identity-service-B9zrvE9z.mjs";
4
+ import { n as IdentityRouterOptions, r as createIdentityRouter, t as CookieRefreshOptions } from "./identity-router-DBL20UWT.mjs";
5
+ import { Request, RequestHandler, Response } from "express";
6
+
7
+ //#region core/express/middleware.d.ts
8
+ declare global {
9
+ namespace Express {
10
+ interface Request {
11
+ /** The authenticated principal, set by {@link requireAuth}. */
12
+ identity?: AuthenticatedIdentity;
13
+ }
14
+ }
15
+ }
16
+ /** Loads the resource a policy will be evaluated against. */
17
+ type ResourceLoader<TResource> = (req: Request) => TResource | Promise<TResource>;
18
+ /**
19
+ * Express middleware that authenticates the request using a Bearer access token and
20
+ * attaches the hydrated principal to `req.identity`. Forwards identity errors to the
21
+ * error-handling middleware.
22
+ */
23
+ declare function requireAuth(service: IdentityService): RequestHandler;
24
+ /**
25
+ * Express middleware enforcing an authorization requirement (deny-by-default). Must run
26
+ * after {@link requireAuth}. Optionally loads a resource for ownership policies.
27
+ */
28
+ declare function requireAuthorization<TResource = unknown>(service: IdentityService, requirement: AuthorizationRequirement<TResource>, loadResource?: ResourceLoader<TResource>): RequestHandler;
29
+ /** Convenience: require a single permission. */
30
+ declare function requirePermission(service: IdentityService, permission: string): RequestHandler;
31
+ //#endregion
32
+ //#region core/express/oauth-routes.d.ts
33
+ /**
34
+ * Options for {@link oauthAuthorize} and {@link oauthCallback}.
35
+ */
36
+ interface OAuthRouteOptions {
37
+ /** The full redirect URI registered with the provider (must match exactly). */
38
+ redirectUri: string;
39
+ /**
40
+ * Retrieves the stored CSRF state value from the current request context (e.g. from a
41
+ * signed cookie or server session). Return `null` if no state has been stored yet.
42
+ */
43
+ getState(req: Request): string | null | Promise<string | null>;
44
+ /**
45
+ * Persists the generated CSRF state value before redirecting the user to the provider.
46
+ * Use a signed cookie or server session.
47
+ */
48
+ setState(req: Request, res: Response, state: string): void | Promise<void>;
49
+ /**
50
+ * Called on a successful OAuth callback with the auth result. Typically sets a session
51
+ * cookie and redirects to the app.
52
+ */
53
+ onSuccess(req: Request, res: Response, result: AuthResult): void | Promise<void>;
54
+ }
55
+ /**
56
+ * Returns an Express route handler that redirects the user to the OAuth provider's
57
+ * authorization page. Generates and persists a CSRF state value via `options.setState`.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * app.get("/auth/google", oauthAuthorize(identity.oauth!, "google", {
62
+ * redirectUri: `${BASE_URL}/auth/google/callback`,
63
+ * getState: (req) => req.session?.oauthState ?? null,
64
+ * setState: (req, _res, state) => { req.session!.oauthState = state; },
65
+ * onSuccess: (_req, res, result) => res.json(result),
66
+ * }));
67
+ * ```
68
+ */
69
+ declare function oauthAuthorize(oauthService: OAuthService, providerName: string, options: Pick<OAuthRouteOptions, "redirectUri" | "setState">): RequestHandler;
70
+ /**
71
+ * Returns an Express route handler that handles the OAuth provider callback. Verifies
72
+ * state, exchanges the code, and calls `options.onSuccess` with the auth result.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * app.get("/auth/google/callback", oauthCallback(identity.oauth!, "google", {
77
+ * redirectUri: `${BASE_URL}/auth/google/callback`,
78
+ * getState: (req) => req.session?.oauthState ?? null,
79
+ * setState: (req, _res, state) => { req.session!.oauthState = state; },
80
+ * onSuccess: (_req, res, result) => res.json(result),
81
+ * }));
82
+ * ```
83
+ */
84
+ declare function oauthCallback(oauthService: OAuthService, providerName: string, options: OAuthRouteOptions): RequestHandler;
85
+ //#endregion
86
+ export { CookieRefreshOptions, IdentityRouterOptions, OAuthRouteOptions, ResourceLoader, createIdentityRouter, oauthAuthorize, oauthCallback, requireAuth, requireAuthorization, requirePermission };
87
+ //# sourceMappingURL=express.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.mts","names":[],"sources":["../core/express/middleware.ts","../core/express/oauth-routes.ts"],"mappings":";;;;;;;QAOQ,MAAA;EAAA,UAEI,OAAA;IAAA,UACE,OAAA;MALwC;MAOhD,QAAA,GAAW,qBAAqB;IAAA;EAAA;AAAA;;KAM1B,cAAA,eAA6B,GAAA,EAAK,OAAA,KAAY,SAAA,GAAY,OAAA,CAAQ,SAAA;;;;;AANxC;iBAoBtB,WAAA,CAAY,OAAA,EAAS,eAAA,GAAkB,cAAc;;;;;iBAmBrD,oBAAA,qBAAA,CACd,OAAA,EAAS,eAAA,EACT,WAAA,EAAa,wBAAA,CAAyB,SAAA,GACtC,YAAA,GAAe,cAAA,CAAe,SAAA,IAC7B,cAAA;;iBAwBa,iBAAA,CACd,OAAA,EAAS,eAAA,EACT,UAAA,WACC,cAAc;;;;;;UC1EA,iBAAA;;EAEf,WAAA;EDLoD;;;;ECUpD,QAAA,CAAS,GAAA,EAAK,OAAA,mBAA0B,OAAA;EDL5B;;;;ECUZ,QAAA,CAAS,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,KAAA,kBAAuB,OAAA;EDRzB;AAMtC;;;ECOE,SAAA,CAAU,GAAA,EAAK,OAAA,EAAS,GAAA,EAAK,QAAA,EAAU,MAAA,EAAF,UAAA,UAAiD,OAAA;AAAA;;;;;;;;;;;;ADPD;AAcvF;;iBCUgB,cAAA,CACd,YAAA,EAAc,YAAA,EACd,YAAA,UACA,OAAA,EAAS,IAAA,CAAK,iBAAA,gCACb,cAAA;;;;;;ADdkE;AAmBrE;;;;;;;;iBCqBgB,aAAA,CACd,YAAA,EAAc,YAAA,EACd,YAAA,UACA,OAAA,EAAS,iBAAA,GACR,cAAA"}