@keycardai/express 0.1.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 (44) hide show
  1. package/README.md +77 -0
  2. package/dist/cjs/bearerAuth.d.ts +76 -0
  3. package/dist/cjs/bearerAuth.d.ts.map +1 -0
  4. package/dist/cjs/bearerAuth.js +123 -0
  5. package/dist/cjs/bearerAuth.js.map +1 -0
  6. package/dist/cjs/grant.d.ts +46 -0
  7. package/dist/cjs/grant.d.ts.map +1 -0
  8. package/dist/cjs/grant.js +99 -0
  9. package/dist/cjs/grant.js.map +1 -0
  10. package/dist/cjs/index.d.ts +9 -0
  11. package/dist/cjs/index.d.ts.map +1 -0
  12. package/dist/cjs/index.js +12 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/middleware.d.ts +54 -0
  15. package/dist/cjs/middleware.d.ts.map +1 -0
  16. package/dist/cjs/middleware.js +49 -0
  17. package/dist/cjs/middleware.js.map +1 -0
  18. package/dist/cjs/package.json +1 -0
  19. package/dist/cjs/wellKnown.d.ts +48 -0
  20. package/dist/cjs/wellKnown.d.ts.map +1 -0
  21. package/dist/cjs/wellKnown.js +73 -0
  22. package/dist/cjs/wellKnown.js.map +1 -0
  23. package/dist/esm/bearerAuth.d.ts +76 -0
  24. package/dist/esm/bearerAuth.d.ts.map +1 -0
  25. package/dist/esm/bearerAuth.js +120 -0
  26. package/dist/esm/bearerAuth.js.map +1 -0
  27. package/dist/esm/grant.d.ts +46 -0
  28. package/dist/esm/grant.d.ts.map +1 -0
  29. package/dist/esm/grant.js +96 -0
  30. package/dist/esm/grant.js.map +1 -0
  31. package/dist/esm/index.d.ts +9 -0
  32. package/dist/esm/index.d.ts.map +1 -0
  33. package/dist/esm/index.js +5 -0
  34. package/dist/esm/index.js.map +1 -0
  35. package/dist/esm/middleware.d.ts +54 -0
  36. package/dist/esm/middleware.d.ts.map +1 -0
  37. package/dist/esm/middleware.js +46 -0
  38. package/dist/esm/middleware.js.map +1 -0
  39. package/dist/esm/package.json +1 -0
  40. package/dist/esm/wellKnown.d.ts +48 -0
  41. package/dist/esm/wellKnown.d.ts.map +1 -0
  42. package/dist/esm/wellKnown.js +70 -0
  43. package/dist/esm/wellKnown.js.map +1 -0
  44. package/package.json +53 -0
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # @keycardai/express
2
+
3
+ > **Preview.** This SDK has not reached parity with the Keycard Python SDK. APIs may change between minor versions.
4
+
5
+ Keycard auth middleware for Express. Wraps Express's standard middleware idioms for protecting HTTP APIs with Keycard: bearer token validation (RFC 6750), delegated token exchange (RFC 8693), and OAuth discovery routes (RFC 9728 + RFC 8414).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @keycardai/express express
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### Protect routes with `requireBearerAuth`
16
+
17
+ ```typescript
18
+ import express from "express";
19
+ import { requireBearerAuth } from "@keycardai/express";
20
+
21
+ const app = express();
22
+
23
+ app.use(requireBearerAuth({ issuer: "https://your-zone.keycard.cloud" }));
24
+
25
+ app.get("/api/data", (req, res) => {
26
+ // req.auth is AccessToken: { token, clientId, scopes, ... }
27
+ res.json({ clientId: req.auth.clientId });
28
+ });
29
+ ```
30
+
31
+ ### Delegate tokens with `grant`
32
+
33
+ ```typescript
34
+ import { requireBearerAuth, grant } from "@keycardai/express";
35
+ import { ClientSecret } from "@keycardai/oauth/server";
36
+
37
+ const credential = new ClientSecret("your-client-id", "your-client-secret");
38
+
39
+ app.use(requireBearerAuth({ issuer: "https://your-zone.keycard.cloud" }));
40
+ app.use(grant(["https://graph.microsoft.com"], {
41
+ zoneUrl: "https://your-zone.keycard.cloud",
42
+ applicationCredential: credential,
43
+ }));
44
+
45
+ app.get("/api/email", async (req, res) => {
46
+ const token = req.accessContext.access("https://graph.microsoft.com");
47
+ // use token.accessToken to call Graph API
48
+ res.json({ ok: true });
49
+ });
50
+ ```
51
+
52
+ ### Add OAuth discovery routes
53
+
54
+ ```typescript
55
+ import { keycardMetadataRouter } from "@keycardai/express";
56
+
57
+ app.use(keycardMetadataRouter({ issuer: "https://your-zone.keycard.cloud" }));
58
+ // Serves:
59
+ // GET /.well-known/oauth-protected-resource (RFC 9728)
60
+ // GET /.well-known/oauth-authorization-server (RFC 8414, proxied)
61
+ ```
62
+
63
+ ## API
64
+
65
+ | Export | Description |
66
+ |---|---|
67
+ | `requireBearerAuth(options)` | Middleware factory that validates a Bearer token and sets `req.auth: AccessToken`. Returns 401 with RFC 6750 `WWW-Authenticate` challenge on failure. |
68
+ | `grant(resources, options)` | Middleware factory that exchanges the bearer token for per-resource access tokens and sets `req.accessContext: AccessContext`. Must run after `requireBearerAuth`. |
69
+ | `keycardMetadataRouter(options)` | Returns an Express Router with `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server` routes. |
70
+ | `AuthenticatedRequest` | `Request` extended with `auth: AccessToken`. |
71
+ | `GrantedRequest` | `Request` extended with `auth: AccessToken` and `accessContext: AccessContext`. |
72
+
73
+ ## Related Packages
74
+
75
+ - [`@keycardai/oauth`](../oauth/) — Framework-free OAuth primitives this package builds on
76
+ - [`@keycardai/mcp`](../mcp/) — MCP-specific OAuth integration
77
+ - [Keycard TypeScript SDK](../../README.md) — Root documentation
@@ -0,0 +1,76 @@
1
+ import type { Request, RequestHandler } from "express";
2
+ import { TokenVerifier } from "@keycardai/oauth/server/tokenVerifier";
3
+ import type { TokenVerifierOptions } from "@keycardai/oauth/server/tokenVerifier";
4
+ import type { AccessToken } from "@keycardai/oauth/server/accessToken";
5
+ /**
6
+ * Extends Express `Request` with the verified Keycard `AccessToken`.
7
+ *
8
+ * Cast inside handlers that run after `requireBearerAuth()`:
9
+ * ```ts
10
+ * app.get("/data", (req, res) => {
11
+ * const { auth } = req as AuthenticatedRequest;
12
+ * });
13
+ * ```
14
+ *
15
+ * Alternatively, adopt Express module augmentation so `req.auth` is
16
+ * available without casting across your entire app:
17
+ * ```ts
18
+ * import type { AccessToken } from "@keycardai/oauth/server";
19
+ * declare global {
20
+ * namespace Express {
21
+ * interface Request {
22
+ * auth?: AccessToken;
23
+ * }
24
+ * }
25
+ * }
26
+ * ```
27
+ * We ship the interface-extension form rather than augmenting the global
28
+ * namespace by default. Augmentation makes `req.auth` optional on every
29
+ * request including unauthenticated routes, which weakens the type
30
+ * contract. Use it when you prefer convenience over strictness.
31
+ * See: https://github.com/auth0/express-jwt/issues/311
32
+ */
33
+ export interface AuthenticatedRequest extends Request {
34
+ auth: AccessToken;
35
+ }
36
+ export type BearerAuthOptions = {
37
+ verifier: TokenVerifier;
38
+ requiredScopes?: readonly string[];
39
+ } | {
40
+ /**
41
+ * Keycard zone URL, e.g. "https://zone-id.keycard.cloud".
42
+ * Either `zoneUrl` or `zoneId` is required (consistent with `grant()`).
43
+ */
44
+ zoneUrl?: string;
45
+ /**
46
+ * Keycard zone ID. Constructs the URL as `https://{zoneId}.keycard.cloud`.
47
+ * Either `zoneUrl` or `zoneId` is required (consistent with `grant()`).
48
+ */
49
+ zoneId?: string;
50
+ audience?: string;
51
+ enableMultiZone?: boolean;
52
+ keyring?: TokenVerifierOptions["keyring"];
53
+ requiredScopes?: readonly string[];
54
+ };
55
+ /**
56
+ * Express middleware that validates a Bearer token (RFC 6750) and sets
57
+ * `req.auth` to the verified `AccessToken`.
58
+ *
59
+ * On failure: responds with a `WWW-Authenticate` challenge containing the
60
+ * `resource_metadata` URL per RFC 9728 §3.
61
+ *
62
+ * Usage with a zone URL:
63
+ * ```ts
64
+ * app.use(requireBearerAuth({ zoneUrl: "https://zone.keycard.cloud" }));
65
+ * // or by zone ID
66
+ * app.use(requireBearerAuth({ zoneId: "zone-id" }));
67
+ * ```
68
+ *
69
+ * Usage with a pre-built verifier (shared across routes):
70
+ * ```ts
71
+ * const verifier = new TokenVerifier({ issuer: "https://zone.keycard.cloud" });
72
+ * app.use(requireBearerAuth({ verifier }));
73
+ * ```
74
+ */
75
+ export declare function requireBearerAuth(options: BearerAuthOptions): RequestHandler;
76
+ //# sourceMappingURL=bearerAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearerAuth.d.ts","sourceRoot":"","sources":["../../src/bearerAuth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AASvE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,oBAAqB,SAAQ,OAAO;IACnD,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,EAAE,aAAa,CAAC;IAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC/D;IACE;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC1C,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,CAAC;AAEN;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAgG5E"}
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireBearerAuth = requireBearerAuth;
4
+ const tokenVerifier_1 = require("@keycardai/oauth/server/tokenVerifier");
5
+ const errors_1 = require("@keycardai/oauth/errors");
6
+ /**
7
+ * Express middleware that validates a Bearer token (RFC 6750) and sets
8
+ * `req.auth` to the verified `AccessToken`.
9
+ *
10
+ * On failure: responds with a `WWW-Authenticate` challenge containing the
11
+ * `resource_metadata` URL per RFC 9728 §3.
12
+ *
13
+ * Usage with a zone URL:
14
+ * ```ts
15
+ * app.use(requireBearerAuth({ zoneUrl: "https://zone.keycard.cloud" }));
16
+ * // or by zone ID
17
+ * app.use(requireBearerAuth({ zoneId: "zone-id" }));
18
+ * ```
19
+ *
20
+ * Usage with a pre-built verifier (shared across routes):
21
+ * ```ts
22
+ * const verifier = new TokenVerifier({ issuer: "https://zone.keycard.cloud" });
23
+ * app.use(requireBearerAuth({ verifier }));
24
+ * ```
25
+ */
26
+ function requireBearerAuth(options) {
27
+ // Do not pass requiredScopes to TokenVerifier: it returns null on scope
28
+ // failure, which the middleware would interpret as a generic 401. The
29
+ // explicit scope check below produces the correct 403 InsufficientScopeError.
30
+ let verifier;
31
+ if ("verifier" in options) {
32
+ verifier = options.verifier;
33
+ }
34
+ else {
35
+ const issuer = options.zoneUrl ?? buildIssuerFromZoneId(options.zoneId);
36
+ if (!issuer) {
37
+ throw new Error("requireBearerAuth: either `zoneUrl` or `zoneId` is required");
38
+ }
39
+ verifier = new tokenVerifier_1.TokenVerifier({
40
+ issuer,
41
+ audience: options.audience,
42
+ enableMultiZone: options.enableMultiZone,
43
+ keyring: options.keyring,
44
+ });
45
+ }
46
+ return async (req, res, next) => {
47
+ const resourceMetadataUrl = getResourceMetadataUrl(req);
48
+ try {
49
+ const authorization = req.headers.authorization;
50
+ if (!authorization) {
51
+ throw new errors_1.UnauthorizedError("No credentials");
52
+ }
53
+ const [scheme, token] = authorization.split(" ");
54
+ if (!token) {
55
+ throw new errors_1.BadRequestError("Malformed credentials");
56
+ }
57
+ if (scheme.toLowerCase() !== "bearer") {
58
+ throw new errors_1.InvalidTokenError("Unsupported authentication scheme");
59
+ }
60
+ const accessToken = await verifier.verifyToken(token);
61
+ if (!accessToken) {
62
+ throw new errors_1.InvalidTokenError("Token validation failed");
63
+ }
64
+ // Validate resource audience: a token scoped to a different resource
65
+ // server must not be accepted here. Compare origins so path and query
66
+ // string differences are ignored (mirrors Workers auth.ts:88-92).
67
+ if (accessToken.resource) {
68
+ const requestOrigin = `${req.protocol}://${req.host}`;
69
+ try {
70
+ const tokenOrigin = new URL(accessToken.resource).origin;
71
+ if (tokenOrigin !== requestOrigin) {
72
+ throw new errors_1.InvalidTokenError("Token not intended for resource");
73
+ }
74
+ }
75
+ catch (e) {
76
+ if (e instanceof errors_1.InvalidTokenError)
77
+ throw e;
78
+ // resource claim is not a URL; opaque audience, skip origin check
79
+ }
80
+ }
81
+ if ("requiredScopes" in options &&
82
+ options.requiredScopes &&
83
+ options.requiredScopes.length > 0) {
84
+ const hasAllScopes = options.requiredScopes.every((scope) => accessToken.scopes.includes(scope));
85
+ if (!hasAllScopes) {
86
+ throw new errors_1.InsufficientScopeError("Insufficient scope");
87
+ }
88
+ }
89
+ req.auth = accessToken;
90
+ next();
91
+ }
92
+ catch (error) {
93
+ if (error instanceof errors_1.BadRequestError) {
94
+ res.status(400).end();
95
+ }
96
+ else if (error instanceof errors_1.UnauthorizedError) {
97
+ res.set("WWW-Authenticate", `Bearer resource_metadata="${resourceMetadataUrl}"`);
98
+ res.status(401).end();
99
+ }
100
+ else if (error instanceof errors_1.InsufficientScopeError) {
101
+ res.set("WWW-Authenticate", `Bearer error="${error.errorCode}", error_description="${error.message}", resource_metadata="${resourceMetadataUrl}"`);
102
+ res.status(403).end();
103
+ }
104
+ else if (error instanceof errors_1.OAuthError || error instanceof errors_1.InvalidTokenError) {
105
+ res.set("WWW-Authenticate", `Bearer error="${error.errorCode}", error_description="${error.message}", resource_metadata="${resourceMetadataUrl}"`);
106
+ res.status(401).end();
107
+ }
108
+ else {
109
+ next(error);
110
+ }
111
+ }
112
+ };
113
+ }
114
+ function getResourceMetadataUrl(req) {
115
+ const origin = `${req.protocol}://${req.host}`;
116
+ return `${origin}/.well-known/oauth-protected-resource`;
117
+ }
118
+ function buildIssuerFromZoneId(zoneId) {
119
+ if (!zoneId)
120
+ return undefined;
121
+ return `https://${zoneId}.keycard.cloud`;
122
+ }
123
+ //# sourceMappingURL=bearerAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearerAuth.js","sourceRoot":"","sources":["../../src/bearerAuth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { RequestHandler } from "express";
2
+ import { AccessContext } from "@keycardai/oauth/server/accessContext";
3
+ import type { ApplicationCredential } from "@keycardai/oauth/credentials";
4
+ import type { AuthenticatedRequest } from "./bearerAuth.js";
5
+ export interface GrantedRequest extends AuthenticatedRequest {
6
+ accessContext: AccessContext;
7
+ }
8
+ export interface GrantOptions {
9
+ /**
10
+ * Keycard zone URL, e.g. "https://zone-id.keycard.cloud".
11
+ * Either `zoneUrl` or `zoneId` is required.
12
+ */
13
+ zoneUrl?: string;
14
+ /**
15
+ * Keycard zone ID. Constructs the zone URL as
16
+ * `https://{zoneId}.keycard.cloud`.
17
+ */
18
+ zoneId?: string;
19
+ /**
20
+ * Application credential provider for authenticated token exchange.
21
+ * When omitted, the bearer token is exchanged without client auth.
22
+ */
23
+ applicationCredential?: ApplicationCredential;
24
+ }
25
+ /**
26
+ * Express middleware factory for delegated token exchange (RFC 8693).
27
+ *
28
+ * Must run AFTER `requireBearerAuth()`. Reads the verified bearer token
29
+ * from `req.auth`, exchanges it for per-resource access tokens at the
30
+ * Keycard zone, and stores the results in `req.accessContext`.
31
+ *
32
+ * On success, `req.accessContext.access(resourceUrl)` returns the
33
+ * `TokenResponse` for that resource. On partial failure, some resources
34
+ * may have errors while others succeed.
35
+ *
36
+ * ```ts
37
+ * app.use(requireBearerAuth({ issuer: "https://zone.keycard.cloud" }));
38
+ * app.use(grant(["https://graph.microsoft.com"], { zoneUrl: "https://zone.keycard.cloud" }));
39
+ * app.get("/data", (req, res) => {
40
+ * const token = req.accessContext.access("https://graph.microsoft.com");
41
+ * // ...
42
+ * });
43
+ * ```
44
+ */
45
+ export declare function grant(resources: string | readonly string[], options: GrantOptions): RequestHandler;
46
+ //# sourceMappingURL=grant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant.d.ts","sourceRoot":"","sources":["../../src/grant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE1E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAG5D,MAAM,WAAW,cAAe,SAAQ,oBAAoB;IAC1D,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;CAC/C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,KAAK,CACnB,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EACrC,OAAO,EAAE,YAAY,GACpB,cAAc,CA2EhB"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.grant = grant;
4
+ const tokenExchange_1 = require("@keycardai/oauth/tokenExchange");
5
+ const accessContext_1 = require("@keycardai/oauth/server/accessContext");
6
+ const errors_1 = require("@keycardai/oauth/errors");
7
+ /**
8
+ * Express middleware factory for delegated token exchange (RFC 8693).
9
+ *
10
+ * Must run AFTER `requireBearerAuth()`. Reads the verified bearer token
11
+ * from `req.auth`, exchanges it for per-resource access tokens at the
12
+ * Keycard zone, and stores the results in `req.accessContext`.
13
+ *
14
+ * On success, `req.accessContext.access(resourceUrl)` returns the
15
+ * `TokenResponse` for that resource. On partial failure, some resources
16
+ * may have errors while others succeed.
17
+ *
18
+ * ```ts
19
+ * app.use(requireBearerAuth({ issuer: "https://zone.keycard.cloud" }));
20
+ * app.use(grant(["https://graph.microsoft.com"], { zoneUrl: "https://zone.keycard.cloud" }));
21
+ * app.get("/data", (req, res) => {
22
+ * const token = req.accessContext.access("https://graph.microsoft.com");
23
+ * // ...
24
+ * });
25
+ * ```
26
+ */
27
+ function grant(resources, options) {
28
+ const zoneUrl = options.zoneUrl ?? buildZoneUrl(options.zoneId);
29
+ if (!zoneUrl) {
30
+ throw new errors_1.AuthProviderConfigurationError("grant: either `zoneUrl` or `zoneId` is required");
31
+ }
32
+ return async (req, _res, next) => {
33
+ const authReq = req;
34
+ const subjectToken = authReq.auth?.token;
35
+ const accessCtx = new accessContext_1.AccessContext();
36
+ if (!subjectToken) {
37
+ accessCtx.setError({
38
+ message: "No authentication token. Ensure requireBearerAuth() runs before grant().",
39
+ });
40
+ req.accessContext = accessCtx;
41
+ return next();
42
+ }
43
+ let client;
44
+ try {
45
+ const auth = options.applicationCredential?.getAuth();
46
+ client = new tokenExchange_1.TokenExchangeClient(zoneUrl, auth ?? undefined);
47
+ }
48
+ catch (e) {
49
+ accessCtx.setError({
50
+ message: "Failed to initialize token exchange client.",
51
+ rawError: String(e),
52
+ });
53
+ req.accessContext = accessCtx;
54
+ return next();
55
+ }
56
+ const resourceList = Array.isArray(resources)
57
+ ? resources
58
+ : [resources];
59
+ const tokens = {};
60
+ for (const resource of resourceList) {
61
+ try {
62
+ let exchangeRequest;
63
+ if (options.applicationCredential) {
64
+ exchangeRequest = await options.applicationCredential.prepareTokenExchangeRequest(subjectToken, resource);
65
+ }
66
+ else {
67
+ exchangeRequest = {
68
+ subjectToken,
69
+ resource,
70
+ subjectTokenType: "urn:ietf:params:oauth:token-type:access_token",
71
+ };
72
+ }
73
+ tokens[resource] = await client.exchangeToken(exchangeRequest);
74
+ }
75
+ catch (e) {
76
+ const detail = {
77
+ message: `Token exchange failed for ${resource}`,
78
+ };
79
+ if (e instanceof errors_1.OAuthError) {
80
+ detail.code = e.errorCode;
81
+ detail.description = e.message;
82
+ }
83
+ else {
84
+ detail.rawError = String(e);
85
+ }
86
+ accessCtx.setResourceError(resource, detail);
87
+ }
88
+ }
89
+ accessCtx.setBulkTokens(tokens);
90
+ req.accessContext = accessCtx;
91
+ next();
92
+ };
93
+ }
94
+ function buildZoneUrl(zoneId) {
95
+ if (!zoneId)
96
+ return undefined;
97
+ return `https://${zoneId}.keycard.cloud`;
98
+ }
99
+ //# sourceMappingURL=grant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant.js","sourceRoot":"","sources":["../../src/grant.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export { requireBearerAuth } from "./bearerAuth.js";
2
+ export type { AuthenticatedRequest, BearerAuthOptions } from "./bearerAuth.js";
3
+ export { grant } from "./grant.js";
4
+ export type { GrantedRequest, GrantOptions } from "./grant.js";
5
+ export { keycardMetadataRouter } from "./wellKnown.js";
6
+ export type { KeycardRouterOptions } from "./wellKnown.js";
7
+ export { createKeycardMiddleware } from "./middleware.js";
8
+ export type { KeycardMiddlewareOptions, KeycardMiddleware } from "./middleware.js";
9
+ //# 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,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createKeycardMiddleware = exports.keycardMetadataRouter = exports.grant = exports.requireBearerAuth = void 0;
4
+ var bearerAuth_js_1 = require("./bearerAuth.js");
5
+ Object.defineProperty(exports, "requireBearerAuth", { enumerable: true, get: function () { return bearerAuth_js_1.requireBearerAuth; } });
6
+ var grant_js_1 = require("./grant.js");
7
+ Object.defineProperty(exports, "grant", { enumerable: true, get: function () { return grant_js_1.grant; } });
8
+ var wellKnown_js_1 = require("./wellKnown.js");
9
+ Object.defineProperty(exports, "keycardMetadataRouter", { enumerable: true, get: function () { return wellKnown_js_1.keycardMetadataRouter; } });
10
+ var middleware_js_1 = require("./middleware.js");
11
+ Object.defineProperty(exports, "createKeycardMiddleware", { enumerable: true, get: function () { return middleware_js_1.createKeycardMiddleware; } });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAoD;AAA3C,kHAAA,iBAAiB,OAAA;AAE1B,uCAAmC;AAA1B,iGAAA,KAAK,OAAA;AAEd,+CAAuD;AAA9C,qHAAA,qBAAqB,OAAA;AAE9B,iDAA0D;AAAjD,wHAAA,uBAAuB,OAAA"}
@@ -0,0 +1,54 @@
1
+ import type { RequestHandler } from "express";
2
+ import type { ApplicationCredential } from "@keycardai/oauth/credentials";
3
+ export interface KeycardMiddlewareOptions {
4
+ /**
5
+ * Keycard zone URL, e.g. "https://zone-id.keycard.cloud".
6
+ * Either `zoneUrl` or `zoneId` is required.
7
+ */
8
+ zoneUrl?: string;
9
+ /**
10
+ * Keycard zone ID. Constructs the URL as `https://{zoneId}.keycard.cloud`.
11
+ */
12
+ zoneId?: string;
13
+ /**
14
+ * Application credential for token exchange in `grant()`.
15
+ * Typically a `ClientSecret` from `@keycardai/oauth/server`.
16
+ */
17
+ applicationCredential?: ApplicationCredential;
18
+ }
19
+ export interface KeycardMiddleware {
20
+ /**
21
+ * Express middleware that validates a Bearer token and sets `req.auth`.
22
+ * Accepts optional `requiredScopes` to enforce at the middleware level.
23
+ */
24
+ requireBearerAuth(options?: {
25
+ requiredScopes?: readonly string[];
26
+ }): RequestHandler;
27
+ /**
28
+ * Express middleware for delegated RFC 8693 token exchange.
29
+ * Sets `req.accessContext` with per-resource tokens.
30
+ */
31
+ grant(resources: string | readonly string[], options?: {
32
+ applicationCredential?: ApplicationCredential;
33
+ }): RequestHandler;
34
+ }
35
+ /**
36
+ * Creates a pair of pre-configured Keycard middleware functions sharing a
37
+ * common zone URL. Eliminates the naming mismatch between `requireBearerAuth`
38
+ * (which takes `issuer`) and `grant` (which takes `zoneUrl`/`zoneId`) by
39
+ * accepting a single consistent config.
40
+ *
41
+ * Python equivalent: `AuthProvider(zone_url=..., application_credential=...)`
42
+ *
43
+ * ```ts
44
+ * const keycard = createKeycardMiddleware({
45
+ * zoneUrl: "https://zone.keycard.cloud",
46
+ * applicationCredential: new ClientSecret("client-id", "client-secret"),
47
+ * });
48
+ *
49
+ * app.use(keycard.requireBearerAuth());
50
+ * app.use(keycard.grant(["https://graph.microsoft.com"]));
51
+ * ```
52
+ */
53
+ export declare function createKeycardMiddleware(options: KeycardMiddlewareOptions): KeycardMiddleware;
54
+ //# 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,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE1E,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;CAC/C;AAED,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,iBAAiB,CAAC,OAAO,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,cAAc,CAAC;IACpF;;;OAGG;IACH,KAAK,CACH,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EACrC,OAAO,CAAC,EAAE;QACR,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;KAC/C,GACA,cAAc,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAsB5F"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createKeycardMiddleware = createKeycardMiddleware;
4
+ const bearerAuth_js_1 = require("./bearerAuth.js");
5
+ const grant_js_1 = require("./grant.js");
6
+ /**
7
+ * Creates a pair of pre-configured Keycard middleware functions sharing a
8
+ * common zone URL. Eliminates the naming mismatch between `requireBearerAuth`
9
+ * (which takes `issuer`) and `grant` (which takes `zoneUrl`/`zoneId`) by
10
+ * accepting a single consistent config.
11
+ *
12
+ * Python equivalent: `AuthProvider(zone_url=..., application_credential=...)`
13
+ *
14
+ * ```ts
15
+ * const keycard = createKeycardMiddleware({
16
+ * zoneUrl: "https://zone.keycard.cloud",
17
+ * applicationCredential: new ClientSecret("client-id", "client-secret"),
18
+ * });
19
+ *
20
+ * app.use(keycard.requireBearerAuth());
21
+ * app.use(keycard.grant(["https://graph.microsoft.com"]));
22
+ * ```
23
+ */
24
+ function createKeycardMiddleware(options) {
25
+ const zoneUrl = options.zoneUrl ?? buildZoneUrl(options.zoneId);
26
+ if (!zoneUrl) {
27
+ throw new Error("createKeycardMiddleware: either `zoneUrl` or `zoneId` is required");
28
+ }
29
+ return {
30
+ requireBearerAuth(localOptions) {
31
+ return (0, bearerAuth_js_1.requireBearerAuth)({
32
+ zoneUrl,
33
+ requiredScopes: localOptions?.requiredScopes,
34
+ });
35
+ },
36
+ grant(resources, localOptions) {
37
+ return (0, grant_js_1.grant)(resources, {
38
+ zoneUrl,
39
+ applicationCredential: localOptions?.applicationCredential ?? options.applicationCredential,
40
+ });
41
+ },
42
+ };
43
+ }
44
+ function buildZoneUrl(zoneId) {
45
+ if (!zoneId)
46
+ return undefined;
47
+ return `https://${zoneId}.keycard.cloud`;
48
+ }
49
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":";;AA0DA,0DAsBC;AA/ED,mDAAoD;AACpD,yCAAmC;AAsCnC;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,uBAAuB,CAAC,OAAiC;IACvE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACvF,CAAC;IAED,OAAO;QACL,iBAAiB,CAAC,YAAqD;YACrE,OAAO,IAAA,iCAAiB,EAAC;gBACvB,OAAO;gBACP,cAAc,EAAE,YAAY,EAAE,cAAc;aAC7C,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,SAAS,EAAE,YAAY;YAC3B,OAAO,IAAA,gBAAK,EAAC,SAAS,EAAE;gBACtB,OAAO;gBACP,qBAAqB,EACnB,YAAY,EAAE,qBAAqB,IAAI,OAAO,CAAC,qBAAqB;aACvE,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,MAAe;IACnC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,OAAO,WAAW,MAAM,gBAAgB,CAAC;AAC3C,CAAC"}
@@ -0,0 +1 @@
1
+ {"type": "commonjs"}
@@ -0,0 +1,48 @@
1
+ import { Router } from "express";
2
+ export interface KeycardRouterOptions {
3
+ /**
4
+ * Keycard issuer URL, e.g. "https://zone-id.keycard.cloud".
5
+ * Used to proxy AS metadata from the Keycard authorization server.
6
+ */
7
+ issuer: string;
8
+ /**
9
+ * Human-readable resource name shown in AS metadata.
10
+ */
11
+ resourceName?: string;
12
+ /**
13
+ * Scopes this resource server supports.
14
+ */
15
+ scopesSupported?: readonly string[];
16
+ /**
17
+ * Link to documentation for this resource.
18
+ */
19
+ resourceDocumentation?: string;
20
+ /**
21
+ * Timeout in milliseconds for the upstream AS metadata fetch.
22
+ * Default: 10 000 ms.
23
+ */
24
+ asMetadataTimeoutMs?: number;
25
+ }
26
+ /**
27
+ * Returns an Express Router that serves the two OAuth discovery endpoints
28
+ * required by RFC 9728 and RFC 8414:
29
+ *
30
+ * - `GET /.well-known/oauth-protected-resource` (RFC 9728 §2)
31
+ * - `GET /.well-known/oauth-authorization-server` (RFC 8414 §3, proxied)
32
+ *
33
+ * Mount it at the application root:
34
+ * ```ts
35
+ * import express from "express";
36
+ * import { keycardMetadataRouter } from "@keycardai/express";
37
+ *
38
+ * const app = express();
39
+ * app.use(keycardMetadataRouter({ issuer: "https://zone.keycard.cloud" }));
40
+ * ```
41
+ *
42
+ * These paths must remain publicly accessible (no bearer auth) per their
43
+ * respective specs. Per the security guidance in
44
+ * `@keycardai/starlette` and the feedback_specific_path_bypass rule:
45
+ * only bypass auth for these exact paths, never a broad `/.well-known/` prefix.
46
+ */
47
+ export declare function keycardMetadataRouter(options: KeycardRouterOptions): Router;
48
+ //# sourceMappingURL=wellKnown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wellKnown.d.ts","sourceRoot":"","sources":["../../src/wellKnown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;OAEG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAc3E"}