@hyper.authio/express 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/README.md CHANGED
@@ -1,3 +1,60 @@
1
- # Temporary Holding Version
1
+ # @hyper.authio/express
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Protect an Express 4 or 5 API with Authio: bearer validation, guards for every form of permission, and a signed
4
+ webhook route. Built on [`@hyper.authio/server`](../server/README.md).
5
+
6
+ ```bash
7
+ npm install @hyper.authio/express
8
+ ```
9
+
10
+ ```ts
11
+ import express from "express";
12
+ import {
13
+ anyOf,
14
+ authioBearer,
15
+ authioWebhooks,
16
+ requireAuthenticationLevel,
17
+ requireAuthio,
18
+ requireGroup,
19
+ requireOrganization,
20
+ requirePermission,
21
+ requireRole,
22
+ requireScope,
23
+ } from "@hyper.authio/express";
24
+
25
+ const app = express();
26
+
27
+ // Before any JSON parser: the signature is checked over the raw body.
28
+ app.post(
29
+ "/webhooks/authio",
30
+ authioWebhooks({ secret: process.env.AUTHIO_WEBHOOK_SECRET }).on("user_deleted", async (event) => {
31
+ await forget(event.data.id);
32
+ }),
33
+ );
34
+
35
+ app.use(express.json());
36
+ app.use("/api", authioBearer({ issuer: "https://auth.example.com/realms/acme", audiences: "https://orders.example.com" }));
37
+
38
+ app.get("/api/me", (req, res) => res.json(req.authio!.user));
39
+ app.get("/api/orders", requireScope("orders:read"), requirePermission("orders", "read"), listOrders);
40
+ app.delete("/api/orders/:id", requireRole("orders-admin", { sources: ["user"] }), deleteOrder);
41
+ app.get("/api/finance", requireGroup("finance"), financeReport);
42
+ app.post("/api/payouts", requireAuthio(requirePermission("payouts", "create"), anyOf(requireAuthenticationLevel(2), requireOrganization("acme"))), payout);
43
+ ```
44
+
45
+ - `authioBearer(verifierOrOptions, { optional?, problemDetails?, passErrors? })` validates the token and puts
46
+ `{ user, token }` on `req.authio`; it answers `401` with `WWW-Authenticate`. With `optional`, a request without a
47
+ token goes through unauthenticated.
48
+ - `requireScope`, `requireRole`, `requirePermission`, `requireGroup`, `requireClaim`, `requireOrganization`,
49
+ `requireAuthenticationLevel`, `requireAuthenticationMethod` and `requireClient` answer `403`, with
50
+ `insufficient_scope` or `insufficient_user_authentication` in `WWW-Authenticate` when that is the reason.
51
+ - Every guard is also a rule: `requireAuthio(...)`, `allOf(...)` and `anyOf(...)` combine guards of this package and
52
+ rules of `@hyper.authio/server` alike, and are guards themselves.
53
+ - `problemDetails: true` adds an RFC 9457 body; `passErrors: true` hands `AuthioBearerError` / `AuthioForbiddenError`
54
+ to `next(error)` for your own error handler. `sendAuthioError(res, error)` answers one by hand.
55
+ - `authioWebhooks(options)` reads the raw body itself (or takes `express.raw()`'s buffer), and answers `200` handled
56
+ or duplicate, `400`, `401`, `413` or `500`.
57
+
58
+ ## License
59
+
60
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,127 @@
1
+ 'use strict';
2
+
3
+ var server = require('@hyper.authio/server');
4
+
5
+ // src/index.ts
6
+ var responseSettings = /* @__PURE__ */ Symbol.for("authio.express.response");
7
+ function authioBearer(verifier, options = {}) {
8
+ const resolved = isVerifier(verifier) ? verifier : server.createAuthioVerifier(verifier);
9
+ return (request, response, next) => {
10
+ request[responseSettings] = { problemDetails: options.problemDetails, passErrors: options.passErrors };
11
+ const header = request.headers.authorization;
12
+ if (options.optional && !header) {
13
+ next();
14
+ return;
15
+ }
16
+ resolved.verifyAuthorizationHeader(header).then((user) => {
17
+ request.authio = { user, token: user.token };
18
+ next();
19
+ }).catch((error) => refuse(request, response, next, error, options));
20
+ };
21
+ }
22
+ function requireAuthio(...rules) {
23
+ if (rules.length === 0) throw new TypeError("requireAuthio needs at least one rule.");
24
+ const rule = rules.length === 1 ? rules[0] : server.allOf(...rules);
25
+ const guard = (request, response, next) => {
26
+ const user = request.authio?.user;
27
+ if (!user) return refuse(request, response, next, new server.AuthioBearerError(void 0, { error: void 0 }));
28
+ const failure = server.authorize(user, ...rules);
29
+ if (failure) return refuse(request, response, next, new server.AuthioForbiddenError(failure));
30
+ next();
31
+ };
32
+ return Object.assign(guard, { description: rule.description, check: (user) => server.authorize(user, ...rules) });
33
+ }
34
+ var requireScope = (scopes, match) => requireAuthio(server.requireScope(scopes, match));
35
+ var requireRole = (roles, options) => requireAuthio(server.requireRole(roles, options));
36
+ var requirePermission = (resource, method, options) => requireAuthio(server.requirePermission(resource, method, options));
37
+ var requireGroup = (groups, match) => requireAuthio(server.requireGroup(groups, match));
38
+ var requireClaim = (type, ...values) => requireAuthio(server.requireClaim(type, ...values));
39
+ var requireOrganization = (organization) => requireAuthio(server.requireOrganization(organization));
40
+ var requireAuthenticationLevel = (minimumLevel) => requireAuthio(server.requireAuthenticationLevel(minimumLevel));
41
+ var requireAuthenticationMethod = (methods, match) => requireAuthio(server.requireAuthenticationMethod(methods, match));
42
+ var requireClient = (clientIds) => requireAuthio(server.requireClient(clientIds));
43
+ var allOf = (...rules) => requireAuthio(server.allOf(...rules));
44
+ var anyOf = (...rules) => requireAuthio(server.anyOf(...rules));
45
+ function authioWebhooks(options) {
46
+ const webhooks = server.createAuthioWebhooks(options);
47
+ const route = ((request, response, next) => {
48
+ readRawBody(request, webhooks.maxBodySize).then(async (body) => {
49
+ if (body === void 0) {
50
+ response.status(413).end();
51
+ return;
52
+ }
53
+ const outcome = await webhooks.handle({ body, headers: request.headers });
54
+ response.status(outcome.status).end();
55
+ }).catch(next);
56
+ });
57
+ route.on = ((type, handler) => {
58
+ webhooks.on(type, handler);
59
+ return route;
60
+ });
61
+ return route;
62
+ }
63
+ function sendAuthioError(response, error, options = {}) {
64
+ const challenge = error.wwwAuthenticate;
65
+ if (challenge) response.append("WWW-Authenticate", challenge);
66
+ response.status(error.status);
67
+ if (options.problemDetails) response.type("application/problem+json").send(JSON.stringify(server.toProblemDetails(error)));
68
+ else response.end();
69
+ }
70
+ function refuse(request, response, next, error, options) {
71
+ const settings = options ?? request[responseSettings] ?? {};
72
+ if (!(error instanceof server.AuthioBearerError) && !(error instanceof server.AuthioForbiddenError)) return next(error);
73
+ if (settings.passErrors) return next(error);
74
+ sendAuthioError(response, error, settings);
75
+ }
76
+ function readRawBody(request, maxBodySize) {
77
+ const parsed = request.body;
78
+ if (Buffer.isBuffer(parsed)) return Promise.resolve(parsed.byteLength > maxBodySize ? void 0 : new Uint8Array(parsed));
79
+ if (typeof parsed === "string") return Promise.resolve(new TextEncoder().encode(parsed));
80
+ if (request.readableEnded || parsed !== void 0 && parsed !== null && typeof parsed === "object" && Object.keys(parsed).length > 0)
81
+ return Promise.reject(
82
+ new Error("The Authio webhook route needs the raw body: mount it before express.json(), or use express.raw({ type: '*/*' }) on it.")
83
+ );
84
+ const declared = Number(request.headers["content-length"]);
85
+ if (Number.isFinite(declared) && declared > maxBodySize) return Promise.resolve(void 0);
86
+ return new Promise((resolve, reject) => {
87
+ const chunks = [];
88
+ let size = 0;
89
+ let done = false;
90
+ request.on("data", (chunk) => {
91
+ if (done) return;
92
+ size += chunk.byteLength;
93
+ if (size > maxBodySize) {
94
+ done = true;
95
+ request.pause();
96
+ resolve(void 0);
97
+ return;
98
+ }
99
+ chunks.push(chunk);
100
+ });
101
+ request.on("end", () => {
102
+ if (!done) resolve(new Uint8Array(Buffer.concat(chunks)));
103
+ });
104
+ request.on("error", reject);
105
+ });
106
+ }
107
+ function isVerifier(value) {
108
+ return typeof value.verify === "function";
109
+ }
110
+
111
+ exports.allOf = allOf;
112
+ exports.anyOf = anyOf;
113
+ exports.authioBearer = authioBearer;
114
+ exports.authioWebhooks = authioWebhooks;
115
+ exports.requireAuthenticationLevel = requireAuthenticationLevel;
116
+ exports.requireAuthenticationMethod = requireAuthenticationMethod;
117
+ exports.requireAuthio = requireAuthio;
118
+ exports.requireClaim = requireClaim;
119
+ exports.requireClient = requireClient;
120
+ exports.requireGroup = requireGroup;
121
+ exports.requireOrganization = requireOrganization;
122
+ exports.requirePermission = requirePermission;
123
+ exports.requireRole = requireRole;
124
+ exports.requireScope = requireScope;
125
+ exports.sendAuthioError = sendAuthioError;
126
+ //# sourceMappingURL=index.cjs.map
127
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["createAuthioVerifier","allOfRule","AuthioBearerError","authorize","AuthioForbiddenError","scopeRule","roleRule","permissionRule","groupRule","claimRule","organizationRule","authenticationLevelRule","authenticationMethodRule","clientRule","anyOfRule","createAuthioWebhooks","toProblemDetails"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { NextFunction, Request, RequestHandler, Response } from \"express\";\nimport type { AuthioResourceAccessSource, AuthioTypedWebhookEvent, AuthioWebhookDataByType } from \"@hyper.authio/client\";\nimport {\n allOf as allOfRule,\n anyOf as anyOfRule,\n authorize,\n AuthioBearerError,\n AuthioForbiddenError,\n createAuthioVerifier,\n createAuthioWebhooks,\n requireAuthenticationLevel as authenticationLevelRule,\n requireAuthenticationMethod as authenticationMethodRule,\n requireClaim as claimRule,\n requireClient as clientRule,\n requireGroup as groupRule,\n requireOrganization as organizationRule,\n requirePermission as permissionRule,\n requireRole as roleRule,\n requireScope as scopeRule,\n toProblemDetails,\n type AuthioMatch,\n type AuthioRule,\n type AuthioUser,\n type AuthioVerifier,\n type AuthioVerifierOptions,\n type AuthioWebhookHandler,\n type AuthioWebhookOptions,\n} from \"@hyper.authio/server\";\n\n/**\n * What `authioBearer` puts on the request.\n */\nexport interface AuthioRequestContext {\n /** The caller. */\n readonly user: AuthioUser;\n /** The access token the caller was read from. */\n readonly token: string;\n}\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n /** The Authio caller, once `authioBearer` validated the token. */\n authio?: AuthioRequestContext;\n }\n }\n}\n\n/**\n * How refusals are answered.\n */\nexport interface AuthioResponseOptions {\n /** Answers `401` and `403` with problem details (RFC 9457) as well as the `WWW-Authenticate` header. */\n problemDetails?: boolean;\n /** Passes refusals to `next(error)` instead of answering, for an application's own error handler. */\n passErrors?: boolean;\n}\n\n/**\n * Options of `authioBearer`.\n */\nexport interface AuthioBearerOptions extends AuthioResponseOptions {\n /** Lets a request without a token through, unauthenticated; a token that is present must still be valid. */\n optional?: boolean;\n}\n\nconst responseSettings = Symbol.for(\"authio.express.response\");\n\ntype RequestWithSettings = Request & { [responseSettings]?: AuthioResponseOptions };\n\n/**\n * Validates the `Authorization: Bearer` token and puts the caller on `req.authio`. Answers `401` with the\n * `WWW-Authenticate` challenge when the token is missing or invalid.\n *\n * @param verifier A verifier, or its options.\n */\nexport function authioBearer(verifier: AuthioVerifier | AuthioVerifierOptions, options: AuthioBearerOptions = {}): RequestHandler {\n const resolved = isVerifier(verifier) ? verifier : createAuthioVerifier(verifier);\n\n return (request, response, next) => {\n (request as RequestWithSettings)[responseSettings] = { problemDetails: options.problemDetails, passErrors: options.passErrors };\n\n const header = request.headers.authorization;\n\n if (options.optional && !header) {\n next();\n return;\n }\n\n resolved\n .verifyAuthorizationHeader(header)\n .then((user) => {\n request.authio = { user, token: user.token! };\n next();\n })\n .catch((error: unknown) => refuse(request, response, next, error, options));\n };\n}\n\n/**\n * An Express guard that is also an Authio rule: use it as middleware, or pass it to `requireAuthio`, `allOf` and\n * `anyOf` like any rule of `@hyper.authio/server`.\n */\nexport type AuthioGuard = RequestHandler & AuthioRule;\n\n/**\n * Requires every rule (guards of this package or rules of `@hyper.authio/server`); answers `401` without a caller and\n * `403` with the joined refusal otherwise.\n */\nexport function requireAuthio(...rules: AuthioRule[]): AuthioGuard {\n if (rules.length === 0) throw new TypeError(\"requireAuthio needs at least one rule.\");\n\n const rule = rules.length === 1 ? rules[0]! : allOfRule(...rules);\n const guard: RequestHandler = (request, response, next) => {\n const user = request.authio?.user;\n\n if (!user) return refuse(request, response, next, new AuthioBearerError(undefined, { error: undefined }));\n\n const failure = authorize(user, ...rules);\n\n if (failure) return refuse(request, response, next, new AuthioForbiddenError(failure));\n\n next();\n };\n\n return Object.assign(guard, { description: rule.description, check: (user: AuthioUser) => authorize(user, ...rules) });\n}\n\n/** Requires granted scopes. */\nexport const requireScope = (scopes: string | readonly string[], match?: AuthioMatch): AuthioGuard => requireAuthio(scopeRule(scopes, match));\n/** Requires roles, from any source or only from the given `*_resource_access` claims. */\nexport const requireRole = (\n roles: string | readonly string[],\n options?: { match?: AuthioMatch; sources?: readonly AuthioResourceAccessSource[] },\n): AuthioGuard => requireAuthio(roleRule(roles, options));\n/** Requires a permission (`method` on `resource`). */\nexport const requirePermission = (resource: string, method: string, options?: { sources?: readonly AuthioResourceAccessSource[] }): AuthioGuard =>\n requireAuthio(permissionRule(resource, method, options));\n/** Requires groups. */\nexport const requireGroup = (groups: string | readonly string[], match?: AuthioMatch): AuthioGuard => requireAuthio(groupRule(groups, match));\n/** Requires a claim, or one of its values. */\nexport const requireClaim = (type: string, ...values: string[]): AuthioGuard => requireAuthio(claimRule(type, ...values));\n/** Requires an organization context, or a given organization. */\nexport const requireOrganization = (organization?: string): AuthioGuard => requireAuthio(organizationRule(organization));\n/** Requires a minimum authentication level (`acr`). */\nexport const requireAuthenticationLevel = (minimumLevel: number): AuthioGuard => requireAuthio(authenticationLevelRule(minimumLevel));\n/** Requires authentication methods (`amr`). */\nexport const requireAuthenticationMethod = (methods: string | readonly string[], match?: AuthioMatch): AuthioGuard =>\n requireAuthio(authenticationMethodRule(methods, match));\n/** Requires the token to have been issued to one of the clients. */\nexport const requireClient = (clientIds: string | readonly string[]): AuthioGuard => requireAuthio(clientRule(clientIds));\n/** Requires every rule. */\nexport const allOf = (...rules: AuthioRule[]): AuthioGuard => requireAuthio(allOfRule(...rules));\n/** Requires at least one rule. */\nexport const anyOf = (...rules: AuthioRule[]): AuthioGuard => requireAuthio(anyOfRule(...rules));\n\n/**\n * A webhook route: verifies the signature over the raw body, deduplicates and dispatches. Mount it before any JSON\n * body parser, or behind `express.raw()`.\n */\nexport interface AuthioWebhookRoute extends RequestHandler {\n on<T extends keyof AuthioWebhookDataByType>(type: T, handler: AuthioWebhookHandler<AuthioTypedWebhookEvent<T>>): AuthioWebhookRoute;\n on(type: string, handler: AuthioWebhookHandler): AuthioWebhookRoute;\n}\n\n/**\n * Creates a webhook route: `app.post(\"/webhooks/authio\", authioWebhooks({ secret }).on(\"user_created\", …))`.\n * Answers `200` handled or duplicate, `400` unreadable, `401` bad signature, `413` too large, `500` handler failure.\n */\nexport function authioWebhooks(options: AuthioWebhookOptions): AuthioWebhookRoute {\n const webhooks = createAuthioWebhooks(options);\n\n const route = ((request: Request, response: Response, next: NextFunction) => {\n readRawBody(request, webhooks.maxBodySize)\n .then(async (body) => {\n if (body === undefined) {\n response.status(413).end();\n return;\n }\n\n const outcome = await webhooks.handle({ body, headers: request.headers });\n\n response.status(outcome.status).end();\n })\n .catch(next);\n }) as AuthioWebhookRoute;\n\n route.on = ((type: string, handler: AuthioWebhookHandler) => {\n webhooks.on(type, handler);\n return route;\n }) as AuthioWebhookRoute[\"on\"];\n\n return route;\n}\n\n/**\n * Answers a refusal: `WWW-Authenticate`, the status, and problem details when asked.\n */\nexport function sendAuthioError(response: Response, error: AuthioBearerError | AuthioForbiddenError, options: AuthioResponseOptions = {}): void {\n const challenge = error.wwwAuthenticate;\n\n if (challenge) response.append(\"WWW-Authenticate\", challenge);\n\n response.status(error.status);\n\n if (options.problemDetails) response.type(\"application/problem+json\").send(JSON.stringify(toProblemDetails(error)));\n else response.end();\n}\n\nfunction refuse(request: Request, response: Response, next: NextFunction, error: unknown, options?: AuthioResponseOptions): void {\n const settings = options ?? (request as RequestWithSettings)[responseSettings] ?? {};\n\n if (!(error instanceof AuthioBearerError) && !(error instanceof AuthioForbiddenError)) return next(error);\n if (settings.passErrors) return next(error);\n\n sendAuthioError(response, error, settings);\n}\n\nfunction readRawBody(request: Request, maxBodySize: number): Promise<Uint8Array | undefined> {\n const parsed = (request as Request & { body?: unknown }).body;\n\n if (Buffer.isBuffer(parsed)) return Promise.resolve(parsed.byteLength > maxBodySize ? undefined : new Uint8Array(parsed));\n if (typeof parsed === \"string\") return Promise.resolve(new TextEncoder().encode(parsed));\n\n if (request.readableEnded || (parsed !== undefined && parsed !== null && typeof parsed === \"object\" && Object.keys(parsed).length > 0))\n return Promise.reject(\n new Error(\"The Authio webhook route needs the raw body: mount it before express.json(), or use express.raw({ type: '*/*' }) on it.\"),\n );\n\n const declared = Number(request.headers[\"content-length\"]);\n\n if (Number.isFinite(declared) && declared > maxBodySize) return Promise.resolve(undefined);\n\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let done = false;\n\n request.on(\"data\", (chunk: Buffer) => {\n if (done) return;\n\n size += chunk.byteLength;\n\n if (size > maxBodySize) {\n done = true;\n request.pause();\n resolve(undefined);\n return;\n }\n\n chunks.push(chunk);\n });\n request.on(\"end\", () => {\n if (!done) resolve(new Uint8Array(Buffer.concat(chunks)));\n });\n request.on(\"error\", reject);\n });\n}\n\nfunction isVerifier(value: AuthioVerifier | AuthioVerifierOptions): value is AuthioVerifier {\n return typeof (value as AuthioVerifier).verify === \"function\";\n}\n\nexport type { AuthioMatch, AuthioRule, AuthioUser, AuthioVerifier, AuthioVerifierOptions } from \"@hyper.authio/server\";\n"]}
@@ -0,0 +1,101 @@
1
+ import { RequestHandler, Response } from 'express';
2
+ import { AuthioWebhookDataByType, AuthioTypedWebhookEvent, AuthioResourceAccessSource } from '@hyper.authio/client';
3
+ import { AuthioUser, AuthioRule, AuthioWebhookHandler, AuthioVerifier, AuthioVerifierOptions, AuthioWebhookOptions, AuthioMatch, AuthioBearerError, AuthioForbiddenError } from '@hyper.authio/server';
4
+ export { AuthioMatch, AuthioRule, AuthioUser, AuthioVerifier, AuthioVerifierOptions } from '@hyper.authio/server';
5
+
6
+ /**
7
+ * What `authioBearer` puts on the request.
8
+ */
9
+ interface AuthioRequestContext {
10
+ /** The caller. */
11
+ readonly user: AuthioUser;
12
+ /** The access token the caller was read from. */
13
+ readonly token: string;
14
+ }
15
+ declare global {
16
+ namespace Express {
17
+ interface Request {
18
+ /** The Authio caller, once `authioBearer` validated the token. */
19
+ authio?: AuthioRequestContext;
20
+ }
21
+ }
22
+ }
23
+ /**
24
+ * How refusals are answered.
25
+ */
26
+ interface AuthioResponseOptions {
27
+ /** Answers `401` and `403` with problem details (RFC 9457) as well as the `WWW-Authenticate` header. */
28
+ problemDetails?: boolean;
29
+ /** Passes refusals to `next(error)` instead of answering, for an application's own error handler. */
30
+ passErrors?: boolean;
31
+ }
32
+ /**
33
+ * Options of `authioBearer`.
34
+ */
35
+ interface AuthioBearerOptions extends AuthioResponseOptions {
36
+ /** Lets a request without a token through, unauthenticated; a token that is present must still be valid. */
37
+ optional?: boolean;
38
+ }
39
+ /**
40
+ * Validates the `Authorization: Bearer` token and puts the caller on `req.authio`. Answers `401` with the
41
+ * `WWW-Authenticate` challenge when the token is missing or invalid.
42
+ *
43
+ * @param verifier A verifier, or its options.
44
+ */
45
+ declare function authioBearer(verifier: AuthioVerifier | AuthioVerifierOptions, options?: AuthioBearerOptions): RequestHandler;
46
+ /**
47
+ * An Express guard that is also an Authio rule: use it as middleware, or pass it to `requireAuthio`, `allOf` and
48
+ * `anyOf` like any rule of `@hyper.authio/server`.
49
+ */
50
+ type AuthioGuard = RequestHandler & AuthioRule;
51
+ /**
52
+ * Requires every rule (guards of this package or rules of `@hyper.authio/server`); answers `401` without a caller and
53
+ * `403` with the joined refusal otherwise.
54
+ */
55
+ declare function requireAuthio(...rules: AuthioRule[]): AuthioGuard;
56
+ /** Requires granted scopes. */
57
+ declare const requireScope: (scopes: string | readonly string[], match?: AuthioMatch) => AuthioGuard;
58
+ /** Requires roles, from any source or only from the given `*_resource_access` claims. */
59
+ declare const requireRole: (roles: string | readonly string[], options?: {
60
+ match?: AuthioMatch;
61
+ sources?: readonly AuthioResourceAccessSource[];
62
+ }) => AuthioGuard;
63
+ /** Requires a permission (`method` on `resource`). */
64
+ declare const requirePermission: (resource: string, method: string, options?: {
65
+ sources?: readonly AuthioResourceAccessSource[];
66
+ }) => AuthioGuard;
67
+ /** Requires groups. */
68
+ declare const requireGroup: (groups: string | readonly string[], match?: AuthioMatch) => AuthioGuard;
69
+ /** Requires a claim, or one of its values. */
70
+ declare const requireClaim: (type: string, ...values: string[]) => AuthioGuard;
71
+ /** Requires an organization context, or a given organization. */
72
+ declare const requireOrganization: (organization?: string) => AuthioGuard;
73
+ /** Requires a minimum authentication level (`acr`). */
74
+ declare const requireAuthenticationLevel: (minimumLevel: number) => AuthioGuard;
75
+ /** Requires authentication methods (`amr`). */
76
+ declare const requireAuthenticationMethod: (methods: string | readonly string[], match?: AuthioMatch) => AuthioGuard;
77
+ /** Requires the token to have been issued to one of the clients. */
78
+ declare const requireClient: (clientIds: string | readonly string[]) => AuthioGuard;
79
+ /** Requires every rule. */
80
+ declare const allOf: (...rules: AuthioRule[]) => AuthioGuard;
81
+ /** Requires at least one rule. */
82
+ declare const anyOf: (...rules: AuthioRule[]) => AuthioGuard;
83
+ /**
84
+ * A webhook route: verifies the signature over the raw body, deduplicates and dispatches. Mount it before any JSON
85
+ * body parser, or behind `express.raw()`.
86
+ */
87
+ interface AuthioWebhookRoute extends RequestHandler {
88
+ on<T extends keyof AuthioWebhookDataByType>(type: T, handler: AuthioWebhookHandler<AuthioTypedWebhookEvent<T>>): AuthioWebhookRoute;
89
+ on(type: string, handler: AuthioWebhookHandler): AuthioWebhookRoute;
90
+ }
91
+ /**
92
+ * Creates a webhook route: `app.post("/webhooks/authio", authioWebhooks({ secret }).on("user_created", …))`.
93
+ * Answers `200` handled or duplicate, `400` unreadable, `401` bad signature, `413` too large, `500` handler failure.
94
+ */
95
+ declare function authioWebhooks(options: AuthioWebhookOptions): AuthioWebhookRoute;
96
+ /**
97
+ * Answers a refusal: `WWW-Authenticate`, the status, and problem details when asked.
98
+ */
99
+ declare function sendAuthioError(response: Response, error: AuthioBearerError | AuthioForbiddenError, options?: AuthioResponseOptions): void;
100
+
101
+ export { type AuthioBearerOptions, type AuthioGuard, type AuthioRequestContext, type AuthioResponseOptions, type AuthioWebhookRoute, allOf, anyOf, authioBearer, authioWebhooks, requireAuthenticationLevel, requireAuthenticationMethod, requireAuthio, requireClaim, requireClient, requireGroup, requireOrganization, requirePermission, requireRole, requireScope, sendAuthioError };
@@ -0,0 +1,101 @@
1
+ import { RequestHandler, Response } from 'express';
2
+ import { AuthioWebhookDataByType, AuthioTypedWebhookEvent, AuthioResourceAccessSource } from '@hyper.authio/client';
3
+ import { AuthioUser, AuthioRule, AuthioWebhookHandler, AuthioVerifier, AuthioVerifierOptions, AuthioWebhookOptions, AuthioMatch, AuthioBearerError, AuthioForbiddenError } from '@hyper.authio/server';
4
+ export { AuthioMatch, AuthioRule, AuthioUser, AuthioVerifier, AuthioVerifierOptions } from '@hyper.authio/server';
5
+
6
+ /**
7
+ * What `authioBearer` puts on the request.
8
+ */
9
+ interface AuthioRequestContext {
10
+ /** The caller. */
11
+ readonly user: AuthioUser;
12
+ /** The access token the caller was read from. */
13
+ readonly token: string;
14
+ }
15
+ declare global {
16
+ namespace Express {
17
+ interface Request {
18
+ /** The Authio caller, once `authioBearer` validated the token. */
19
+ authio?: AuthioRequestContext;
20
+ }
21
+ }
22
+ }
23
+ /**
24
+ * How refusals are answered.
25
+ */
26
+ interface AuthioResponseOptions {
27
+ /** Answers `401` and `403` with problem details (RFC 9457) as well as the `WWW-Authenticate` header. */
28
+ problemDetails?: boolean;
29
+ /** Passes refusals to `next(error)` instead of answering, for an application's own error handler. */
30
+ passErrors?: boolean;
31
+ }
32
+ /**
33
+ * Options of `authioBearer`.
34
+ */
35
+ interface AuthioBearerOptions extends AuthioResponseOptions {
36
+ /** Lets a request without a token through, unauthenticated; a token that is present must still be valid. */
37
+ optional?: boolean;
38
+ }
39
+ /**
40
+ * Validates the `Authorization: Bearer` token and puts the caller on `req.authio`. Answers `401` with the
41
+ * `WWW-Authenticate` challenge when the token is missing or invalid.
42
+ *
43
+ * @param verifier A verifier, or its options.
44
+ */
45
+ declare function authioBearer(verifier: AuthioVerifier | AuthioVerifierOptions, options?: AuthioBearerOptions): RequestHandler;
46
+ /**
47
+ * An Express guard that is also an Authio rule: use it as middleware, or pass it to `requireAuthio`, `allOf` and
48
+ * `anyOf` like any rule of `@hyper.authio/server`.
49
+ */
50
+ type AuthioGuard = RequestHandler & AuthioRule;
51
+ /**
52
+ * Requires every rule (guards of this package or rules of `@hyper.authio/server`); answers `401` without a caller and
53
+ * `403` with the joined refusal otherwise.
54
+ */
55
+ declare function requireAuthio(...rules: AuthioRule[]): AuthioGuard;
56
+ /** Requires granted scopes. */
57
+ declare const requireScope: (scopes: string | readonly string[], match?: AuthioMatch) => AuthioGuard;
58
+ /** Requires roles, from any source or only from the given `*_resource_access` claims. */
59
+ declare const requireRole: (roles: string | readonly string[], options?: {
60
+ match?: AuthioMatch;
61
+ sources?: readonly AuthioResourceAccessSource[];
62
+ }) => AuthioGuard;
63
+ /** Requires a permission (`method` on `resource`). */
64
+ declare const requirePermission: (resource: string, method: string, options?: {
65
+ sources?: readonly AuthioResourceAccessSource[];
66
+ }) => AuthioGuard;
67
+ /** Requires groups. */
68
+ declare const requireGroup: (groups: string | readonly string[], match?: AuthioMatch) => AuthioGuard;
69
+ /** Requires a claim, or one of its values. */
70
+ declare const requireClaim: (type: string, ...values: string[]) => AuthioGuard;
71
+ /** Requires an organization context, or a given organization. */
72
+ declare const requireOrganization: (organization?: string) => AuthioGuard;
73
+ /** Requires a minimum authentication level (`acr`). */
74
+ declare const requireAuthenticationLevel: (minimumLevel: number) => AuthioGuard;
75
+ /** Requires authentication methods (`amr`). */
76
+ declare const requireAuthenticationMethod: (methods: string | readonly string[], match?: AuthioMatch) => AuthioGuard;
77
+ /** Requires the token to have been issued to one of the clients. */
78
+ declare const requireClient: (clientIds: string | readonly string[]) => AuthioGuard;
79
+ /** Requires every rule. */
80
+ declare const allOf: (...rules: AuthioRule[]) => AuthioGuard;
81
+ /** Requires at least one rule. */
82
+ declare const anyOf: (...rules: AuthioRule[]) => AuthioGuard;
83
+ /**
84
+ * A webhook route: verifies the signature over the raw body, deduplicates and dispatches. Mount it before any JSON
85
+ * body parser, or behind `express.raw()`.
86
+ */
87
+ interface AuthioWebhookRoute extends RequestHandler {
88
+ on<T extends keyof AuthioWebhookDataByType>(type: T, handler: AuthioWebhookHandler<AuthioTypedWebhookEvent<T>>): AuthioWebhookRoute;
89
+ on(type: string, handler: AuthioWebhookHandler): AuthioWebhookRoute;
90
+ }
91
+ /**
92
+ * Creates a webhook route: `app.post("/webhooks/authio", authioWebhooks({ secret }).on("user_created", …))`.
93
+ * Answers `200` handled or duplicate, `400` unreadable, `401` bad signature, `413` too large, `500` handler failure.
94
+ */
95
+ declare function authioWebhooks(options: AuthioWebhookOptions): AuthioWebhookRoute;
96
+ /**
97
+ * Answers a refusal: `WWW-Authenticate`, the status, and problem details when asked.
98
+ */
99
+ declare function sendAuthioError(response: Response, error: AuthioBearerError | AuthioForbiddenError, options?: AuthioResponseOptions): void;
100
+
101
+ export { type AuthioBearerOptions, type AuthioGuard, type AuthioRequestContext, type AuthioResponseOptions, type AuthioWebhookRoute, allOf, anyOf, authioBearer, authioWebhooks, requireAuthenticationLevel, requireAuthenticationMethod, requireAuthio, requireClaim, requireClient, requireGroup, requireOrganization, requirePermission, requireRole, requireScope, sendAuthioError };
package/dist/index.js ADDED
@@ -0,0 +1,111 @@
1
+ import { createAuthioVerifier, allOf as allOf$1, authorize, requireScope as requireScope$1, requireRole as requireRole$1, requirePermission as requirePermission$1, requireGroup as requireGroup$1, requireClaim as requireClaim$1, requireOrganization as requireOrganization$1, requireAuthenticationLevel as requireAuthenticationLevel$1, requireAuthenticationMethod as requireAuthenticationMethod$1, requireClient as requireClient$1, anyOf as anyOf$1, createAuthioWebhooks, toProblemDetails, AuthioBearerError, AuthioForbiddenError } from '@hyper.authio/server';
2
+
3
+ // src/index.ts
4
+ var responseSettings = /* @__PURE__ */ Symbol.for("authio.express.response");
5
+ function authioBearer(verifier, options = {}) {
6
+ const resolved = isVerifier(verifier) ? verifier : createAuthioVerifier(verifier);
7
+ return (request, response, next) => {
8
+ request[responseSettings] = { problemDetails: options.problemDetails, passErrors: options.passErrors };
9
+ const header = request.headers.authorization;
10
+ if (options.optional && !header) {
11
+ next();
12
+ return;
13
+ }
14
+ resolved.verifyAuthorizationHeader(header).then((user) => {
15
+ request.authio = { user, token: user.token };
16
+ next();
17
+ }).catch((error) => refuse(request, response, next, error, options));
18
+ };
19
+ }
20
+ function requireAuthio(...rules) {
21
+ if (rules.length === 0) throw new TypeError("requireAuthio needs at least one rule.");
22
+ const rule = rules.length === 1 ? rules[0] : allOf$1(...rules);
23
+ const guard = (request, response, next) => {
24
+ const user = request.authio?.user;
25
+ if (!user) return refuse(request, response, next, new AuthioBearerError(void 0, { error: void 0 }));
26
+ const failure = authorize(user, ...rules);
27
+ if (failure) return refuse(request, response, next, new AuthioForbiddenError(failure));
28
+ next();
29
+ };
30
+ return Object.assign(guard, { description: rule.description, check: (user) => authorize(user, ...rules) });
31
+ }
32
+ var requireScope = (scopes, match) => requireAuthio(requireScope$1(scopes, match));
33
+ var requireRole = (roles, options) => requireAuthio(requireRole$1(roles, options));
34
+ var requirePermission = (resource, method, options) => requireAuthio(requirePermission$1(resource, method, options));
35
+ var requireGroup = (groups, match) => requireAuthio(requireGroup$1(groups, match));
36
+ var requireClaim = (type, ...values) => requireAuthio(requireClaim$1(type, ...values));
37
+ var requireOrganization = (organization) => requireAuthio(requireOrganization$1(organization));
38
+ var requireAuthenticationLevel = (minimumLevel) => requireAuthio(requireAuthenticationLevel$1(minimumLevel));
39
+ var requireAuthenticationMethod = (methods, match) => requireAuthio(requireAuthenticationMethod$1(methods, match));
40
+ var requireClient = (clientIds) => requireAuthio(requireClient$1(clientIds));
41
+ var allOf = (...rules) => requireAuthio(allOf$1(...rules));
42
+ var anyOf = (...rules) => requireAuthio(anyOf$1(...rules));
43
+ function authioWebhooks(options) {
44
+ const webhooks = createAuthioWebhooks(options);
45
+ const route = ((request, response, next) => {
46
+ readRawBody(request, webhooks.maxBodySize).then(async (body) => {
47
+ if (body === void 0) {
48
+ response.status(413).end();
49
+ return;
50
+ }
51
+ const outcome = await webhooks.handle({ body, headers: request.headers });
52
+ response.status(outcome.status).end();
53
+ }).catch(next);
54
+ });
55
+ route.on = ((type, handler) => {
56
+ webhooks.on(type, handler);
57
+ return route;
58
+ });
59
+ return route;
60
+ }
61
+ function sendAuthioError(response, error, options = {}) {
62
+ const challenge = error.wwwAuthenticate;
63
+ if (challenge) response.append("WWW-Authenticate", challenge);
64
+ response.status(error.status);
65
+ if (options.problemDetails) response.type("application/problem+json").send(JSON.stringify(toProblemDetails(error)));
66
+ else response.end();
67
+ }
68
+ function refuse(request, response, next, error, options) {
69
+ const settings = options ?? request[responseSettings] ?? {};
70
+ if (!(error instanceof AuthioBearerError) && !(error instanceof AuthioForbiddenError)) return next(error);
71
+ if (settings.passErrors) return next(error);
72
+ sendAuthioError(response, error, settings);
73
+ }
74
+ function readRawBody(request, maxBodySize) {
75
+ const parsed = request.body;
76
+ if (Buffer.isBuffer(parsed)) return Promise.resolve(parsed.byteLength > maxBodySize ? void 0 : new Uint8Array(parsed));
77
+ if (typeof parsed === "string") return Promise.resolve(new TextEncoder().encode(parsed));
78
+ if (request.readableEnded || parsed !== void 0 && parsed !== null && typeof parsed === "object" && Object.keys(parsed).length > 0)
79
+ return Promise.reject(
80
+ new Error("The Authio webhook route needs the raw body: mount it before express.json(), or use express.raw({ type: '*/*' }) on it.")
81
+ );
82
+ const declared = Number(request.headers["content-length"]);
83
+ if (Number.isFinite(declared) && declared > maxBodySize) return Promise.resolve(void 0);
84
+ return new Promise((resolve, reject) => {
85
+ const chunks = [];
86
+ let size = 0;
87
+ let done = false;
88
+ request.on("data", (chunk) => {
89
+ if (done) return;
90
+ size += chunk.byteLength;
91
+ if (size > maxBodySize) {
92
+ done = true;
93
+ request.pause();
94
+ resolve(void 0);
95
+ return;
96
+ }
97
+ chunks.push(chunk);
98
+ });
99
+ request.on("end", () => {
100
+ if (!done) resolve(new Uint8Array(Buffer.concat(chunks)));
101
+ });
102
+ request.on("error", reject);
103
+ });
104
+ }
105
+ function isVerifier(value) {
106
+ return typeof value.verify === "function";
107
+ }
108
+
109
+ export { allOf, anyOf, authioBearer, authioWebhooks, requireAuthenticationLevel, requireAuthenticationMethod, requireAuthio, requireClaim, requireClient, requireGroup, requireOrganization, requirePermission, requireRole, requireScope, sendAuthioError };
110
+ //# sourceMappingURL=index.js.map
111
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["allOfRule","scopeRule","roleRule","permissionRule","groupRule","claimRule","organizationRule","authenticationLevelRule","authenticationMethodRule","clientRule","anyOfRule"],"mappings":";;;AAmEA,IAAM,gBAAA,mBAAmB,MAAA,CAAO,GAAA,CAAI,yBAAyB,CAAA;AAUtD,SAAS,YAAA,CAAa,QAAA,EAAkD,OAAA,GAA+B,EAAC,EAAmB;AAChI,EAAA,MAAM,WAAW,UAAA,CAAW,QAAQ,CAAA,GAAI,QAAA,GAAW,qBAAqB,QAAQ,CAAA;AAEhF,EAAA,OAAO,CAAC,OAAA,EAAS,QAAA,EAAU,IAAA,KAAS;AAClC,IAAC,OAAA,CAAgC,gBAAgB,CAAA,GAAI,EAAE,gBAAgB,OAAA,CAAQ,cAAA,EAAgB,UAAA,EAAY,OAAA,CAAQ,UAAA,EAAW;AAE9H,IAAA,MAAM,MAAA,GAAS,QAAQ,OAAA,CAAQ,aAAA;AAE/B,IAAA,IAAI,OAAA,CAAQ,QAAA,IAAY,CAAC,MAAA,EAAQ;AAC/B,MAAA,IAAA,EAAK;AACL,MAAA;AAAA,IACF;AAEA,IAAA,QAAA,CACG,yBAAA,CAA0B,MAAM,CAAA,CAChC,IAAA,CAAK,CAAC,IAAA,KAAS;AACd,MAAA,OAAA,CAAQ,MAAA,GAAS,EAAE,IAAA,EAAM,KAAA,EAAO,KAAK,KAAA,EAAO;AAC5C,MAAA,IAAA,EAAK;AAAA,IACP,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,KAAA,KAAmB,MAAA,CAAO,OAAA,EAAS,QAAA,EAAU,IAAA,EAAM,KAAA,EAAO,OAAO,CAAC,CAAA;AAAA,EAC9E,CAAA;AACF;AAYO,SAAS,iBAAiB,KAAA,EAAkC;AACjE,EAAA,IAAI,MAAM,MAAA,KAAW,CAAA,EAAG,MAAM,IAAI,UAAU,wCAAwC,CAAA;AAEpF,EAAA,MAAM,IAAA,GAAO,MAAM,MAAA,KAAW,CAAA,GAAI,MAAM,CAAC,CAAA,GAAKA,OAAA,CAAU,GAAG,KAAK,CAAA;AAChE,EAAA,MAAM,KAAA,GAAwB,CAAC,OAAA,EAAS,QAAA,EAAU,IAAA,KAAS;AACzD,IAAA,MAAM,IAAA,GAAO,QAAQ,MAAA,EAAQ,IAAA;AAE7B,IAAA,IAAI,CAAC,IAAA,EAAM,OAAO,MAAA,CAAO,SAAS,QAAA,EAAU,IAAA,EAAM,IAAI,iBAAA,CAAkB,MAAA,EAAW,EAAE,KAAA,EAAO,MAAA,EAAW,CAAC,CAAA;AAExG,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,IAAA,EAAM,GAAG,KAAK,CAAA;AAExC,IAAA,IAAI,OAAA,SAAgB,MAAA,CAAO,OAAA,EAAS,UAAU,IAAA,EAAM,IAAI,oBAAA,CAAqB,OAAO,CAAC,CAAA;AAErF,IAAA,IAAA,EAAK;AAAA,EACP,CAAA;AAEA,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,EAAE,aAAa,IAAA,CAAK,WAAA,EAAa,KAAA,EAAO,CAAC,SAAqB,SAAA,CAAU,IAAA,EAAM,GAAG,KAAK,GAAG,CAAA;AACvH;AAGO,IAAM,YAAA,GAAe,CAAC,MAAA,EAAoC,KAAA,KAAqC,cAAcC,cAAA,CAAU,MAAA,EAAQ,KAAK,CAAC;AAErI,IAAM,WAAA,GAAc,CACzB,KAAA,EACA,OAAA,KACgB,cAAcC,aAAA,CAAS,KAAA,EAAO,OAAO,CAAC;AAEjD,IAAM,iBAAA,GAAoB,CAAC,QAAA,EAAkB,MAAA,EAAgB,OAAA,KAClE,cAAcC,mBAAA,CAAe,QAAA,EAAU,MAAA,EAAQ,OAAO,CAAC;AAElD,IAAM,YAAA,GAAe,CAAC,MAAA,EAAoC,KAAA,KAAqC,cAAcC,cAAA,CAAU,MAAA,EAAQ,KAAK,CAAC;AAErI,IAAM,YAAA,GAAe,CAAC,IAAA,EAAA,GAAiB,MAAA,KAAkC,cAAcC,cAAA,CAAU,IAAA,EAAM,GAAG,MAAM,CAAC;AAEjH,IAAM,sBAAsB,CAAC,YAAA,KAAuC,aAAA,CAAcC,qBAAA,CAAiB,YAAY,CAAC;AAEhH,IAAM,6BAA6B,CAAC,YAAA,KAAsC,aAAA,CAAcC,4BAAA,CAAwB,YAAY,CAAC;AAE7H,IAAM,2BAAA,GAA8B,CAAC,OAAA,EAAqC,KAAA,KAC/E,cAAcC,6BAAA,CAAyB,OAAA,EAAS,KAAK,CAAC;AAEjD,IAAM,gBAAgB,CAAC,SAAA,KAAuD,aAAA,CAAcC,eAAA,CAAW,SAAS,CAAC;AAEjH,IAAM,QAAQ,CAAA,GAAI,KAAA,KAAqC,cAAcT,OAAA,CAAU,GAAG,KAAK,CAAC;AAExF,IAAM,QAAQ,CAAA,GAAI,KAAA,KAAqC,cAAcU,OAAA,CAAU,GAAG,KAAK,CAAC;AAexF,SAAS,eAAe,OAAA,EAAmD;AAChF,EAAA,MAAM,QAAA,GAAW,qBAAqB,OAAO,CAAA;AAE7C,EAAA,MAAM,KAAA,IAAS,CAAC,OAAA,EAAkB,QAAA,EAAoB,IAAA,KAAuB;AAC3E,IAAA,WAAA,CAAY,SAAS,QAAA,CAAS,WAAW,CAAA,CACtC,IAAA,CAAK,OAAO,IAAA,KAAS;AACpB,MAAA,IAAI,SAAS,MAAA,EAAW;AACtB,QAAA,QAAA,CAAS,MAAA,CAAO,GAAG,CAAA,CAAE,GAAA,EAAI;AACzB,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,MAAA,CAAO,EAAE,IAAA,EAAM,OAAA,EAAS,OAAA,CAAQ,OAAA,EAAS,CAAA;AAExE,MAAA,QAAA,CAAS,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,CAAE,GAAA,EAAI;AAAA,IACtC,CAAC,CAAA,CACA,KAAA,CAAM,IAAI,CAAA;AAAA,EACf,CAAA,CAAA;AAEA,EAAA,KAAA,CAAM,EAAA,IAAM,CAAC,IAAA,EAAc,OAAA,KAAkC;AAC3D,IAAA,QAAA,CAAS,EAAA,CAAG,MAAM,OAAO,CAAA;AACzB,IAAA,OAAO,KAAA;AAAA,EACT,CAAA,CAAA;AAEA,EAAA,OAAO,KAAA;AACT;AAKO,SAAS,eAAA,CAAgB,QAAA,EAAoB,KAAA,EAAiD,OAAA,GAAiC,EAAC,EAAS;AAC9I,EAAA,MAAM,YAAY,KAAA,CAAM,eAAA;AAExB,EAAA,IAAI,SAAA,EAAW,QAAA,CAAS,MAAA,CAAO,kBAAA,EAAoB,SAAS,CAAA;AAE5D,EAAA,QAAA,CAAS,MAAA,CAAO,MAAM,MAAM,CAAA;AAE5B,EAAA,IAAI,OAAA,CAAQ,cAAA,EAAgB,QAAA,CAAS,IAAA,CAAK,0BAA0B,CAAA,CAAE,IAAA,CAAK,IAAA,CAAK,SAAA,CAAU,gBAAA,CAAiB,KAAK,CAAC,CAAC,CAAA;AAAA,gBACpG,GAAA,EAAI;AACpB;AAEA,SAAS,MAAA,CAAO,OAAA,EAAkB,QAAA,EAAoB,IAAA,EAAoB,OAAgB,OAAA,EAAuC;AAC/H,EAAA,MAAM,QAAA,GAAW,OAAA,IAAY,OAAA,CAAgC,gBAAgB,KAAK,EAAC;AAEnF,EAAA,IAAI,EAAE,iBAAiB,iBAAA,CAAA,IAAsB,EAAE,iBAAiB,oBAAA,CAAA,EAAuB,OAAO,KAAK,KAAK,CAAA;AACxG,EAAA,IAAI,QAAA,CAAS,UAAA,EAAY,OAAO,IAAA,CAAK,KAAK,CAAA;AAE1C,EAAA,eAAA,CAAgB,QAAA,EAAU,OAAO,QAAQ,CAAA;AAC3C;AAEA,SAAS,WAAA,CAAY,SAAkB,WAAA,EAAsD;AAC3F,EAAA,MAAM,SAAU,OAAA,CAAyC,IAAA;AAEzD,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA,SAAU,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,UAAA,GAAa,WAAA,GAAc,MAAA,GAAY,IAAI,UAAA,CAAW,MAAM,CAAC,CAAA;AACxH,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,EAAU,OAAO,OAAA,CAAQ,OAAA,CAAQ,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,MAAM,CAAC,CAAA;AAEvF,EAAA,IAAI,OAAA,CAAQ,aAAA,IAAkB,MAAA,KAAW,MAAA,IAAa,MAAA,KAAW,IAAA,IAAQ,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,IAAA,CAAK,MAAM,EAAE,MAAA,GAAS,CAAA;AAClI,IAAA,OAAO,OAAA,CAAQ,MAAA;AAAA,MACb,IAAI,MAAM,yHAAyH;AAAA,KACrI;AAEF,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,gBAAgB,CAAC,CAAA;AAEzD,EAAA,IAAI,MAAA,CAAO,SAAS,QAAQ,CAAA,IAAK,WAAW,WAAA,EAAa,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAS,CAAA;AAEzF,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,MAAM,SAAmB,EAAC;AAC1B,IAAA,IAAI,IAAA,GAAO,CAAA;AACX,IAAA,IAAI,IAAA,GAAO,KAAA;AAEX,IAAA,OAAA,CAAQ,EAAA,CAAG,MAAA,EAAQ,CAAC,KAAA,KAAkB;AACpC,MAAA,IAAI,IAAA,EAAM;AAEV,MAAA,IAAA,IAAQ,KAAA,CAAM,UAAA;AAEd,MAAA,IAAI,OAAO,WAAA,EAAa;AACtB,QAAA,IAAA,GAAO,IAAA;AACP,QAAA,OAAA,CAAQ,KAAA,EAAM;AACd,QAAA,OAAA,CAAQ,MAAS,CAAA;AACjB,QAAA;AAAA,MACF;AAEA,MAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,IACnB,CAAC,CAAA;AACD,IAAA,OAAA,CAAQ,EAAA,CAAG,OAAO,MAAM;AACtB,MAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,IAAI,WAAW,MAAA,CAAO,MAAA,CAAO,MAAM,CAAC,CAAC,CAAA;AAAA,IAC1D,CAAC,CAAA;AACD,IAAA,OAAA,CAAQ,EAAA,CAAG,SAAS,MAAM,CAAA;AAAA,EAC5B,CAAC,CAAA;AACH;AAEA,SAAS,WAAW,KAAA,EAAwE;AAC1F,EAAA,OAAO,OAAQ,MAAyB,MAAA,KAAW,UAAA;AACrD","file":"index.js","sourcesContent":["import type { NextFunction, Request, RequestHandler, Response } from \"express\";\nimport type { AuthioResourceAccessSource, AuthioTypedWebhookEvent, AuthioWebhookDataByType } from \"@hyper.authio/client\";\nimport {\n allOf as allOfRule,\n anyOf as anyOfRule,\n authorize,\n AuthioBearerError,\n AuthioForbiddenError,\n createAuthioVerifier,\n createAuthioWebhooks,\n requireAuthenticationLevel as authenticationLevelRule,\n requireAuthenticationMethod as authenticationMethodRule,\n requireClaim as claimRule,\n requireClient as clientRule,\n requireGroup as groupRule,\n requireOrganization as organizationRule,\n requirePermission as permissionRule,\n requireRole as roleRule,\n requireScope as scopeRule,\n toProblemDetails,\n type AuthioMatch,\n type AuthioRule,\n type AuthioUser,\n type AuthioVerifier,\n type AuthioVerifierOptions,\n type AuthioWebhookHandler,\n type AuthioWebhookOptions,\n} from \"@hyper.authio/server\";\n\n/**\n * What `authioBearer` puts on the request.\n */\nexport interface AuthioRequestContext {\n /** The caller. */\n readonly user: AuthioUser;\n /** The access token the caller was read from. */\n readonly token: string;\n}\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Express {\n interface Request {\n /** The Authio caller, once `authioBearer` validated the token. */\n authio?: AuthioRequestContext;\n }\n }\n}\n\n/**\n * How refusals are answered.\n */\nexport interface AuthioResponseOptions {\n /** Answers `401` and `403` with problem details (RFC 9457) as well as the `WWW-Authenticate` header. */\n problemDetails?: boolean;\n /** Passes refusals to `next(error)` instead of answering, for an application's own error handler. */\n passErrors?: boolean;\n}\n\n/**\n * Options of `authioBearer`.\n */\nexport interface AuthioBearerOptions extends AuthioResponseOptions {\n /** Lets a request without a token through, unauthenticated; a token that is present must still be valid. */\n optional?: boolean;\n}\n\nconst responseSettings = Symbol.for(\"authio.express.response\");\n\ntype RequestWithSettings = Request & { [responseSettings]?: AuthioResponseOptions };\n\n/**\n * Validates the `Authorization: Bearer` token and puts the caller on `req.authio`. Answers `401` with the\n * `WWW-Authenticate` challenge when the token is missing or invalid.\n *\n * @param verifier A verifier, or its options.\n */\nexport function authioBearer(verifier: AuthioVerifier | AuthioVerifierOptions, options: AuthioBearerOptions = {}): RequestHandler {\n const resolved = isVerifier(verifier) ? verifier : createAuthioVerifier(verifier);\n\n return (request, response, next) => {\n (request as RequestWithSettings)[responseSettings] = { problemDetails: options.problemDetails, passErrors: options.passErrors };\n\n const header = request.headers.authorization;\n\n if (options.optional && !header) {\n next();\n return;\n }\n\n resolved\n .verifyAuthorizationHeader(header)\n .then((user) => {\n request.authio = { user, token: user.token! };\n next();\n })\n .catch((error: unknown) => refuse(request, response, next, error, options));\n };\n}\n\n/**\n * An Express guard that is also an Authio rule: use it as middleware, or pass it to `requireAuthio`, `allOf` and\n * `anyOf` like any rule of `@hyper.authio/server`.\n */\nexport type AuthioGuard = RequestHandler & AuthioRule;\n\n/**\n * Requires every rule (guards of this package or rules of `@hyper.authio/server`); answers `401` without a caller and\n * `403` with the joined refusal otherwise.\n */\nexport function requireAuthio(...rules: AuthioRule[]): AuthioGuard {\n if (rules.length === 0) throw new TypeError(\"requireAuthio needs at least one rule.\");\n\n const rule = rules.length === 1 ? rules[0]! : allOfRule(...rules);\n const guard: RequestHandler = (request, response, next) => {\n const user = request.authio?.user;\n\n if (!user) return refuse(request, response, next, new AuthioBearerError(undefined, { error: undefined }));\n\n const failure = authorize(user, ...rules);\n\n if (failure) return refuse(request, response, next, new AuthioForbiddenError(failure));\n\n next();\n };\n\n return Object.assign(guard, { description: rule.description, check: (user: AuthioUser) => authorize(user, ...rules) });\n}\n\n/** Requires granted scopes. */\nexport const requireScope = (scopes: string | readonly string[], match?: AuthioMatch): AuthioGuard => requireAuthio(scopeRule(scopes, match));\n/** Requires roles, from any source or only from the given `*_resource_access` claims. */\nexport const requireRole = (\n roles: string | readonly string[],\n options?: { match?: AuthioMatch; sources?: readonly AuthioResourceAccessSource[] },\n): AuthioGuard => requireAuthio(roleRule(roles, options));\n/** Requires a permission (`method` on `resource`). */\nexport const requirePermission = (resource: string, method: string, options?: { sources?: readonly AuthioResourceAccessSource[] }): AuthioGuard =>\n requireAuthio(permissionRule(resource, method, options));\n/** Requires groups. */\nexport const requireGroup = (groups: string | readonly string[], match?: AuthioMatch): AuthioGuard => requireAuthio(groupRule(groups, match));\n/** Requires a claim, or one of its values. */\nexport const requireClaim = (type: string, ...values: string[]): AuthioGuard => requireAuthio(claimRule(type, ...values));\n/** Requires an organization context, or a given organization. */\nexport const requireOrganization = (organization?: string): AuthioGuard => requireAuthio(organizationRule(organization));\n/** Requires a minimum authentication level (`acr`). */\nexport const requireAuthenticationLevel = (minimumLevel: number): AuthioGuard => requireAuthio(authenticationLevelRule(minimumLevel));\n/** Requires authentication methods (`amr`). */\nexport const requireAuthenticationMethod = (methods: string | readonly string[], match?: AuthioMatch): AuthioGuard =>\n requireAuthio(authenticationMethodRule(methods, match));\n/** Requires the token to have been issued to one of the clients. */\nexport const requireClient = (clientIds: string | readonly string[]): AuthioGuard => requireAuthio(clientRule(clientIds));\n/** Requires every rule. */\nexport const allOf = (...rules: AuthioRule[]): AuthioGuard => requireAuthio(allOfRule(...rules));\n/** Requires at least one rule. */\nexport const anyOf = (...rules: AuthioRule[]): AuthioGuard => requireAuthio(anyOfRule(...rules));\n\n/**\n * A webhook route: verifies the signature over the raw body, deduplicates and dispatches. Mount it before any JSON\n * body parser, or behind `express.raw()`.\n */\nexport interface AuthioWebhookRoute extends RequestHandler {\n on<T extends keyof AuthioWebhookDataByType>(type: T, handler: AuthioWebhookHandler<AuthioTypedWebhookEvent<T>>): AuthioWebhookRoute;\n on(type: string, handler: AuthioWebhookHandler): AuthioWebhookRoute;\n}\n\n/**\n * Creates a webhook route: `app.post(\"/webhooks/authio\", authioWebhooks({ secret }).on(\"user_created\", …))`.\n * Answers `200` handled or duplicate, `400` unreadable, `401` bad signature, `413` too large, `500` handler failure.\n */\nexport function authioWebhooks(options: AuthioWebhookOptions): AuthioWebhookRoute {\n const webhooks = createAuthioWebhooks(options);\n\n const route = ((request: Request, response: Response, next: NextFunction) => {\n readRawBody(request, webhooks.maxBodySize)\n .then(async (body) => {\n if (body === undefined) {\n response.status(413).end();\n return;\n }\n\n const outcome = await webhooks.handle({ body, headers: request.headers });\n\n response.status(outcome.status).end();\n })\n .catch(next);\n }) as AuthioWebhookRoute;\n\n route.on = ((type: string, handler: AuthioWebhookHandler) => {\n webhooks.on(type, handler);\n return route;\n }) as AuthioWebhookRoute[\"on\"];\n\n return route;\n}\n\n/**\n * Answers a refusal: `WWW-Authenticate`, the status, and problem details when asked.\n */\nexport function sendAuthioError(response: Response, error: AuthioBearerError | AuthioForbiddenError, options: AuthioResponseOptions = {}): void {\n const challenge = error.wwwAuthenticate;\n\n if (challenge) response.append(\"WWW-Authenticate\", challenge);\n\n response.status(error.status);\n\n if (options.problemDetails) response.type(\"application/problem+json\").send(JSON.stringify(toProblemDetails(error)));\n else response.end();\n}\n\nfunction refuse(request: Request, response: Response, next: NextFunction, error: unknown, options?: AuthioResponseOptions): void {\n const settings = options ?? (request as RequestWithSettings)[responseSettings] ?? {};\n\n if (!(error instanceof AuthioBearerError) && !(error instanceof AuthioForbiddenError)) return next(error);\n if (settings.passErrors) return next(error);\n\n sendAuthioError(response, error, settings);\n}\n\nfunction readRawBody(request: Request, maxBodySize: number): Promise<Uint8Array | undefined> {\n const parsed = (request as Request & { body?: unknown }).body;\n\n if (Buffer.isBuffer(parsed)) return Promise.resolve(parsed.byteLength > maxBodySize ? undefined : new Uint8Array(parsed));\n if (typeof parsed === \"string\") return Promise.resolve(new TextEncoder().encode(parsed));\n\n if (request.readableEnded || (parsed !== undefined && parsed !== null && typeof parsed === \"object\" && Object.keys(parsed).length > 0))\n return Promise.reject(\n new Error(\"The Authio webhook route needs the raw body: mount it before express.json(), or use express.raw({ type: '*/*' }) on it.\"),\n );\n\n const declared = Number(request.headers[\"content-length\"]);\n\n if (Number.isFinite(declared) && declared > maxBodySize) return Promise.resolve(undefined);\n\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let done = false;\n\n request.on(\"data\", (chunk: Buffer) => {\n if (done) return;\n\n size += chunk.byteLength;\n\n if (size > maxBodySize) {\n done = true;\n request.pause();\n resolve(undefined);\n return;\n }\n\n chunks.push(chunk);\n });\n request.on(\"end\", () => {\n if (!done) resolve(new Uint8Array(Buffer.concat(chunks)));\n });\n request.on(\"error\", reject);\n });\n}\n\nfunction isVerifier(value: AuthioVerifier | AuthioVerifierOptions): value is AuthioVerifier {\n return typeof (value as AuthioVerifier).verify === \"function\";\n}\n\nexport type { AuthioMatch, AuthioRule, AuthioUser, AuthioVerifier, AuthioVerifierOptions } from \"@hyper.authio/server\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@hyper.authio/express",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Express middleware for Authio: bearer validation, scope/role/permission/group/claim/organization/acr/amr/client guards, and signed webhook routes.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.cts",
19
+ "default": "./dist/index.cjs"
20
+ }
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md"
27
+ ],
28
+ "engines": {
29
+ "node": ">=20.19"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup"
33
+ },
34
+ "dependencies": {
35
+ "@hyper.authio/client": "0.1.1",
36
+ "@hyper.authio/server": "0.1.1"
37
+ },
38
+ "keywords": [
39
+ "authio",
40
+ "express",
41
+ "oauth",
42
+ "jwt",
43
+ "authorization",
44
+ "middleware"
45
+ ],
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/LeonardoFerreira1209/authio-node.git",
49
+ "directory": "packages/express"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "peerDependencies": {
55
+ "express": ">=4.18 <6"
56
+ }
57
+ }