@zauso-ai/capstan-auth 0.2.0 → 0.3.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 (51) hide show
  1. package/dist/dpop.d.ts +46 -0
  2. package/dist/dpop.d.ts.map +1 -0
  3. package/dist/dpop.js +259 -0
  4. package/dist/dpop.js.map +1 -0
  5. package/dist/execution.d.ts +10 -0
  6. package/dist/execution.d.ts.map +1 -0
  7. package/dist/execution.js +50 -0
  8. package/dist/execution.js.map +1 -0
  9. package/dist/harness-authorizer.d.ts +10 -0
  10. package/dist/harness-authorizer.d.ts.map +1 -0
  11. package/dist/harness-authorizer.js +90 -0
  12. package/dist/harness-authorizer.js.map +1 -0
  13. package/dist/index.d.ts +14 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +8 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/middleware.d.ts +6 -3
  18. package/dist/middleware.d.ts.map +1 -1
  19. package/dist/middleware.js +209 -30
  20. package/dist/middleware.js.map +1 -1
  21. package/dist/oauth.d.ts +47 -0
  22. package/dist/oauth.d.ts.map +1 -0
  23. package/dist/oauth.js +199 -0
  24. package/dist/oauth.js.map +1 -0
  25. package/dist/permissions.d.ts +12 -22
  26. package/dist/permissions.d.ts.map +1 -1
  27. package/dist/permissions.js +91 -33
  28. package/dist/permissions.js.map +1 -1
  29. package/dist/runtime-authorizer.d.ts +28 -0
  30. package/dist/runtime-authorizer.d.ts.map +1 -0
  31. package/dist/runtime-authorizer.js +136 -0
  32. package/dist/runtime-authorizer.js.map +1 -0
  33. package/dist/runtime-grants.d.ts +31 -0
  34. package/dist/runtime-grants.d.ts.map +1 -0
  35. package/dist/runtime-grants.js +96 -0
  36. package/dist/runtime-grants.js.map +1 -0
  37. package/dist/session.d.ts +3 -3
  38. package/dist/session.d.ts.map +1 -1
  39. package/dist/session.js +21 -3
  40. package/dist/session.js.map +1 -1
  41. package/dist/store.d.ts +27 -0
  42. package/dist/store.d.ts.map +1 -0
  43. package/dist/store.js +46 -0
  44. package/dist/store.js.map +1 -0
  45. package/dist/types.d.ts +109 -1
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/workload.d.ts +46 -0
  48. package/dist/workload.d.ts.map +1 -0
  49. package/dist/workload.js +227 -0
  50. package/dist/workload.js.map +1 -0
  51. package/package.json +3 -2
@@ -1,10 +1,25 @@
1
1
  import { verifySession } from "./session.js";
2
2
  import { verifyApiKey, extractApiKeyPrefix } from "./api-key.js";
3
- const SESSION_COOKIE_NAME = "capstan_session";
3
+ import { validateDpopProof } from "./dpop.js";
4
+ import { extractWorkloadIdentity } from "./workload.js";
5
+ import { normalizePermissionsToGrants, serializeGrantsToPermissions, } from "./permissions.js";
6
+ import { createRequestExecution } from "./execution.js";
4
7
  const DEFAULT_API_KEY_PREFIX = "cap_ak_";
5
8
  const ANONYMOUS_CONTEXT = {
6
9
  isAuthenticated: false,
7
10
  type: "anonymous",
11
+ actor: {
12
+ kind: "anonymous",
13
+ id: "anonymous",
14
+ displayName: "Anonymous",
15
+ },
16
+ credential: {
17
+ kind: "anonymous",
18
+ subjectId: "anonymous",
19
+ presentedAt: new Date(0).toISOString(),
20
+ },
21
+ delegation: [],
22
+ grants: [],
8
23
  };
9
24
  // ── Cookie helpers ─────────────────────────────────────────────────
