@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.
- package/dist/dpop.d.ts +46 -0
- package/dist/dpop.d.ts.map +1 -0
- package/dist/dpop.js +259 -0
- package/dist/dpop.js.map +1 -0
- package/dist/execution.d.ts +10 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +50 -0
- package/dist/execution.js.map +1 -0
- package/dist/harness-authorizer.d.ts +10 -0
- package/dist/harness-authorizer.d.ts.map +1 -0
- package/dist/harness-authorizer.js +90 -0
- package/dist/harness-authorizer.js.map +1 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +6 -3
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +209 -30
- package/dist/middleware.js.map +1 -1
- package/dist/oauth.d.ts +47 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +199 -0
- package/dist/oauth.js.map +1 -0
- package/dist/permissions.d.ts +12 -22
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +91 -33
- package/dist/permissions.js.map +1 -1
- package/dist/runtime-authorizer.d.ts +28 -0
- package/dist/runtime-authorizer.d.ts.map +1 -0
- package/dist/runtime-authorizer.js +136 -0
- package/dist/runtime-authorizer.js.map +1 -0
- package/dist/runtime-grants.d.ts +31 -0
- package/dist/runtime-grants.d.ts.map +1 -0
- package/dist/runtime-grants.js +96 -0
- package/dist/runtime-grants.js.map +1 -0
- package/dist/session.d.ts +3 -3
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +21 -3
- package/dist/session.js.map +1 -1
- package/dist/store.d.ts +27 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +46 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +109 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/workload.d.ts +46 -0
- package/dist/workload.d.ts.map +1 -0
- package/dist/workload.js +227 -0
- package/dist/workload.js.map +1 -0
- package/package.json +3 -2
package/dist/middleware.js
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import { verifySession } from "./session.js";
|
|
2
2
|
import { verifyApiKey, extractApiKeyPrefix } from "./api-key.js";
|
|
3
|
-
|
|
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.
|
|
29
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
178
|
+
if (payload.permissions !== undefined)
|
|
179
|
+
ctx.permissions = [...payload.permissions];
|
|
180
|
+
authCtx = ctx;
|
|
181
|
+
accessToken = sessionToken;
|
|
56
182
|
}
|
|
57
183
|
}
|
|
58
184
|
}
|
|
59
|
-
// ──
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
// ──
|
|
83
|
-
|
|
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
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,sBAAsB,GAAG,SAAS,CAAC;AACzC,MAAM,iBAAiB,GAAgB;IACrC,eAAe,EAAE,KAAK;IACtB,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE;QACL,IAAI,EAAE,WAAW;QACjB,EAAE,EAAE,WAAW;QACf,WAAW,EAAE,WAAW;KACzB;IACD,UAAU,EAAE;QACV,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,WAAW;QACtB,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;KACvC;IACD,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,EAAE;CACX,CAAC;AAEF,sEAAsE;AAEtE,SAAS,YAAY,CAAC,MAAc;IAClC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,OAAO,KAAK,CAAC,CAAC;YAAE,SAAS;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uEAAuE;AAEvE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAkB,EAClB,IAAsB;IAEtB,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,sBAAsB,CAAC;IACtE,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,EAAE,UAAU,IAAI,eAAe,CAAC;IACrE,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;IACnD,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC;IAEzE,SAAS,YAAY,CAAC,OAAoB;QACxC,MAAM,QAAQ,GAAiB;YAC7B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC;QACF,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACpC,QAAQ,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACzC,CAAC;QACD,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC5B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,UAAU,aAAa,CAC1B,OAAoB,EACpB,OAAgB;QAEhB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,uBAAuB,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC3E,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,4BAA4B,CAAC,WAAW,CAAC,CAAC,CAAC;YACnF,OAAO,CAAC,WAAW,GAAG,4BAA4B,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACrE,CAAC;QACD,MAAM,SAAS,GACb,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACjD,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAClC,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;QAC9B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACpE,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxC,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC;QAClC,CAAC;QACD,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,SAAS,gBAAgB,CACvB,IAA6B,EAC7B,SAAiB,EACjB,OAGC;QAED,MAAM,UAAU,GAAoB;YAClC,IAAI;YACJ,SAAS;YACT,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACtC,CAAC;QACF,IAAI,OAAO,EAAE,SAAS,KAAK,SAAS;YAAE,UAAU,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QAC/E,IAAI,OAAO,EAAE,QAAQ,KAAK,SAAS;YAAE,UAAU,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAC5E,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO,KAAK,EAAE,OAAgB,EAAwB,EAAE;QACtD,IAAI,OAAgC,CAAC;QACrC,IAAI,WAA+B,CAAC;QAEpC,gEAAgE;QAChE,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAuC,EAAE,CAAC;YACvD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;gBACrD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACvB,CAAC;YAED,MAAM,QAAQ,GAAG,uBAAuB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;YAClE,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,GAAG;oBACR,eAAe,EAAE,IAAI;oBACrB,IAAI,EAAE,UAAU;oBAChB,KAAK,EAAE;wBACL,IAAI,EAAE,UAAU;wBAChB,EAAE,EAAE,QAAQ,CAAC,QAAQ;wBACrB,WAAW,EAAE,QAAQ,CAAC,YAAY;qBACnC;oBACD,UAAU,EAAE,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,EAAE;wBACtD,QAAQ,EAAE;4BACR,eAAe,EAAE,QAAQ,CAAC,eAAe;4BACzC,WAAW,EAAE,QAAQ,CAAC,WAAW;4BACjC,YAAY,EAAE,QAAQ,CAAC,YAAY;yBACpC;qBACF,CAAC;oBACF,UAAU,EAAE,EAAE;oBACd,MAAM,EAAE,EAAE;oBACV,QAAQ,EAAE,QAAQ,CAAC,QAAQ;oBAC3B,eAAe,EAAE,QAAQ,CAAC,eAAe;iBAC1C,CAAC;YACJ,CAAC;QACH,CAAC;QAED,gEAAgE;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,IAAI,YAAY,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;YAEpD,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,OAAO,GAAG,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE;oBACjE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS;wBACrC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE;wBACnC,CAAC,CAAC,EAAE,CAAC;oBACP,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS;wBACvC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE;wBACvC,CAAC,CAAC,EAAE,CAAC;iBACR,CAAC,CAAC;gBACH,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,MAAM,GAAG,4BAA4B,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;oBACvE,MAAM,GAAG,GAAgB;wBACvB,eAAe,EAAE,IAAI;wBACrB,IAAI,EAAE,OAAO;wBACb,KAAK,EAAE;4BACL,IAAI,EAAE,MAAM;4BACZ,EAAE,EAAE,OAAO,CAAC,MAAM;4BAClB,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS;gCACnC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE;gCACtC,CAAC,CAAC,EAAE,CAAC;4BACP,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;4BAC7D,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;4BAChE,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;yBACpE;wBACD,UAAU,EAAE,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE;4BACtD,SAAS,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;4BACrD,QAAQ,EAAE;gCACR,QAAQ,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;gCACpD,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS;oCACjC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;oCAClC,CAAC,CAAC,EAAE,CAAC;gCACP,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gCAC7D,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;6BAChE;yBACF,CAAC;wBACF,UAAU,EAAE,EAAE;wBACd,MAAM;wBACN,MAAM,EAAE,OAAO,CAAC,MAAM;qBACvB,CAAC;oBACF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;wBAAE,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;oBACxD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;wBAAE,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;oBAC3D,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS;wBAAE,GAAG,CAAC,WAAW,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;oBAClF,OAAO,GAAG,GAAG,CAAC;oBACd,WAAW,GAAG,YAAY,CAAC;gBAC7B,CAAC;YACH,CAAC;QACH,CAAC;QAED,gEAAgE;QAChE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YACvD,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC;oBAC5C,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;oBACrB,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC;wBAC9B,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;wBACrB,CAAC,CAAC,IAAI,CAAC;gBAEX,IAAI,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;oBACzE,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;oBAC1C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;oBAE3D,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;wBACxC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;wBAC/D,IAAI,KAAK,EAAE,CAAC;4BACV,MAAM,MAAM,GAAG,4BAA4B,CAAC;gCAC1C,GAAG,UAAU,CAAC,WAAW;gCACzB,GAAG,CAAC,UAAU,CAAC,MAAM,IAAI,EAAE,CAAC;6BAC7B,CAAC,CAAC;4BACH,OAAO,GAAG;gCACR,eAAe,EAAE,IAAI;gCACrB,IAAI,EAAE,OAAO;gCACb,KAAK,EAAE;oCACL,IAAI,EAAE,OAAO;oCACb,EAAE,EAAE,UAAU,CAAC,EAAE;oCACjB,WAAW,EAAE,UAAU,CAAC,IAAI;oCAC5B,GAAG,CAAC,UAAU,CAAC,MAAM,KAAK,SAAS;wCACjC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE;wCAC/B,CAAC,CAAC,EAAE,CAAC;iCACR;gCACD,UAAU,EAAE,gBAAgB,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,CAAC;gCACtD,UAAU,EAAE,EAAE;gCACd,MAAM;gCACN,OAAO,EAAE,UAAU,CAAC,EAAE;gCACtB,SAAS,EAAE,UAAU,CAAC,IAAI;gCAC1B,WAAW,EAAE,4BAA4B,CAAC,MAAM,CAAC;6BAClD,CAAC;4BACF,WAAW,GAAG,KAAK,CAAC;wBACtB,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,gEAAgE;QAChE,+DAA+D;QAC/D,gEAAgE;QAChE,gEAAgE;QAChE,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,MAAM,iBAAiB,CACpC,UAAU,EACV,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,GAAG,EACX,WAAW,CACZ,CAAC;YAEF,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,2DAA2D;gBAC3D,OAAO,YAAY,CAAC,EAAE,GAAG,iBAAiB,EAAE,CAAC,CAAC;YAChD,CAAC;YAED,gDAAgD;YAChD,OAAO,CAAC,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC;YAC3C,OAAO,CAAC,UAAU,GAAG,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE;gBAC9D,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,KAAK,SAAS;oBAC5C,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,SAAS,EAAE;oBAC7C,CAAC,CAAC,EAAE,CAAC;gBACP,QAAQ,EAAE;oBACR,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;oBACtC,UAAU,EAAE,MAAM,CAAC,UAAU;oBAC7B,mBAAmB,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI;iBAC7C;aACF,CAAC,CAAC;QACL,CAAC;QAED,gEAAgE;QAChE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,aAAa,CAClB;gBACE,GAAG,iBAAiB;gBACpB,UAAU,EAAE;oBACV,GAAG,iBAAiB,CAAC,UAAU;oBAC/B,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACtC;aACF,EACD,OAAO,CACR,CAAC;QACJ,CAAC;QACD,OAAO,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/oauth.d.ts
ADDED
|
@@ -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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"}
|
package/dist/permissions.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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"}
|