10
25
  function parseCookies(header) {
@@ -25,62 +40,226 @@ function parseCookies(header) {
25
40
  * incoming `Request`.
26
41
  *
27
42
  * Resolution order:
28
- * 1. Session cookie (`capstan_session`) — verifies JWT, returns human context.
29
- * 2. `Authorization: Bearer <token>` header — if the token matches the
43
+ * 1. Client certificate / workload identity (`X-Client-Cert` or
44
+ * `X-Forwarded-Client-Cert` header)extracts SPIFFE ID, validates
45
+ * trust domain, returns workload context.
46
+ * 2. Session cookie (`capstan_session`) — verifies JWT, returns human context.
47
+ * 3. `Authorization: Bearer <token>` header — if the token matches the
30
48
  * configured API key prefix, looks up the agent credential and verifies
31
49
  * the key hash.
32
- * 3. Falls back to `{ type: "anonymous", isAuthenticated: false }`.
50
+ * 4. Falls back to `{ type: "anonymous", isAuthenticated: false }`.
33
51
  */
34
52
  export function createAuthMiddleware(config, deps) {
35
53
  const apiKeyPrefix = config.apiKeys?.prefix ?? DEFAULT_API_KEY_PREFIX;
36
54
  const authHeaderName = config.apiKeys?.headerName ?? "Authorization";
55
+ const trustedDomains = config.trustedDomains ?? [];
56
+ const sessionCookieName = config.session.cookieName ?? "capstan_session";
57
+ function syncEnvelope(authCtx) {
58
+ const envelope = {
59
+ actor: authCtx.actor,
60
+ credential: authCtx.credential,
61
+ delegation: authCtx.delegation,
62
+ grants: authCtx.grants,
63
+ };
64
+ if (authCtx.execution !== undefined) {
65
+ envelope.execution = authCtx.execution;
66
+ }
67
+ authCtx.envelope = envelope;
68
+ return authCtx;
69
+ }
70
+ async function enrichContext(authCtx, request) {
71
+ const extraGrants = await deps.resolveAdditionalGrants?.(authCtx, request);
72
+ if (extraGrants && extraGrants.length > 0) {
73
+ authCtx.grants = [...authCtx.grants, ...normalizePermissionsToGrants(extraGrants)];
74
+ authCtx.permissions = serializeGrantsToPermissions(authCtx.grants);
75
+ }
76
+ const execution = (await deps.resolveExecution?.(authCtx, request)) ??
77
+ createRequestExecution(request);
78
+ authCtx.execution = execution;
79
+ const delegation = await deps.resolveDelegation?.(authCtx, request);
80
+ if (delegation && delegation.length > 0) {
81
+ authCtx.delegation = delegation;
82
+ }
83
+ return syncEnvelope(authCtx);
84
+ }
85
+ function createCredential(kind, subjectId, options) {
86
+ const credential = {
87
+ kind,
88
+ subjectId,
89
+ presentedAt: new Date().toISOString(),
90
+ };
91
+ if (options?.expiresAt !== undefined)
92
+ credential.expiresAt = options.expiresAt;
93
+ if (options?.metadata !== undefined)
94
+ credential.metadata = options.metadata;
95
+ return credential;
96
+ }
37
97
  return async (request) => {
38
- // ── 1. Session cookie ────────────────────────────────────────
98
+ let authCtx;
99
+ let accessToken;
100
+ // ── 1. Workload identity (mTLS / SPIFFE) ─────────────────────
101
+ if (trustedDomains.length > 0) {
102
+ const headers = {};
103
+ for (const [key, value] of request.headers.entries()) {
104
+ headers[key] = value;
105
+ }
106
+ const identity = extractWorkloadIdentity(headers, trustedDomains);
107
+ if (identity) {
108
+ authCtx = {
109
+ isAuthenticated: true,
110
+ type: "workload",
111
+ actor: {
112
+ kind: "workload",
113
+ id: identity.spiffeId,
114
+ displayName: identity.workloadPath,
115
+ },
116
+ credential: createCredential("mtls", identity.spiffeId, {
117
+ metadata: {
118
+ certFingerprint: identity.certFingerprint,
119
+ trustDomain: identity.trustDomain,
120
+ workloadPath: identity.workloadPath,
121
+ },
122
+ }),
123
+ delegation: [],
124
+ grants: [],
125
+ spiffeId: identity.spiffeId,
126
+ certFingerprint: identity.certFingerprint,
127
+ };
128
+ }
129
+ }
130
+ // ── 2. Session cookie ────────────────────────────────────────
39
131
  const cookieHeader = request.headers.get("cookie");
40
- if (cookieHeader) {
132
+ if (!authCtx && cookieHeader) {
41
133
  const cookies = parseCookies(cookieHeader);
42
- const sessionToken = cookies.get(SESSION_COOKIE_NAME);
134
+ const sessionToken = cookies.get(sessionCookieName);
43
135
  if (sessionToken) {
44
- const payload = verifySession(sessionToken, config.session.secret);
136
+ const payload = verifySession(sessionToken, config.session.secret, {
137
+ ...(config.session.issuer !== undefined
138
+ ? { issuer: config.session.issuer }
139
+ : {}),
140
+ ...(config.session.audience !== undefined
141
+ ? { audience: config.session.audience }
142
+ : {}),
143
+ });
45
144
  if (payload) {
145
+ const grants = normalizePermissionsToGrants(payload.permissions ?? []);
46
146
  const ctx = {
47
147
  isAuthenticated: true,
48
148
  type: "human",
149
+ actor: {
150
+ kind: "user",
151
+ id: payload.userId,
152
+ ...(payload.displayName !== undefined
153
+ ? { displayName: payload.displayName }
154
+ : {}),
155
+ ...(payload.role !== undefined ? { role: payload.role } : {}),
156
+ ...(payload.email !== undefined ? { email: payload.email } : {}),
157
+ ...(payload.claims !== undefined ? { claims: payload.claims } : {}),
158
+ },
159
+ credential: createCredential("session", payload.userId, {
160
+ expiresAt: new Date(payload.exp * 1000).toISOString(),
161
+ metadata: {
162
+ issuedAt: new Date(payload.iat * 1000).toISOString(),
163
+ ...(payload.sessionId !== undefined
164
+ ? { sessionId: payload.sessionId }
165
+ : {}),
166
+ ...(payload.iss !== undefined ? { issuer: payload.iss } : {}),
167
+ ...(payload.aud !== undefined ? { audience: payload.aud } : {}),
168
+ },
169
+ }),
170
+ delegation: [],
171
+ grants,
49
172
  userId: payload.userId,
50
173
  };
51
174
  if (payload.role !== undefined)
52
175
  ctx.role = payload.role;
53
176
  if (payload.email !== undefined)
54
177
  ctx.email = payload.email;
55
- return ctx;
178
+ if (payload.permissions !== undefined)
179
+ ctx.permissions = [...payload.permissions];
180
+ authCtx = ctx;
181
+ accessToken = sessionToken;
56
182
  }
57
183
  }
58
184
  }
59
- // ── 2. API key via Authorization header ──────────────────────
60
- const authHeader = request.headers.get(authHeaderName);
61
- if (authHeader) {
62
- const token = authHeader.startsWith("Bearer ")
63
- ? authHeader.slice(7)
64
- : null;
65
- if (token && token.startsWith(apiKeyPrefix) && deps.findAgentByKeyPrefix) {
66
- const prefix = extractApiKeyPrefix(token);
67
- const credential = await deps.findAgentByKeyPrefix(prefix);
68
- if (credential && !credential.revokedAt) {
69
- const valid = await verifyApiKey(token, credential.apiKeyHash);
70
- if (valid) {
71
- return {
72
- isAuthenticated: true,
73
- type: "agent",
74
- agentId: credential.id,
75
- agentName: credential.name,
76
- permissions: credential.permissions,
77
- };
185
+ // ── 3. API key via Authorization header ──────────────────────
186
+ if (!authCtx) {
187
+ const authHeader = request.headers.get(authHeaderName);
188
+ if (authHeader) {
189
+ const token = authHeader.startsWith("Bearer ")
190
+ ? authHeader.slice(7)
191
+ : authHeader.startsWith("DPoP ")
192
+ ? authHeader.slice(5)
193
+ : null;
194
+ if (token && token.startsWith(apiKeyPrefix) && deps.findAgentByKeyPrefix) {
195
+ const prefix = extractApiKeyPrefix(token);
196
+ const credential = await deps.findAgentByKeyPrefix(prefix);
197
+ if (credential && !credential.revokedAt) {
198
+ const valid = await verifyApiKey(token, credential.apiKeyHash);
199
+ if (valid) {
200
+ const grants = normalizePermissionsToGrants([
201
+ ...credential.permissions,
202
+ ...(credential.grants ?? []),
203
+ ]);
204
+ authCtx = {
205
+ isAuthenticated: true,
206
+ type: "agent",
207
+ actor: {
208
+ kind: "agent",
209
+ id: credential.id,
210
+ displayName: credential.name,
211
+ ...(credential.claims !== undefined
212
+ ? { claims: credential.claims }
213
+ : {}),
214
+ },
215
+ credential: createCredential("api_key", credential.id),
216
+ delegation: [],
217
+ grants,
218
+ agentId: credential.id,
219
+ agentName: credential.name,
220
+ permissions: serializeGrantsToPermissions(grants),
221
+ };
222
+ accessToken = token;
223
+ }
78
224
  }
79
225
  }
80
226
  }
81
227
  }
82
- // ── 3. Anonymous ─────────────────────────────────────────────
83
- return ANONYMOUS_CONTEXT;
228
+ // ── 4. DPoP proof validation ─────────────────────────────────
229
+ // If a DPoP header is present, validate the proof and bind the
230
+ // token to the key thumbprint. A missing or invalid proof when
231
+ // the header is present causes the auth context to be rejected.
232
+ const dpopHeader = request.headers.get("dpop");
233
+ if (dpopHeader && authCtx) {
234
+ const result = await validateDpopProof(dpopHeader, request.method, request.url, accessToken);
235
+ if (!result) {
236
+ // DPoP proof failed validation — treat as unauthenticated.
237
+ return syncEnvelope({ ...ANONYMOUS_CONTEXT });
238
+ }
239
+ // Bind the DPoP thumbprint to the auth context.
240
+ authCtx.dpopThumbprint = result.thumbprint;
241
+ authCtx.credential = createCredential("dpop", authCtx.actor.id, {
242
+ ...(authCtx.credential.expiresAt !== undefined
243
+ ? { expiresAt: authCtx.credential.expiresAt }
244
+ : {}),
245
+ metadata: {
246
+ ...(authCtx.credential.metadata ?? {}),
247
+ thumbprint: result.thumbprint,
248
+ boundCredentialKind: authCtx.credential.kind,
249
+ },
250
+ });
251
+ }
252
+ // ── 5. Anonymous ─────────────────────────────────────────────
253
+ if (!authCtx) {
254
+ return enrichContext({
255
+ ...ANONYMOUS_CONTEXT,
256
+ credential: {
257
+ ...ANONYMOUS_CONTEXT.credential,
258
+ presentedAt: new Date().toISOString(),
259
+ },
260
+ }, request);
261
+ }
262
+ return enrichContext(authCtx, request);
84
263
  };
85
264
  }
86
265
  //# sourceMappingURL=middleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { AuthCookieConfig } from "./types.js";
2
+ export interface OAuthProvider {
3
+ name: string;
4
+ authorizeUrl: string;
5
+ tokenUrl: string;
6
+ userInfoUrl: string;
7
+ clientId: string;
8
+ clientSecret: string;
9
+ scopes: string[];
10
+ }
11
+ export interface OAuthConfig {
12
+ providers: OAuthProvider[];
13
+ callbackPath?: string;
14
+ sessionSecret?: string;
15
+ session?: {
16
+ secret?: string;
17
+ maxAge?: string;
18
+ cookieName?: string;
19
+ cookie?: AuthCookieConfig;
20
+ };
21
+ successRedirectPath?: string;
22
+ stateCookieName?: string;
23
+ }
24
+ /** Pre-built Google OAuth provider */
25
+ export declare function googleProvider(opts: {
26
+ clientId: string;
27
+ clientSecret: string;
28
+ }): OAuthProvider;
29
+ /** Pre-built GitHub OAuth provider */
30
+ export declare function githubProvider(opts: {
31
+ clientId: string;
32
+ clientSecret: string;
33
+ }): OAuthProvider;
34
+ export interface OAuthHandlers {
35
+ /** GET /auth/login/:provider — redirect to OAuth provider */
36
+ login: (request: Request, providerName: string) => Response;
37
+ /** GET /auth/callback — handle OAuth callback, create session */
38
+ callback: (request: Request) => Promise<Response>;
39
+ }
40
+ /**
41
+ * Create OAuth route handlers.
42
+ * Returns handlers for:
43
+ * - GET /auth/login/:provider — redirect to OAuth provider
44
+ * - GET /auth/callback — handle OAuth callback, create session
45
+ */
46
+ export declare function createOAuthHandlers(config: OAuthConfig, fetchFn?: typeof globalThis.fetch): OAuthHandlers;
47
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;KAC3B,CAAC;IACF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAkBD,sCAAsC;AACtC,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,aAAa,CAUhB;AAED,sCAAsC;AACtC,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,aAAa,CAUhB;AAwCD,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,QAAQ,CAAC;IAC5D,iEAAiE;IACjE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACnD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,OAAO,UAAU,CAAC,KAAwB,GAClD,aAAa,CA4Lf"}
package/dist/oauth.js ADDED
@@ -0,0 +1,199 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { signSession } from "./session.js";
3
+ // ── Pre-built providers ───────────────────────────────────────────────
4
+ /** Pre-built Google OAuth provider */
5
+ export function googleProvider(opts) {
6
+ return {
7
+ name: "google",
8
+ authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
9
+ tokenUrl: "https://oauth2.googleapis.com/token",
10
+ userInfoUrl: "https://www.googleapis.com/oauth2/v3/userinfo",
11
+ clientId: opts.clientId,
12
+ clientSecret: opts.clientSecret,
13
+ scopes: ["openid", "email", "profile"],
14
+ };
15
+ }
16
+ /** Pre-built GitHub OAuth provider */
17
+ export function githubProvider(opts) {
18
+ return {
19
+ name: "github",
20
+ authorizeUrl: "https://github.com/login/oauth/authorize",
21
+ tokenUrl: "https://github.com/login/oauth/access_token",
22
+ userInfoUrl: "https://api.github.com/user",
23
+ clientId: opts.clientId,
24
+ clientSecret: opts.clientSecret,
25
+ scopes: ["user:email"],
26
+ };
27
+ }
28
+ // ── State management ──────────────────────────────────────────────────
29
+ /** Generate a cryptographically random state parameter. */
30
+ function generateState() {
31
+ return randomBytes(32).toString("hex");
32
+ }
33
+ // ── Cookie helpers ────────────────────────────────────────────────────
34
+ function parseCookies(header) {
35
+ const cookies = new Map();
36
+ for (const pair of header.split(";")) {
37
+ const eqIndex = pair.indexOf("=");
38
+ if (eqIndex === -1)
39
+ continue;
40
+ const name = pair.slice(0, eqIndex).trim();
41
+ const value = pair.slice(eqIndex + 1).trim();
42
+ cookies.set(name, value);
43
+ }
44
+ return cookies;
45
+ }
46
+ function buildCookie(name, value, options) {
47
+ const parts = [`${name}=${value}`];
48
+ parts.push(`Path=${options?.path ?? "/"}`);
49
+ if (options?.domain)
50
+ parts.push(`Domain=${options.domain}`);
51
+ if (options?.httpOnly !== false)
52
+ parts.push("HttpOnly");
53
+ parts.push(`SameSite=${options?.sameSite ?? "Lax"}`);
54
+ if (options?.secure)
55
+ parts.push("Secure");
56
+ if (options?.maxAge !== undefined)
57
+ parts.push(`Max-Age=${options.maxAge}`);
58
+ return parts.join("; ");
59
+ }
60
+ /**
61
+ * Create OAuth route handlers.
62
+ * Returns handlers for:
63
+ * - GET /auth/login/:provider — redirect to OAuth provider
64
+ * - GET /auth/callback — handle OAuth callback, create session
65
+ */
66
+ export function createOAuthHandlers(config, fetchFn = globalThis.fetch) {
67
+ const callbackPath = config.callbackPath ?? "/auth/callback";
68
+ const stateCookieName = config.stateCookieName ?? "capstan_oauth_state";
69
+ const sessionCookieName = config.session?.cookieName ?? "capstan_session";
70
+ const resolvedSessionSecret = config.session?.secret ?? config.sessionSecret;
71
+ if (!resolvedSessionSecret) {
72
+ throw new Error("OAuthConfig requires session.secret or sessionSecret");
73
+ }
74
+ const sessionSecret = resolvedSessionSecret;
75
+ const providerMap = new Map();
76
+ for (const p of config.providers) {
77
+ providerMap.set(p.name, p);
78
+ }
79
+ function login(request, providerName) {
80
+ const provider = providerMap.get(providerName);
81
+ if (!provider) {
82
+ return new Response(JSON.stringify({ error: `Unknown provider: ${providerName}` }), { status: 400, headers: { "content-type": "application/json" } });
83
+ }
84
+ const state = generateState();
85
+ const url = new URL(request.url);
86
+ const redirectUri = `${url.origin}${callbackPath}`;
87
+ const authorizeUrl = new URL(provider.authorizeUrl);
88
+ authorizeUrl.searchParams.set("client_id", provider.clientId);
89
+ authorizeUrl.searchParams.set("redirect_uri", redirectUri);
90
+ authorizeUrl.searchParams.set("response_type", "code");
91
+ authorizeUrl.searchParams.set("scope", provider.scopes.join(" "));
92
+ authorizeUrl.searchParams.set("state", `${providerName}:${state}`);
93
+ return new Response(null, {
94
+ status: 302,
95
+ headers: {
96
+ location: authorizeUrl.toString(),
97
+ "set-cookie": buildCookie(stateCookieName, `${providerName}:${state}`, {
98
+ maxAge: 600,
99
+ }),
100
+ },
101
+ });
102
+ }
103
+ async function callback(request) {
104
+ const url = new URL(request.url);
105
+ const code = url.searchParams.get("code");
106
+ const stateParam = url.searchParams.get("state");
107
+ if (!code || !stateParam) {
108
+ return new Response(JSON.stringify({ error: "Missing code or state parameter" }), { status: 400, headers: { "content-type": "application/json" } });
109
+ }
110
+ // Validate state against cookie
111
+ const cookieHeader = request.headers.get("cookie") ?? "";
112
+ const cookies = parseCookies(cookieHeader);
113
+ const storedState = cookies.get(stateCookieName);
114
+ if (!storedState || storedState !== stateParam) {
115
+ return new Response(JSON.stringify({ error: "Invalid state parameter" }), { status: 403, headers: { "content-type": "application/json" } });
116
+ }
117
+ // Extract provider name from state
118
+ const colonIndex = stateParam.indexOf(":");
119
+ if (colonIndex === -1) {
120
+ return new Response(JSON.stringify({ error: "Malformed state parameter" }), { status: 400, headers: { "content-type": "application/json" } });
121
+ }
122
+ const providerName = stateParam.slice(0, colonIndex);
123
+ const provider = providerMap.get(providerName);
124
+ if (!provider) {
125
+ return new Response(JSON.stringify({ error: `Unknown provider: ${providerName}` }), { status: 400, headers: { "content-type": "application/json" } });
126
+ }
127
+ // Exchange code for access token
128
+ const redirectUri = `${url.origin}${callbackPath}`;
129
+ let tokenData;
130
+ try {
131
+ const tokenResponse = await fetchFn(provider.tokenUrl, {
132
+ method: "POST",
133
+ headers: {
134
+ "content-type": "application/x-www-form-urlencoded",
135
+ accept: "application/json",
136
+ },
137
+ body: new URLSearchParams({
138
+ grant_type: "authorization_code",
139
+ code,
140
+ redirect_uri: redirectUri,
141
+ client_id: provider.clientId,
142
+ client_secret: provider.clientSecret,
143
+ }).toString(),
144
+ });
145
+ if (!tokenResponse.ok) {
146
+ return new Response(JSON.stringify({ error: "Token exchange failed" }), { status: 502, headers: { "content-type": "application/json" } });
147
+ }
148
+ tokenData = (await tokenResponse.json());
149
+ }
150
+ catch {
151
+ return new Response(JSON.stringify({ error: "Token exchange failed" }), { status: 502, headers: { "content-type": "application/json" } });
152
+ }
153
+ if (!tokenData.access_token) {
154
+ return new Response(JSON.stringify({ error: "Token exchange failed" }), { status: 502, headers: { "content-type": "application/json" } });
155
+ }
156
+ // Fetch user info
157
+ let userInfo;
158
+ try {
159
+ const userResponse = await fetchFn(provider.userInfoUrl, {
160
+ headers: {
161
+ authorization: `Bearer ${tokenData.access_token}`,
162
+ accept: "application/json",
163
+ },
164
+ });
165
+ if (!userResponse.ok) {
166
+ return new Response(JSON.stringify({ error: "Failed to fetch user info" }), { status: 502, headers: { "content-type": "application/json" } });
167
+ }
168
+ userInfo = (await userResponse.json());
169
+ }
170
+ catch {
171
+ return new Response(JSON.stringify({ error: "Failed to fetch user info" }), { status: 502, headers: { "content-type": "application/json" } });
172
+ }
173
+ // Build session from user info
174
+ const userId = userInfo.sub ?? userInfo.id?.toString() ?? userInfo.login ?? "unknown";
175
+ const sessionData = {
176
+ userId: `${providerName}:${userId}`,
177
+ ...(userInfo.name !== undefined ? { displayName: userInfo.name } : {}),
178
+ };
179
+ if (userInfo.email !== undefined) {
180
+ sessionData.email = userInfo.email;
181
+ }
182
+ const sessionToken = signSession(sessionData, sessionSecret, config.session?.maxAge !== undefined
183
+ ? { maxAge: config.session.maxAge }
184
+ : undefined);
185
+ // Set session cookie and redirect to /
186
+ return new Response(null, {
187
+ status: 302,
188
+ headers: {
189
+ location: config.successRedirectPath ?? "/",
190
+ "set-cookie": buildCookie(sessionCookieName, sessionToken, {
191
+ ...(config.session?.cookie ?? {}),
192
+ maxAge: 604800,
193
+ }),
194
+ },
195
+ });
196
+ }
197
+ return { login, callback };
198
+ }
199
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
@@ -1,34 +1,24 @@
1
+ import type { AuthGrant, AuthGrantRequirement } from "./types.js";
2
+ export interface AuthorizationDecision {
3
+ allowed: boolean;
4
+ matchedGrant?: AuthGrant;
5
+ reason?: string;
6
+ }
7
+ export declare function normalizePermissionsToGrants(granted: readonly (string | AuthGrant)[]): AuthGrant[];
8
+ export declare function serializeGrantsToPermissions(grants: readonly AuthGrant[]): string[];
9
+ export declare function authorizeGrant(required: AuthGrantRequirement, granted: readonly (string | AuthGrant)[]): AuthorizationDecision;
10
+ export declare function checkGrant(required: AuthGrantRequirement, granted: readonly (string | AuthGrant)[]): boolean;
1
11
  /**
2
12
  * Check whether a required permission is satisfied by at least one entry in
3
- * the `granted` permission set.
4
- *
5
- * Permission strings follow the `resource:action` pattern.
6
- *
7
- * Wildcards:
8
- * - `*:read` — allows `read` on any resource
9
- * - `ticket:*` — allows any action on `ticket`
10
- * - `*:*` — full access (superuser)
11
- *
12
- * Examples:
13
- * checkPermission({ resource: "ticket", action: "read" }, ["ticket:read"]) // true
14
- * checkPermission({ resource: "ticket", action: "write" }, ["*:write"]) // true
15
- * checkPermission({ resource: "ticket", action: "delete" }, ["ticket:*"]) // true
16
- * checkPermission({ resource: "ticket", action: "delete" }, ["*:*"]) // true
13
+ * the granted permission / grant set.
17
14
  */
18
15
  export declare function checkPermission(required: {
19
16
  resource: string;
20
17
  action: "read" | "write" | "delete";
21
- }, granted: string[]): boolean;
18
+ }, granted: readonly (string | AuthGrant)[]): boolean;
22
19
  /**
23
20
  * Derive a `{ resource, action }` pair from an agent capability mode and
24
21
  * an optional resource name.
25
- *
26
- * Mapping:
27
- * - `"read"` → `{ resource, action: "read" }`
28
- * - `"write"` → `{ resource, action: "write" }`
29
- * - `"external"` → `{ resource: "external", action: "write" }`
30
- *
31
- * When `resource` is omitted the wildcard `"*"` is used.
32
22
  */
33
23
  export declare function derivePermission(capability: "read" | "write" | "external", resource?: string): {
34
24
  resource: string;
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;CAAE,EACnE,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAiBT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,EACzC,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAStC"}
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgDD,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GACvC,SAAS,EAAE,CAab;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,EAAE,CAEnF;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GACvC,qBAAqB,CA0BvB;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GACvC,OAAO,CAET;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;CAAE,EACnE,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GACvC,OAAO,CAET;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,EACzC,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAStC"}