prism-authts 0.0.0-stage → 1.0.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/README.md CHANGED
@@ -1,3 +1,133 @@
1
- # Temporary Holding Version
1
+ # prism-authts
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Cloudflare Workers 与 Edge Runtime 的 Prism OAuth2 / OIDC 单点登录 (SSO) 鉴权中间件。
4
+
5
+ 提供防时序侧信道攻击 (Timing-safe) HMAC-SHA256 签名校验、CSRF 状态校验、泛域名 Cookie 共享支持,并自动处理登录拦截、回调换取 Token 及登出逻辑。
6
+
7
+ ---
8
+
9
+ ## 特性
10
+
11
+ - ⚡️ **边缘计算原生**:基于标准 Web Crypto API,完美适配 Cloudflare Workers、Vercel Edge 等边缘环境。
12
+ - 🔒 **安全可靠**:内置 HMAC-SHA256 防篡改签名、时序安全比对(防御 Timing Attack)与 Open Redirect 防御。
13
+ - 📦 **双格式构建**:支持 ESM 与 CommonJS,开箱即用完整的 TypeScript 类型定义 (`.d.ts`)。
14
+ - 🍪 **单点登录与子域名共享**:支持设置 `COOKIE_DOMAIN`,实现多级子域名共享登录态。
15
+ - 🛠 **灵活接入**:支持类继承 (`PrismWorker`)、函数包装器 (`withPrismAuth`) 与旧版 Service Worker 三种接入方式。
16
+
17
+ ---
18
+
19
+ ## 安装
20
+
21
+ ```bash
22
+ npm install prism-authts
23
+ # 或
24
+ pnpm add prism-authts
25
+ # 或
26
+ yarn add prism-authts
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 环境变量配置
32
+
33
+ 在 Cloudflare Worker 的 `wrangler.toml` 或环境变量中配置以下变量:
34
+
35
+ | 变量名 | 必填 | 说明 |
36
+ | :--- | :---: | :--- |
37
+ | `PRISM_URL` | 是 | Prism 服务端的基础 URL(例如:`https://auth.example.com`) |
38
+ | `PRISM_CLIENT_ID` | 是 | OAuth2 Client ID |
39
+ | `PRISM_CLIENT_SECRET` | 是 | OAuth2 Client Secret |
40
+ | `COOKIE_DOMAIN` | 否 | 共享 Cookie 的域名(例如:`.example.com`,留空则默认当前站点) |
41
+ | `PRISM_SCOPE` | 否 | OAuth 请求的 Scope(默认:`openid profile email`) |
42
+
43
+ ---
44
+
45
+ ## 使用方式
46
+
47
+ ### 方式 1:类继承模式(推荐)
48
+
49
+ 通过继承 `PrismWorker` 类并实现 `handleRequest`:
50
+
51
+ ```typescript
52
+ import { PrismWorker, type AuthenticatedRequest, type PrismEnv, type PrismUser } from 'prism-authts';
53
+
54
+ export default class MyWorker extends PrismWorker {
55
+ // 可选:重写公开白名单路由(无需登录即可访问)
56
+ override isPublic(url: URL, request: Request): boolean {
57
+ if (url.pathname === '/favicon.ico' || url.pathname.startsWith('/public/')) {
58
+ return true;
59
+ }
60
+ return super.isPublic(url, request);
61
+ }
62
+
63
+ // 鉴权通过后的业务逻辑
64
+ override async handleRequest(
65
+ request: AuthenticatedRequest,
66
+ env: PrismEnv,
67
+ ctx: any,
68
+ user: PrismUser | null
69
+ ): Promise<Response> {
70
+ return new Response(JSON.stringify({
71
+ message: "认证成功!",
72
+ user: user, // 当前登录用户信息
73
+ }), {
74
+ headers: { "Content-Type": "application/json; charset=utf-8" },
75
+ });
76
+ }
77
+ }
78
+ ```
79
+
80
+ ### 方式 2:函数式包装器 (`withPrismAuth`)
81
+
82
+ 如果不习惯类继承,可以直接使用函数包装器:
83
+
84
+ ```typescript
85
+ import { withPrismAuth } from 'prism-authts';
86
+
87
+ export default {
88
+ fetch: withPrismAuth(
89
+ async (request, env, ctx, user) => {
90
+ return new Response(`欢迎登录, ${user?.name || user?.email || '用户'}!`);
91
+ },
92
+ {
93
+ // 可选:自定义放行规则
94
+ isPublic: (url) => url.pathname.startsWith('/api/health'),
95
+ }
96
+ ),
97
+ };
98
+ ```
99
+
100
+ ### 方式 3:兼容旧版 Service Worker 模式
101
+
102
+ ```typescript
103
+ import { registerServiceWorker } from 'prism-authts';
104
+
105
+ registerServiceWorker(async (event, user) => {
106
+ return new Response(`Hello, ${user?.sub}`);
107
+ });
108
+ ```
109
+
110
+ ---
111
+
112
+ ## 内置路由端点
113
+
114
+ - **`GET /auth/callback`**:OAuth2 授权码重定向回调端点(自动拦截并换取 Token、写入 Cookie)。
115
+ - **`GET /__logout`**:注销登录端点,访问后自动清空当前站点的登录 Cookie 并重定向至 `/`。
116
+
117
+ ---
118
+
119
+ ## 构建与发布
120
+
121
+ ```bash
122
+ # 本地构建
123
+ npm run build
124
+
125
+ # 发布至 npm(会自动执行 npm run build)
126
+ npm publish
127
+ ```
128
+
129
+ ---
130
+
131
+ ## License
132
+
133
+ MIT
@@ -0,0 +1,308 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/prism-auth.ts
20
+ var prism_auth_exports = {};
21
+ __export(prism_auth_exports, {
22
+ PrismWorker: () => PrismWorker,
23
+ default: () => prism_auth_default,
24
+ registerServiceWorker: () => registerServiceWorker,
25
+ withPrismAuth: () => withPrismAuth
26
+ });
27
+ module.exports = __toCommonJS(prism_auth_exports);
28
+ function timingSafeEqual(a, b) {
29
+ if (typeof a !== "string" || typeof b !== "string" || a.length !== b.length) {
30
+ return false;
31
+ }
32
+ let diff = 0;
33
+ for (let i = 0; i < a.length; i++) {
34
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
35
+ }
36
+ return diff === 0;
37
+ }
38
+ var _cachedSecret = null;
39
+ var _cachedKey = null;
40
+ async function getHmacKey(secret) {
41
+ if (_cachedKey && _cachedSecret === secret) return _cachedKey;
42
+ const enc = new TextEncoder();
43
+ _cachedKey = await crypto.subtle.importKey(
44
+ "raw",
45
+ enc.encode(secret),
46
+ { name: "HMAC", hash: "SHA-256" },
47
+ false,
48
+ ["sign", "verify"]
49
+ );
50
+ _cachedSecret = secret;
51
+ return _cachedKey;
52
+ }
53
+ async function hmacSign(data, secret) {
54
+ const key = await getHmacKey(secret);
55
+ const enc = new TextEncoder();
56
+ const sig = await crypto.subtle.sign("HMAC", key, enc.encode(data));
57
+ return btoa(String.fromCharCode(...new Uint8Array(sig))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
58
+ }
59
+ async function hmacVerify(data, signature, secret) {
60
+ const expected = await hmacSign(data, secret);
61
+ return timingSafeEqual(expected, signature);
62
+ }
63
+ function base64UrlEncode(str) {
64
+ const bytes = new TextEncoder().encode(str);
65
+ let binary = "";
66
+ for (let i = 0; i < bytes.byteLength; i++) {
67
+ binary += String.fromCharCode(bytes[i]);
68
+ }
69
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
70
+ }
71
+ function base64UrlDecode(str) {
72
+ let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
73
+ while (base64.length % 4) {
74
+ base64 += "=";
75
+ }
76
+ const binary = atob(base64);
77
+ const bytes = new Uint8Array(binary.length);
78
+ for (let i = 0; i < binary.length; i++) {
79
+ bytes[i] = binary.charCodeAt(i);
80
+ }
81
+ return new TextDecoder().decode(bytes);
82
+ }
83
+ function parseCookies(cookieHeader) {
84
+ const cookies = {};
85
+ if (!cookieHeader) return cookies;
86
+ for (const item of cookieHeader.split(";")) {
87
+ const idx = item.indexOf("=");
88
+ if (idx !== -1) {
89
+ const key = item.slice(0, idx).trim();
90
+ const val = item.slice(idx + 1).trim();
91
+ cookies[key] = val;
92
+ }
93
+ }
94
+ return cookies;
95
+ }
96
+ function sanitizeRedirectPath(path) {
97
+ if (!path || typeof path !== "string") return "/";
98
+ if (path.startsWith("/") && !path.startsWith("//") && !path.startsWith("/\\")) {
99
+ return path;
100
+ }
101
+ return "/";
102
+ }
103
+ var PrismWorker = class {
104
+ /**
105
+ * 可选扩展:指定公开免登路径(如 /favicon.ico 或静态公共 API)
106
+ * 子类可以重写此方法以自定义放行规则
107
+ */
108
+ isPublic(url, request) {
109
+ return url.pathname === "/favicon.ico";
110
+ }
111
+ /**
112
+ * Worker 入口:拦截未登录、OAuth 回调、登出并透传业务请求
113
+ */
114
+ async fetch(request, env, ctx) {
115
+ const currentEnv = env || (typeof globalThis !== "undefined" ? globalThis : {});
116
+ const url = new URL(request.url);
117
+ if (!currentEnv.PRISM_URL || !currentEnv.PRISM_CLIENT_ID || !currentEnv.PRISM_CLIENT_SECRET) {
118
+ return new Response("\u914D\u7F6E\u9519\u8BEF\uFF1A\u7F3A\u5C11\u5FC5\u8981\u7684\u73AF\u5883\u53D8\u91CF (PRISM_URL, PRISM_CLIENT_ID, PRISM_CLIENT_SECRET)", {
119
+ status: 500,
120
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
121
+ });
122
+ }
123
+ if (this.isPublic(url, request)) {
124
+ return this.handleRequest(request, currentEnv, ctx, null);
125
+ }
126
+ const cookies = parseCookies(request.headers.get("Cookie"));
127
+ const prismBase = currentEnv.PRISM_URL.replace(/\/+$/, "");
128
+ let cookieDomain = (currentEnv.COOKIE_DOMAIN || "").trim();
129
+ if (cookieDomain && !cookieDomain.startsWith(".")) {
130
+ cookieDomain = "." + cookieDomain;
131
+ }
132
+ const domainAttr = cookieDomain ? `; Domain=${cookieDomain}` : "";
133
+ if (url.pathname === "/__logout") {
134
+ return new Response("\u5DF2\u9000\u51FA\u5F53\u524D\u7AD9\u70B9\u767B\u5F55", {
135
+ status: 302,
136
+ headers: {
137
+ "Location": "/",
138
+ "Set-Cookie": `prism_session=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0${domainAttr}`
139
+ }
140
+ });
141
+ }
142
+ if (url.pathname === "/auth/callback") {
143
+ const error = url.searchParams.get("error");
144
+ if (error) {
145
+ const desc = url.searchParams.get("error_description") || error;
146
+ return new Response(`Prism \u6388\u6743\u5931\u8D25: ${desc}`, { status: 403 });
147
+ }
148
+ const code = url.searchParams.get("code");
149
+ const rawState = url.searchParams.get("state");
150
+ if (!code) return new Response("\u7F3A\u5C11\u6388\u6743\u7801 (Code)", { status: 400 });
151
+ let targetPath = "/";
152
+ if (rawState && rawState.includes(".")) {
153
+ const [statePayloadB64, stateSig] = rawState.split(".");
154
+ if (await hmacVerify(statePayloadB64, stateSig, currentEnv.PRISM_CLIENT_SECRET)) {
155
+ try {
156
+ const parsed = JSON.parse(base64UrlDecode(statePayloadB64));
157
+ if (parsed.exp && parsed.exp > Date.now()) {
158
+ targetPath = sanitizeRedirectPath(parsed.path);
159
+ }
160
+ } catch (e) {
161
+ }
162
+ }
163
+ }
164
+ const tokenRes = await fetch(`${prismBase}/api/oauth/token`, {
165
+ method: "POST",
166
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
167
+ body: new URLSearchParams({
168
+ grant_type: "authorization_code",
169
+ client_id: currentEnv.PRISM_CLIENT_ID,
170
+ client_secret: currentEnv.PRISM_CLIENT_SECRET,
171
+ code,
172
+ redirect_uri: `${url.origin}/auth/callback`
173
+ })
174
+ });
175
+ if (!tokenRes.ok) {
176
+ const errText = await tokenRes.text();
177
+ return new Response(`Prism \u6362\u53D6 Token \u5931\u8D25: ${errText}`, { status: 401 });
178
+ }
179
+ const tokenData = await tokenRes.json();
180
+ let user2 = {};
181
+ if (tokenData.id_token) {
182
+ try {
183
+ const idPayload = tokenData.id_token.split(".")[1];
184
+ user2 = JSON.parse(base64UrlDecode(idPayload));
185
+ } catch (e) {
186
+ }
187
+ } else if (tokenData.access_token) {
188
+ try {
189
+ const uRes = await fetch(`${prismBase}/api/oauth/userinfo`, {
190
+ headers: { Authorization: `Bearer ${tokenData.access_token}` }
191
+ });
192
+ if (uRes.ok) user2 = await uRes.json();
193
+ } catch (e) {
194
+ }
195
+ }
196
+ const expireAt = Date.now() + 7 * 86400 * 1e3;
197
+ const sessionPayload = {
198
+ aud: currentEnv.PRISM_CLIENT_ID,
199
+ exp: expireAt,
200
+ user: user2
201
+ };
202
+ const payloadStr = JSON.stringify(sessionPayload);
203
+ const encodedPayload = base64UrlEncode(payloadStr);
204
+ const signature = await hmacSign(encodedPayload, currentEnv.PRISM_CLIENT_SECRET);
205
+ const sessionValue = `${encodedPayload}.${signature}`;
206
+ return new Response(null, {
207
+ status: 302,
208
+ headers: {
209
+ "Location": targetPath,
210
+ "Set-Cookie": `prism_session=${sessionValue}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=604800${domainAttr}`
211
+ }
212
+ });
213
+ }
214
+ const sessionCookie = cookies["prism_session"];
215
+ let user = null;
216
+ if (sessionCookie && sessionCookie.includes(".")) {
217
+ const [encodedPayload, signature] = sessionCookie.split(".");
218
+ if (encodedPayload && signature) {
219
+ const isValid = await hmacVerify(encodedPayload, signature, currentEnv.PRISM_CLIENT_SECRET);
220
+ if (isValid) {
221
+ try {
222
+ const sessionData = JSON.parse(base64UrlDecode(encodedPayload));
223
+ if (Number(sessionData.exp) > Date.now() && sessionData.aud === currentEnv.PRISM_CLIENT_ID) {
224
+ user = sessionData.user || { authenticated: true };
225
+ }
226
+ } catch (e) {
227
+ }
228
+ }
229
+ }
230
+ }
231
+ if (!user) {
232
+ const isApi = url.pathname.startsWith("/api/") || request.headers.get("Accept")?.includes("application/json") || request.headers.get("X-Requested-With") === "XMLHttpRequest";
233
+ if (isApi) {
234
+ return new Response(
235
+ JSON.stringify({ error: "Unauthorized", message: "\u672A\u767B\u5F55\u6216\u767B\u5F55\u5DF2\u8FC7\u671F" }),
236
+ {
237
+ status: 401,
238
+ headers: { "Content-Type": "application/json; charset=utf-8" }
239
+ }
240
+ );
241
+ }
242
+ const stateData = JSON.stringify({
243
+ path: url.pathname + url.search,
244
+ exp: Date.now() + 600 * 1e3
245
+ });
246
+ const stateB64 = base64UrlEncode(stateData);
247
+ const stateSig = await hmacSign(stateB64, currentEnv.PRISM_CLIENT_SECRET);
248
+ const stateToken = `${stateB64}.${stateSig}`;
249
+ const authUrl = new URL(`${prismBase}/api/oauth/authorize`);
250
+ authUrl.searchParams.set("client_id", currentEnv.PRISM_CLIENT_ID);
251
+ authUrl.searchParams.set("redirect_uri", `${url.origin}/auth/callback`);
252
+ authUrl.searchParams.set("response_type", "code");
253
+ authUrl.searchParams.set("scope", currentEnv.PRISM_SCOPE || "openid profile email");
254
+ authUrl.searchParams.set("state", stateToken);
255
+ return Response.redirect(authUrl.toString(), 302);
256
+ }
257
+ const authReq = request;
258
+ authReq.user = user;
259
+ return this.handleRequest(authReq, currentEnv, ctx, user);
260
+ }
261
+ /**
262
+ * 子类重写的业务方法(增加第 4 个参数 user,获取当前登录用户信息)
263
+ */
264
+ async handleRequest(request, env, ctx, user) {
265
+ return new Response("\u8BF7\u5728\u5B50\u7C7B\u4E2D\u91CD\u5199 handleRequest \u65B9\u6CD5", { status: 404 });
266
+ }
267
+ };
268
+ function withPrismAuth(handler, options = {}) {
269
+ const instance = new class extends PrismWorker {
270
+ isPublic(url, req) {
271
+ return options.isPublic ? options.isPublic(url, req) : super.isPublic(url, req);
272
+ }
273
+ async handleRequest(req, env, ctx, user) {
274
+ return handler(req, env, ctx, user);
275
+ }
276
+ }();
277
+ return (req, env, ctx) => instance.fetch(req, env, ctx);
278
+ }
279
+ function registerServiceWorker(handleRequest, options = {}) {
280
+ const worker = new class extends PrismWorker {
281
+ isPublic(url, req) {
282
+ return options.isPublic ? options.isPublic(url, req) : super.isPublic(url, req);
283
+ }
284
+ async handleRequest(request, env, ctx, user) {
285
+ const fakeEvent = {
286
+ request,
287
+ respondWith: () => {
288
+ },
289
+ waitUntil: (p) => ctx && ctx.waitUntil ? ctx.waitUntil(p) : p,
290
+ user
291
+ };
292
+ return handleRequest(fakeEvent, user);
293
+ }
294
+ }();
295
+ if (typeof addEventListener !== "undefined") {
296
+ addEventListener("fetch", (event) => {
297
+ event.respondWith(worker.fetch(event.request, globalThis, event));
298
+ });
299
+ }
300
+ }
301
+ var prism_auth_default = PrismWorker;
302
+ // Annotate the CommonJS export names for ESM import in node:
303
+ 0 && (module.exports = {
304
+ PrismWorker,
305
+ registerServiceWorker,
306
+ withPrismAuth
307
+ });
308
+ //# sourceMappingURL=prism-auth.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/prism-auth.ts"],"sourcesContent":["// prism-auth.ts\n\n// =========================================================================\n// 类型定义 (Types)\n// =========================================================================\n\nexport interface PrismEnv {\n PRISM_URL: string;\n PRISM_CLIENT_ID: string;\n PRISM_CLIENT_SECRET: string;\n COOKIE_DOMAIN?: string;\n PRISM_SCOPE?: string;\n [key: string]: any;\n}\n\nexport interface PrismUser {\n sub?: string;\n name?: string;\n email?: string;\n preferred_username?: string;\n authenticated?: boolean;\n [key: string]: any;\n}\n\nexport interface AuthenticatedRequest extends Request {\n user?: PrismUser;\n}\n\nexport interface PrismWorkerOptions {\n isPublic?: (url: URL, req: Request) => boolean;\n}\n\nexport type PrismHandler = (\n req: AuthenticatedRequest,\n env: PrismEnv,\n ctx: any,\n user: PrismUser | null\n) => Promise<Response> | Response;\n\n// =========================================================================\n// 1. 加密与安全工具函数 (Web Crypto & Timing-Safe)\n// =========================================================================\n\n// 恒定时间字符串比对(防御时序侧信道攻击)\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (typeof a !== \"string\" || typeof b !== \"string\" || a.length !== b.length) {\n return false;\n }\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return diff === 0;\n}\n\n// 缓存 CryptoKey,避免高并发下每个请求重复 importKey 消耗 CPU\nlet _cachedSecret: string | null = null;\nlet _cachedKey: CryptoKey | null = null;\n\nasync function getHmacKey(secret: string): Promise<CryptoKey> {\n if (_cachedKey && _cachedSecret === secret) return _cachedKey;\n const enc = new TextEncoder();\n _cachedKey = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\", \"verify\"]\n );\n _cachedSecret = secret;\n return _cachedKey;\n}\n\n// 基于 Web Crypto 的 HMAC-SHA256 签名工具\nasync function hmacSign(data: string, secret: string): Promise<string> {\n const key = await getHmacKey(secret);\n const enc = new TextEncoder();\n const sig = await crypto.subtle.sign(\"HMAC\", key, enc.encode(data));\n return btoa(String.fromCharCode(...new Uint8Array(sig)))\n .replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nasync function hmacVerify(data: string, signature: string, secret: string): Promise<boolean> {\n const expected = await hmacSign(data, secret);\n return timingSafeEqual(expected, signature);\n}\n\n// UTF-8 安全的 Base64URL 编解码工具\nfunction base64UrlEncode(str: string): string {\n const bytes = new TextEncoder().encode(str);\n let binary = \"\";\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction base64UrlDecode(str: string): string {\n let base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4) {\n base64 += \"=\";\n }\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n// 健壮的 Cookie 解析方法(避免 split(\"=\") 误切 base64 中的等于号)\nfunction parseCookies(cookieHeader: string | null): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (!cookieHeader) return cookies;\n for (const item of cookieHeader.split(\";\")) {\n const idx = item.indexOf(\"=\");\n if (idx !== -1) {\n const key = item.slice(0, idx).trim();\n const val = item.slice(idx + 1).trim();\n cookies[key] = val;\n }\n }\n return cookies;\n}\n\n// 防御开放重定向(Open Redirect):强制要求单斜杠内部相对路径,禁止 // 或 /\\ 外部协议伪造\nfunction sanitizeRedirectPath(path: string | null | undefined): string {\n if (!path || typeof path !== \"string\") return \"/\";\n if (path.startsWith(\"/\") && !path.startsWith(\"//\") && !path.startsWith(\"/\\\\\")) {\n return path;\n }\n return \"/\";\n}\n\n// =========================================================================\n// 2. 鉴权基类 (PrismWorker)\n// =========================================================================\n\nexport class PrismWorker {\n /**\n * 可选扩展:指定公开免登路径(如 /favicon.ico 或静态公共 API)\n * 子类可以重写此方法以自定义放行规则\n */\n isPublic(url: URL, request: Request): boolean {\n return url.pathname === \"/favicon.ico\";\n }\n\n /**\n * Worker 入口:拦截未登录、OAuth 回调、登出并透传业务请求\n */\n async fetch(request: Request, env?: PrismEnv, ctx?: any): Promise<Response> {\n const currentEnv: PrismEnv = env || (typeof globalThis !== \"undefined\" ? (globalThis as any) : {});\n const url = new URL(request.url);\n\n // 前置环境变量完整性检查\n if (!currentEnv.PRISM_URL || !currentEnv.PRISM_CLIENT_ID || !currentEnv.PRISM_CLIENT_SECRET) {\n return new Response(\"配置错误:缺少必要的环境变量 (PRISM_URL, PRISM_CLIENT_ID, PRISM_CLIENT_SECRET)\", {\n status: 500,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\" },\n });\n }\n\n // 白名单路径直接放行\n if (this.isPublic(url, request)) {\n return this.handleRequest(request as AuthenticatedRequest, currentEnv, ctx, null);\n }\n\n const cookies = parseCookies(request.headers.get(\"Cookie\"));\n const prismBase = currentEnv.PRISM_URL.replace(/\\/+$/, \"\");\n // 泛域名 Cookie 支持(例如 .b.cc.cd 或 .cc.cd,让多级子域名站点直接共享登录态)\n // 自动容错:去除前后空格,若未写前导点号自动补齐,输入 \"b.cc.cd\" 也会标准化为 \".b.cc.cd\"\n let cookieDomain = (currentEnv.COOKIE_DOMAIN || \"\").trim();\n if (cookieDomain && !cookieDomain.startsWith(\".\")) {\n cookieDomain = \".\" + cookieDomain;\n }\n const domainAttr = cookieDomain ? `; Domain=${cookieDomain}` : \"\";\n\n // 【功能 1:主动登出】访问 /__logout 即可清理 Cookie 并登出\n if (url.pathname === \"/__logout\") {\n return new Response(\"已退出当前站点登录\", {\n status: 302,\n headers: {\n \"Location\": \"/\",\n \"Set-Cookie\": `prism_session=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0${domainAttr}`,\n },\n });\n }\n\n // 【功能 2:OAuth 回调处理】拦截 Prism 登录完成后的跳回\n if (url.pathname === \"/auth/callback\") {\n const error = url.searchParams.get(\"error\");\n if (error) {\n const desc = url.searchParams.get(\"error_description\") || error;\n return new Response(`Prism 授权失败: ${desc}`, { status: 403 });\n }\n\n const code = url.searchParams.get(\"code\");\n const rawState = url.searchParams.get(\"state\");\n if (!code) return new Response(\"缺少授权码 (Code)\", { status: 400 });\n\n // 校验 State:防范登录 CSRF 攻击与开放重定向\n let targetPath = \"/\";\n if (rawState && rawState.includes(\".\")) {\n const [statePayloadB64, stateSig] = rawState.split(\".\");\n if (await hmacVerify(statePayloadB64, stateSig, currentEnv.PRISM_CLIENT_SECRET)) {\n try {\n const parsed = JSON.parse(base64UrlDecode(statePayloadB64));\n // 10 分钟有效期校验\n if (parsed.exp && parsed.exp > Date.now()) {\n targetPath = sanitizeRedirectPath(parsed.path);\n }\n } catch (e) { }\n }\n }\n\n // 向 Prism 请求换取 Token(标准端点为 /api/oauth/token)\n const tokenRes = await fetch(`${prismBase}/api/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n client_id: currentEnv.PRISM_CLIENT_ID,\n client_secret: currentEnv.PRISM_CLIENT_SECRET,\n code,\n redirect_uri: `${url.origin}/auth/callback`,\n }),\n });\n\n if (!tokenRes.ok) {\n const errText = await tokenRes.text();\n return new Response(`Prism 换取 Token 失败: ${errText}`, { status: 401 });\n }\n\n const tokenData: any = await tokenRes.json();\n let user: PrismUser = {};\n\n // 解析用户身份信息(优先从 OIDC id_token 解码,回退 userinfo 端点)\n if (tokenData.id_token) {\n try {\n const idPayload = tokenData.id_token.split(\".\")[1];\n user = JSON.parse(base64UrlDecode(idPayload));\n } catch (e) { }\n } else if (tokenData.access_token) {\n try {\n const uRes = await fetch(`${prismBase}/api/oauth/userinfo`, {\n headers: { Authorization: `Bearer ${tokenData.access_token}` },\n });\n if (uRes.ok) user = await uRes.json();\n } catch (e) { }\n }\n\n // 生成带签名的 Session Cookie(绑定 client_id,默认有效期 7 天)\n const expireAt = Date.now() + 7 * 86400 * 1000;\n const sessionPayload = {\n aud: currentEnv.PRISM_CLIENT_ID,\n exp: expireAt,\n user,\n };\n const payloadStr = JSON.stringify(sessionPayload);\n const encodedPayload = base64UrlEncode(payloadStr);\n const signature = await hmacSign(encodedPayload, currentEnv.PRISM_CLIENT_SECRET);\n const sessionValue = `${encodedPayload}.${signature}`;\n\n return new Response(null, {\n status: 302,\n headers: {\n \"Location\": targetPath,\n \"Set-Cookie\": `prism_session=${sessionValue}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=604800${domainAttr}`,\n },\n });\n }\n\n // 【功能 3:校验本地登录态】\n const sessionCookie = cookies[\"prism_session\"];\n let user: PrismUser | null = null;\n\n if (sessionCookie && sessionCookie.includes(\".\")) {\n const [encodedPayload, signature] = sessionCookie.split(\".\");\n if (encodedPayload && signature) {\n const isValid = await hmacVerify(encodedPayload, signature, currentEnv.PRISM_CLIENT_SECRET);\n if (isValid) {\n try {\n const sessionData = JSON.parse(base64UrlDecode(encodedPayload));\n // 校验有效期与 Client ID 归属\n if (\n Number(sessionData.exp) > Date.now() &&\n sessionData.aud === currentEnv.PRISM_CLIENT_ID\n ) {\n user = sessionData.user || { authenticated: true };\n }\n } catch (e) { }\n }\n }\n }\n\n // 【功能 4:未登录拦截】\n if (!user) {\n // 对 API 或 Ajax 请求返回 401 JSON,避免前端 302 跟随重定向导致 CORS 跨域报错\n const isApi = url.pathname.startsWith(\"/api/\") ||\n request.headers.get(\"Accept\")?.includes(\"application/json\") ||\n request.headers.get(\"X-Requested-With\") === \"XMLHttpRequest\";\n\n if (isApi) {\n return new Response(\n JSON.stringify({ error: \"Unauthorized\", message: \"未登录或登录已过期\" }),\n {\n status: 401,\n headers: { \"Content-Type\": \"application/json; charset=utf-8\" },\n }\n );\n }\n\n // 生成带签名与防篡改的 State(有效期 10 分钟)\n const stateData = JSON.stringify({\n path: url.pathname + url.search,\n exp: Date.now() + 600 * 1000,\n });\n const stateB64 = base64UrlEncode(stateData);\n const stateSig = await hmacSign(stateB64, currentEnv.PRISM_CLIENT_SECRET);\n const stateToken = `${stateB64}.${stateSig}`;\n\n // 网页端未登录:重定向至 Prism 登录(标准端点为 /api/oauth/authorize)\n const authUrl = new URL(`${prismBase}/api/oauth/authorize`);\n authUrl.searchParams.set(\"client_id\", currentEnv.PRISM_CLIENT_ID);\n authUrl.searchParams.set(\"redirect_uri\", `${url.origin}/auth/callback`);\n authUrl.searchParams.set(\"response_type\", \"code\");\n // scope 默认包含 openid profile email(按需通过 PRISM_SCOPE 覆盖)\n authUrl.searchParams.set(\"scope\", currentEnv.PRISM_SCOPE || \"openid profile email\");\n authUrl.searchParams.set(\"state\", stateToken);\n\n return Response.redirect(authUrl.toString(), 302);\n }\n\n // 鉴权通过:把 user 挂载到 request 并透传给业务逻辑\n const authReq = request as AuthenticatedRequest;\n authReq.user = user;\n return this.handleRequest(authReq, currentEnv, ctx, user);\n }\n\n /**\n * 子类重写的业务方法(增加第 4 个参数 user,获取当前登录用户信息)\n */\n async handleRequest(\n request: AuthenticatedRequest,\n env: PrismEnv,\n ctx: any,\n user: PrismUser | null\n ): Promise<Response> {\n return new Response(\"请在子类中重写 handleRequest 方法\", { status: 404 });\n }\n}\n\n// 兼容纯函数式习惯的导出包装器\nexport function withPrismAuth(\n handler: PrismHandler,\n options: PrismWorkerOptions = {}\n): (req: Request, env?: PrismEnv, ctx?: any) => Promise<Response> {\n const instance = new (class extends PrismWorker {\n override isPublic(url: URL, req: Request) {\n return options.isPublic ? options.isPublic(url, req) : super.isPublic(url, req);\n }\n override async handleRequest(req: AuthenticatedRequest, env: PrismEnv, ctx: any, user: PrismUser | null) {\n return handler(req, env, ctx, user);\n }\n })();\n return (req: Request, env?: PrismEnv, ctx?: any) => instance.fetch(req, env, ctx);\n}\n\n// 兼容旧版 Service Worker (addEventListener(\"fetch\")) 模式\nexport function registerServiceWorker(\n handleRequest: (event: any, user: PrismUser | null) => Promise<Response> | Response,\n options: PrismWorkerOptions = {}\n): void {\n const worker = new (class extends PrismWorker {\n override isPublic(url: URL, req: Request) {\n return options.isPublic ? options.isPublic(url, req) : super.isPublic(url, req);\n }\n override async handleRequest(request: AuthenticatedRequest, env: PrismEnv, ctx: any, user: PrismUser | null) {\n // 构造兼容旧版代码的 event 对象\n const fakeEvent = {\n request,\n respondWith: () => { },\n waitUntil: (p: Promise<any>) => (ctx && ctx.waitUntil ? ctx.waitUntil(p) : p),\n user,\n };\n return handleRequest(fakeEvent, user);\n }\n })();\n\n if (typeof addEventListener !== \"undefined\") {\n addEventListener(\"fetch\", (event: any) => {\n event.respondWith(worker.fetch(event.request, (globalThis as any), event));\n });\n }\n}\n\nexport default PrismWorker;\n"],"mappings":"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","names":["user"]}
@@ -0,0 +1,42 @@
1
+ interface PrismEnv {
2
+ PRISM_URL: string;
3
+ PRISM_CLIENT_ID: string;
4
+ PRISM_CLIENT_SECRET: string;
5
+ COOKIE_DOMAIN?: string;
6
+ PRISM_SCOPE?: string;
7
+ [key: string]: any;
8
+ }
9
+ interface PrismUser {
10
+ sub?: string;
11
+ name?: string;
12
+ email?: string;
13
+ preferred_username?: string;
14
+ authenticated?: boolean;
15
+ [key: string]: any;
16
+ }
17
+ interface AuthenticatedRequest extends Request {
18
+ user?: PrismUser;
19
+ }
20
+ interface PrismWorkerOptions {
21
+ isPublic?: (url: URL, req: Request) => boolean;
22
+ }
23
+ type PrismHandler = (req: AuthenticatedRequest, env: PrismEnv, ctx: any, user: PrismUser | null) => Promise<Response> | Response;
24
+ declare class PrismWorker {
25
+ /**
26
+ * 可选扩展:指定公开免登路径(如 /favicon.ico 或静态公共 API)
27
+ * 子类可以重写此方法以自定义放行规则
28
+ */
29
+ isPublic(url: URL, request: Request): boolean;
30
+ /**
31
+ * Worker 入口:拦截未登录、OAuth 回调、登出并透传业务请求
32
+ */
33
+ fetch(request: Request, env?: PrismEnv, ctx?: any): Promise<Response>;
34
+ /**
35
+ * 子类重写的业务方法(增加第 4 个参数 user,获取当前登录用户信息)
36
+ */
37
+ handleRequest(request: AuthenticatedRequest, env: PrismEnv, ctx: any, user: PrismUser | null): Promise<Response>;
38
+ }
39
+ declare function withPrismAuth(handler: PrismHandler, options?: PrismWorkerOptions): (req: Request, env?: PrismEnv, ctx?: any) => Promise<Response>;
40
+ declare function registerServiceWorker(handleRequest: (event: any, user: PrismUser | null) => Promise<Response> | Response, options?: PrismWorkerOptions): void;
41
+
42
+ export { type AuthenticatedRequest, type PrismEnv, type PrismHandler, type PrismUser, PrismWorker, type PrismWorkerOptions, PrismWorker as default, registerServiceWorker, withPrismAuth };
@@ -0,0 +1,42 @@
1
+ interface PrismEnv {
2
+ PRISM_URL: string;
3
+ PRISM_CLIENT_ID: string;
4
+ PRISM_CLIENT_SECRET: string;
5
+ COOKIE_DOMAIN?: string;
6
+ PRISM_SCOPE?: string;
7
+ [key: string]: any;
8
+ }
9
+ interface PrismUser {
10
+ sub?: string;
11
+ name?: string;
12
+ email?: string;
13
+ preferred_username?: string;
14
+ authenticated?: boolean;
15
+ [key: string]: any;
16
+ }
17
+ interface AuthenticatedRequest extends Request {
18
+ user?: PrismUser;
19
+ }
20
+ interface PrismWorkerOptions {
21
+ isPublic?: (url: URL, req: Request) => boolean;
22
+ }
23
+ type PrismHandler = (req: AuthenticatedRequest, env: PrismEnv, ctx: any, user: PrismUser | null) => Promise<Response> | Response;
24
+ declare class PrismWorker {
25
+ /**
26
+ * 可选扩展:指定公开免登路径(如 /favicon.ico 或静态公共 API)
27
+ * 子类可以重写此方法以自定义放行规则
28
+ */
29
+ isPublic(url: URL, request: Request): boolean;
30
+ /**
31
+ * Worker 入口:拦截未登录、OAuth 回调、登出并透传业务请求
32
+ */
33
+ fetch(request: Request, env?: PrismEnv, ctx?: any): Promise<Response>;
34
+ /**
35
+ * 子类重写的业务方法(增加第 4 个参数 user,获取当前登录用户信息)
36
+ */
37
+ handleRequest(request: AuthenticatedRequest, env: PrismEnv, ctx: any, user: PrismUser | null): Promise<Response>;
38
+ }
39
+ declare function withPrismAuth(handler: PrismHandler, options?: PrismWorkerOptions): (req: Request, env?: PrismEnv, ctx?: any) => Promise<Response>;
40
+ declare function registerServiceWorker(handleRequest: (event: any, user: PrismUser | null) => Promise<Response> | Response, options?: PrismWorkerOptions): void;
41
+
42
+ export { type AuthenticatedRequest, type PrismEnv, type PrismHandler, type PrismUser, PrismWorker, type PrismWorkerOptions, PrismWorker as default, registerServiceWorker, withPrismAuth };
@@ -0,0 +1,282 @@
1
+ // src/prism-auth.ts
2
+ function timingSafeEqual(a, b) {
3
+ if (typeof a !== "string" || typeof b !== "string" || a.length !== b.length) {
4
+ return false;
5
+ }
6
+ let diff = 0;
7
+ for (let i = 0; i < a.length; i++) {
8
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
9
+ }
10
+ return diff === 0;
11
+ }
12
+ var _cachedSecret = null;
13
+ var _cachedKey = null;
14
+ async function getHmacKey(secret) {
15
+ if (_cachedKey && _cachedSecret === secret) return _cachedKey;
16
+ const enc = new TextEncoder();
17
+ _cachedKey = await crypto.subtle.importKey(
18
+ "raw",
19
+ enc.encode(secret),
20
+ { name: "HMAC", hash: "SHA-256" },
21
+ false,
22
+ ["sign", "verify"]
23
+ );
24
+ _cachedSecret = secret;
25
+ return _cachedKey;
26
+ }
27
+ async function hmacSign(data, secret) {
28
+ const key = await getHmacKey(secret);
29
+ const enc = new TextEncoder();
30
+ const sig = await crypto.subtle.sign("HMAC", key, enc.encode(data));
31
+ return btoa(String.fromCharCode(...new Uint8Array(sig))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
32
+ }
33
+ async function hmacVerify(data, signature, secret) {
34
+ const expected = await hmacSign(data, secret);
35
+ return timingSafeEqual(expected, signature);
36
+ }
37
+ function base64UrlEncode(str) {
38
+ const bytes = new TextEncoder().encode(str);
39
+ let binary = "";
40
+ for (let i = 0; i < bytes.byteLength; i++) {
41
+ binary += String.fromCharCode(bytes[i]);
42
+ }
43
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
44
+ }
45
+ function base64UrlDecode(str) {
46
+ let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
47
+ while (base64.length % 4) {
48
+ base64 += "=";
49
+ }
50
+ const binary = atob(base64);
51
+ const bytes = new Uint8Array(binary.length);
52
+ for (let i = 0; i < binary.length; i++) {
53
+ bytes[i] = binary.charCodeAt(i);
54
+ }
55
+ return new TextDecoder().decode(bytes);
56
+ }
57
+ function parseCookies(cookieHeader) {
58
+ const cookies = {};
59
+ if (!cookieHeader) return cookies;
60
+ for (const item of cookieHeader.split(";")) {
61
+ const idx = item.indexOf("=");
62
+ if (idx !== -1) {
63
+ const key = item.slice(0, idx).trim();
64
+ const val = item.slice(idx + 1).trim();
65
+ cookies[key] = val;
66
+ }
67
+ }
68
+ return cookies;
69
+ }
70
+ function sanitizeRedirectPath(path) {
71
+ if (!path || typeof path !== "string") return "/";
72
+ if (path.startsWith("/") && !path.startsWith("//") && !path.startsWith("/\\")) {
73
+ return path;
74
+ }
75
+ return "/";
76
+ }
77
+ var PrismWorker = class {
78
+ /**
79
+ * 可选扩展:指定公开免登路径(如 /favicon.ico 或静态公共 API)
80
+ * 子类可以重写此方法以自定义放行规则
81
+ */
82
+ isPublic(url, request) {
83
+ return url.pathname === "/favicon.ico";
84
+ }
85
+ /**
86
+ * Worker 入口:拦截未登录、OAuth 回调、登出并透传业务请求
87
+ */
88
+ async fetch(request, env, ctx) {
89
+ const currentEnv = env || (typeof globalThis !== "undefined" ? globalThis : {});
90
+ const url = new URL(request.url);
91
+ if (!currentEnv.PRISM_URL || !currentEnv.PRISM_CLIENT_ID || !currentEnv.PRISM_CLIENT_SECRET) {
92
+ return new Response("\u914D\u7F6E\u9519\u8BEF\uFF1A\u7F3A\u5C11\u5FC5\u8981\u7684\u73AF\u5883\u53D8\u91CF (PRISM_URL, PRISM_CLIENT_ID, PRISM_CLIENT_SECRET)", {
93
+ status: 500,
94
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
95
+ });
96
+ }
97
+ if (this.isPublic(url, request)) {
98
+ return this.handleRequest(request, currentEnv, ctx, null);
99
+ }
100
+ const cookies = parseCookies(request.headers.get("Cookie"));
101
+ const prismBase = currentEnv.PRISM_URL.replace(/\/+$/, "");
102
+ let cookieDomain = (currentEnv.COOKIE_DOMAIN || "").trim();
103
+ if (cookieDomain && !cookieDomain.startsWith(".")) {
104
+ cookieDomain = "." + cookieDomain;
105
+ }
106
+ const domainAttr = cookieDomain ? `; Domain=${cookieDomain}` : "";
107
+ if (url.pathname === "/__logout") {
108
+ return new Response("\u5DF2\u9000\u51FA\u5F53\u524D\u7AD9\u70B9\u767B\u5F55", {
109
+ status: 302,
110
+ headers: {
111
+ "Location": "/",
112
+ "Set-Cookie": `prism_session=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0${domainAttr}`
113
+ }
114
+ });
115
+ }
116
+ if (url.pathname === "/auth/callback") {
117
+ const error = url.searchParams.get("error");
118
+ if (error) {
119
+ const desc = url.searchParams.get("error_description") || error;
120
+ return new Response(`Prism \u6388\u6743\u5931\u8D25: ${desc}`, { status: 403 });
121
+ }
122
+ const code = url.searchParams.get("code");
123
+ const rawState = url.searchParams.get("state");
124
+ if (!code) return new Response("\u7F3A\u5C11\u6388\u6743\u7801 (Code)", { status: 400 });
125
+ let targetPath = "/";
126
+ if (rawState && rawState.includes(".")) {
127
+ const [statePayloadB64, stateSig] = rawState.split(".");
128
+ if (await hmacVerify(statePayloadB64, stateSig, currentEnv.PRISM_CLIENT_SECRET)) {
129
+ try {
130
+ const parsed = JSON.parse(base64UrlDecode(statePayloadB64));
131
+ if (parsed.exp && parsed.exp > Date.now()) {
132
+ targetPath = sanitizeRedirectPath(parsed.path);
133
+ }
134
+ } catch (e) {
135
+ }
136
+ }
137
+ }
138
+ const tokenRes = await fetch(`${prismBase}/api/oauth/token`, {
139
+ method: "POST",
140
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
141
+ body: new URLSearchParams({
142
+ grant_type: "authorization_code",
143
+ client_id: currentEnv.PRISM_CLIENT_ID,
144
+ client_secret: currentEnv.PRISM_CLIENT_SECRET,
145
+ code,
146
+ redirect_uri: `${url.origin}/auth/callback`
147
+ })
148
+ });
149
+ if (!tokenRes.ok) {
150
+ const errText = await tokenRes.text();
151
+ return new Response(`Prism \u6362\u53D6 Token \u5931\u8D25: ${errText}`, { status: 401 });
152
+ }
153
+ const tokenData = await tokenRes.json();
154
+ let user2 = {};
155
+ if (tokenData.id_token) {
156
+ try {
157
+ const idPayload = tokenData.id_token.split(".")[1];
158
+ user2 = JSON.parse(base64UrlDecode(idPayload));
159
+ } catch (e) {
160
+ }
161
+ } else if (tokenData.access_token) {
162
+ try {
163
+ const uRes = await fetch(`${prismBase}/api/oauth/userinfo`, {
164
+ headers: { Authorization: `Bearer ${tokenData.access_token}` }
165
+ });
166
+ if (uRes.ok) user2 = await uRes.json();
167
+ } catch (e) {
168
+ }
169
+ }
170
+ const expireAt = Date.now() + 7 * 86400 * 1e3;
171
+ const sessionPayload = {
172
+ aud: currentEnv.PRISM_CLIENT_ID,
173
+ exp: expireAt,
174
+ user: user2
175
+ };
176
+ const payloadStr = JSON.stringify(sessionPayload);
177
+ const encodedPayload = base64UrlEncode(payloadStr);
178
+ const signature = await hmacSign(encodedPayload, currentEnv.PRISM_CLIENT_SECRET);
179
+ const sessionValue = `${encodedPayload}.${signature}`;
180
+ return new Response(null, {
181
+ status: 302,
182
+ headers: {
183
+ "Location": targetPath,
184
+ "Set-Cookie": `prism_session=${sessionValue}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=604800${domainAttr}`
185
+ }
186
+ });
187
+ }
188
+ const sessionCookie = cookies["prism_session"];
189
+ let user = null;
190
+ if (sessionCookie && sessionCookie.includes(".")) {
191
+ const [encodedPayload, signature] = sessionCookie.split(".");
192
+ if (encodedPayload && signature) {
193
+ const isValid = await hmacVerify(encodedPayload, signature, currentEnv.PRISM_CLIENT_SECRET);
194
+ if (isValid) {
195
+ try {
196
+ const sessionData = JSON.parse(base64UrlDecode(encodedPayload));
197
+ if (Number(sessionData.exp) > Date.now() && sessionData.aud === currentEnv.PRISM_CLIENT_ID) {
198
+ user = sessionData.user || { authenticated: true };
199
+ }
200
+ } catch (e) {
201
+ }
202
+ }
203
+ }
204
+ }
205
+ if (!user) {
206
+ const isApi = url.pathname.startsWith("/api/") || request.headers.get("Accept")?.includes("application/json") || request.headers.get("X-Requested-With") === "XMLHttpRequest";
207
+ if (isApi) {
208
+ return new Response(
209
+ JSON.stringify({ error: "Unauthorized", message: "\u672A\u767B\u5F55\u6216\u767B\u5F55\u5DF2\u8FC7\u671F" }),
210
+ {
211
+ status: 401,
212
+ headers: { "Content-Type": "application/json; charset=utf-8" }
213
+ }
214
+ );
215
+ }
216
+ const stateData = JSON.stringify({
217
+ path: url.pathname + url.search,
218
+ exp: Date.now() + 600 * 1e3
219
+ });
220
+ const stateB64 = base64UrlEncode(stateData);
221
+ const stateSig = await hmacSign(stateB64, currentEnv.PRISM_CLIENT_SECRET);
222
+ const stateToken = `${stateB64}.${stateSig}`;
223
+ const authUrl = new URL(`${prismBase}/api/oauth/authorize`);
224
+ authUrl.searchParams.set("client_id", currentEnv.PRISM_CLIENT_ID);
225
+ authUrl.searchParams.set("redirect_uri", `${url.origin}/auth/callback`);
226
+ authUrl.searchParams.set("response_type", "code");
227
+ authUrl.searchParams.set("scope", currentEnv.PRISM_SCOPE || "openid profile email");
228
+ authUrl.searchParams.set("state", stateToken);
229
+ return Response.redirect(authUrl.toString(), 302);
230
+ }
231
+ const authReq = request;
232
+ authReq.user = user;
233
+ return this.handleRequest(authReq, currentEnv, ctx, user);
234
+ }
235
+ /**
236
+ * 子类重写的业务方法(增加第 4 个参数 user,获取当前登录用户信息)
237
+ */
238
+ async handleRequest(request, env, ctx, user) {
239
+ return new Response("\u8BF7\u5728\u5B50\u7C7B\u4E2D\u91CD\u5199 handleRequest \u65B9\u6CD5", { status: 404 });
240
+ }
241
+ };
242
+ function withPrismAuth(handler, options = {}) {
243
+ const instance = new class extends PrismWorker {
244
+ isPublic(url, req) {
245
+ return options.isPublic ? options.isPublic(url, req) : super.isPublic(url, req);
246
+ }
247
+ async handleRequest(req, env, ctx, user) {
248
+ return handler(req, env, ctx, user);
249
+ }
250
+ }();
251
+ return (req, env, ctx) => instance.fetch(req, env, ctx);
252
+ }
253
+ function registerServiceWorker(handleRequest, options = {}) {
254
+ const worker = new class extends PrismWorker {
255
+ isPublic(url, req) {
256
+ return options.isPublic ? options.isPublic(url, req) : super.isPublic(url, req);
257
+ }
258
+ async handleRequest(request, env, ctx, user) {
259
+ const fakeEvent = {
260
+ request,
261
+ respondWith: () => {
262
+ },
263
+ waitUntil: (p) => ctx && ctx.waitUntil ? ctx.waitUntil(p) : p,
264
+ user
265
+ };
266
+ return handleRequest(fakeEvent, user);
267
+ }
268
+ }();
269
+ if (typeof addEventListener !== "undefined") {
270
+ addEventListener("fetch", (event) => {
271
+ event.respondWith(worker.fetch(event.request, globalThis, event));
272
+ });
273
+ }
274
+ }
275
+ var prism_auth_default = PrismWorker;
276
+ export {
277
+ PrismWorker,
278
+ prism_auth_default as default,
279
+ registerServiceWorker,
280
+ withPrismAuth
281
+ };
282
+ //# sourceMappingURL=prism-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/prism-auth.ts"],"sourcesContent":["// prism-auth.ts\n\n// =========================================================================\n// 类型定义 (Types)\n// =========================================================================\n\nexport interface PrismEnv {\n PRISM_URL: string;\n PRISM_CLIENT_ID: string;\n PRISM_CLIENT_SECRET: string;\n COOKIE_DOMAIN?: string;\n PRISM_SCOPE?: string;\n [key: string]: any;\n}\n\nexport interface PrismUser {\n sub?: string;\n name?: string;\n email?: string;\n preferred_username?: string;\n authenticated?: boolean;\n [key: string]: any;\n}\n\nexport interface AuthenticatedRequest extends Request {\n user?: PrismUser;\n}\n\nexport interface PrismWorkerOptions {\n isPublic?: (url: URL, req: Request) => boolean;\n}\n\nexport type PrismHandler = (\n req: AuthenticatedRequest,\n env: PrismEnv,\n ctx: any,\n user: PrismUser | null\n) => Promise<Response> | Response;\n\n// =========================================================================\n// 1. 加密与安全工具函数 (Web Crypto & Timing-Safe)\n// =========================================================================\n\n// 恒定时间字符串比对(防御时序侧信道攻击)\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (typeof a !== \"string\" || typeof b !== \"string\" || a.length !== b.length) {\n return false;\n }\n let diff = 0;\n for (let i = 0; i < a.length; i++) {\n diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return diff === 0;\n}\n\n// 缓存 CryptoKey,避免高并发下每个请求重复 importKey 消耗 CPU\nlet _cachedSecret: string | null = null;\nlet _cachedKey: CryptoKey | null = null;\n\nasync function getHmacKey(secret: string): Promise<CryptoKey> {\n if (_cachedKey && _cachedSecret === secret) return _cachedKey;\n const enc = new TextEncoder();\n _cachedKey = await crypto.subtle.importKey(\n \"raw\",\n enc.encode(secret),\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\", \"verify\"]\n );\n _cachedSecret = secret;\n return _cachedKey;\n}\n\n// 基于 Web Crypto 的 HMAC-SHA256 签名工具\nasync function hmacSign(data: string, secret: string): Promise<string> {\n const key = await getHmacKey(secret);\n const enc = new TextEncoder();\n const sig = await crypto.subtle.sign(\"HMAC\", key, enc.encode(data));\n return btoa(String.fromCharCode(...new Uint8Array(sig)))\n .replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nasync function hmacVerify(data: string, signature: string, secret: string): Promise<boolean> {\n const expected = await hmacSign(data, secret);\n return timingSafeEqual(expected, signature);\n}\n\n// UTF-8 安全的 Base64URL 编解码工具\nfunction base64UrlEncode(str: string): string {\n const bytes = new TextEncoder().encode(str);\n let binary = \"\";\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction base64UrlDecode(str: string): string {\n let base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4) {\n base64 += \"=\";\n }\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n// 健壮的 Cookie 解析方法(避免 split(\"=\") 误切 base64 中的等于号)\nfunction parseCookies(cookieHeader: string | null): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (!cookieHeader) return cookies;\n for (const item of cookieHeader.split(\";\")) {\n const idx = item.indexOf(\"=\");\n if (idx !== -1) {\n const key = item.slice(0, idx).trim();\n const val = item.slice(idx + 1).trim();\n cookies[key] = val;\n }\n }\n return cookies;\n}\n\n// 防御开放重定向(Open Redirect):强制要求单斜杠内部相对路径,禁止 // 或 /\\ 外部协议伪造\nfunction sanitizeRedirectPath(path: string | null | undefined): string {\n if (!path || typeof path !== \"string\") return \"/\";\n if (path.startsWith(\"/\") && !path.startsWith(\"//\") && !path.startsWith(\"/\\\\\")) {\n return path;\n }\n return \"/\";\n}\n\n// =========================================================================\n// 2. 鉴权基类 (PrismWorker)\n// =========================================================================\n\nexport class PrismWorker {\n /**\n * 可选扩展:指定公开免登路径(如 /favicon.ico 或静态公共 API)\n * 子类可以重写此方法以自定义放行规则\n */\n isPublic(url: URL, request: Request): boolean {\n return url.pathname === \"/favicon.ico\";\n }\n\n /**\n * Worker 入口:拦截未登录、OAuth 回调、登出并透传业务请求\n */\n async fetch(request: Request, env?: PrismEnv, ctx?: any): Promise<Response> {\n const currentEnv: PrismEnv = env || (typeof globalThis !== \"undefined\" ? (globalThis as any) : {});\n const url = new URL(request.url);\n\n // 前置环境变量完整性检查\n if (!currentEnv.PRISM_URL || !currentEnv.PRISM_CLIENT_ID || !currentEnv.PRISM_CLIENT_SECRET) {\n return new Response(\"配置错误:缺少必要的环境变量 (PRISM_URL, PRISM_CLIENT_ID, PRISM_CLIENT_SECRET)\", {\n status: 500,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\" },\n });\n }\n\n // 白名单路径直接放行\n if (this.isPublic(url, request)) {\n return this.handleRequest(request as AuthenticatedRequest, currentEnv, ctx, null);\n }\n\n const cookies = parseCookies(request.headers.get(\"Cookie\"));\n const prismBase = currentEnv.PRISM_URL.replace(/\\/+$/, \"\");\n // 泛域名 Cookie 支持(例如 .b.cc.cd 或 .cc.cd,让多级子域名站点直接共享登录态)\n // 自动容错:去除前后空格,若未写前导点号自动补齐,输入 \"b.cc.cd\" 也会标准化为 \".b.cc.cd\"\n let cookieDomain = (currentEnv.COOKIE_DOMAIN || \"\").trim();\n if (cookieDomain && !cookieDomain.startsWith(\".\")) {\n cookieDomain = \".\" + cookieDomain;\n }\n const domainAttr = cookieDomain ? `; Domain=${cookieDomain}` : \"\";\n\n // 【功能 1:主动登出】访问 /__logout 即可清理 Cookie 并登出\n if (url.pathname === \"/__logout\") {\n return new Response(\"已退出当前站点登录\", {\n status: 302,\n headers: {\n \"Location\": \"/\",\n \"Set-Cookie\": `prism_session=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0${domainAttr}`,\n },\n });\n }\n\n // 【功能 2:OAuth 回调处理】拦截 Prism 登录完成后的跳回\n if (url.pathname === \"/auth/callback\") {\n const error = url.searchParams.get(\"error\");\n if (error) {\n const desc = url.searchParams.get(\"error_description\") || error;\n return new Response(`Prism 授权失败: ${desc}`, { status: 403 });\n }\n\n const code = url.searchParams.get(\"code\");\n const rawState = url.searchParams.get(\"state\");\n if (!code) return new Response(\"缺少授权码 (Code)\", { status: 400 });\n\n // 校验 State:防范登录 CSRF 攻击与开放重定向\n let targetPath = \"/\";\n if (rawState && rawState.includes(\".\")) {\n const [statePayloadB64, stateSig] = rawState.split(\".\");\n if (await hmacVerify(statePayloadB64, stateSig, currentEnv.PRISM_CLIENT_SECRET)) {\n try {\n const parsed = JSON.parse(base64UrlDecode(statePayloadB64));\n // 10 分钟有效期校验\n if (parsed.exp && parsed.exp > Date.now()) {\n targetPath = sanitizeRedirectPath(parsed.path);\n }\n } catch (e) { }\n }\n }\n\n // 向 Prism 请求换取 Token(标准端点为 /api/oauth/token)\n const tokenRes = await fetch(`${prismBase}/api/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"authorization_code\",\n client_id: currentEnv.PRISM_CLIENT_ID,\n client_secret: currentEnv.PRISM_CLIENT_SECRET,\n code,\n redirect_uri: `${url.origin}/auth/callback`,\n }),\n });\n\n if (!tokenRes.ok) {\n const errText = await tokenRes.text();\n return new Response(`Prism 换取 Token 失败: ${errText}`, { status: 401 });\n }\n\n const tokenData: any = await tokenRes.json();\n let user: PrismUser = {};\n\n // 解析用户身份信息(优先从 OIDC id_token 解码,回退 userinfo 端点)\n if (tokenData.id_token) {\n try {\n const idPayload = tokenData.id_token.split(\".\")[1];\n user = JSON.parse(base64UrlDecode(idPayload));\n } catch (e) { }\n } else if (tokenData.access_token) {\n try {\n const uRes = await fetch(`${prismBase}/api/oauth/userinfo`, {\n headers: { Authorization: `Bearer ${tokenData.access_token}` },\n });\n if (uRes.ok) user = await uRes.json();\n } catch (e) { }\n }\n\n // 生成带签名的 Session Cookie(绑定 client_id,默认有效期 7 天)\n const expireAt = Date.now() + 7 * 86400 * 1000;\n const sessionPayload = {\n aud: currentEnv.PRISM_CLIENT_ID,\n exp: expireAt,\n user,\n };\n const payloadStr = JSON.stringify(sessionPayload);\n const encodedPayload = base64UrlEncode(payloadStr);\n const signature = await hmacSign(encodedPayload, currentEnv.PRISM_CLIENT_SECRET);\n const sessionValue = `${encodedPayload}.${signature}`;\n\n return new Response(null, {\n status: 302,\n headers: {\n \"Location\": targetPath,\n \"Set-Cookie\": `prism_session=${sessionValue}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=604800${domainAttr}`,\n },\n });\n }\n\n // 【功能 3:校验本地登录态】\n const sessionCookie = cookies[\"prism_session\"];\n let user: PrismUser | null = null;\n\n if (sessionCookie && sessionCookie.includes(\".\")) {\n const [encodedPayload, signature] = sessionCookie.split(\".\");\n if (encodedPayload && signature) {\n const isValid = await hmacVerify(encodedPayload, signature, currentEnv.PRISM_CLIENT_SECRET);\n if (isValid) {\n try {\n const sessionData = JSON.parse(base64UrlDecode(encodedPayload));\n // 校验有效期与 Client ID 归属\n if (\n Number(sessionData.exp) > Date.now() &&\n sessionData.aud === currentEnv.PRISM_CLIENT_ID\n ) {\n user = sessionData.user || { authenticated: true };\n }\n } catch (e) { }\n }\n }\n }\n\n // 【功能 4:未登录拦截】\n if (!user) {\n // 对 API 或 Ajax 请求返回 401 JSON,避免前端 302 跟随重定向导致 CORS 跨域报错\n const isApi = url.pathname.startsWith(\"/api/\") ||\n request.headers.get(\"Accept\")?.includes(\"application/json\") ||\n request.headers.get(\"X-Requested-With\") === \"XMLHttpRequest\";\n\n if (isApi) {\n return new Response(\n JSON.stringify({ error: \"Unauthorized\", message: \"未登录或登录已过期\" }),\n {\n status: 401,\n headers: { \"Content-Type\": \"application/json; charset=utf-8\" },\n }\n );\n }\n\n // 生成带签名与防篡改的 State(有效期 10 分钟)\n const stateData = JSON.stringify({\n path: url.pathname + url.search,\n exp: Date.now() + 600 * 1000,\n });\n const stateB64 = base64UrlEncode(stateData);\n const stateSig = await hmacSign(stateB64, currentEnv.PRISM_CLIENT_SECRET);\n const stateToken = `${stateB64}.${stateSig}`;\n\n // 网页端未登录:重定向至 Prism 登录(标准端点为 /api/oauth/authorize)\n const authUrl = new URL(`${prismBase}/api/oauth/authorize`);\n authUrl.searchParams.set(\"client_id\", currentEnv.PRISM_CLIENT_ID);\n authUrl.searchParams.set(\"redirect_uri\", `${url.origin}/auth/callback`);\n authUrl.searchParams.set(\"response_type\", \"code\");\n // scope 默认包含 openid profile email(按需通过 PRISM_SCOPE 覆盖)\n authUrl.searchParams.set(\"scope\", currentEnv.PRISM_SCOPE || \"openid profile email\");\n authUrl.searchParams.set(\"state\", stateToken);\n\n return Response.redirect(authUrl.toString(), 302);\n }\n\n // 鉴权通过:把 user 挂载到 request 并透传给业务逻辑\n const authReq = request as AuthenticatedRequest;\n authReq.user = user;\n return this.handleRequest(authReq, currentEnv, ctx, user);\n }\n\n /**\n * 子类重写的业务方法(增加第 4 个参数 user,获取当前登录用户信息)\n */\n async handleRequest(\n request: AuthenticatedRequest,\n env: PrismEnv,\n ctx: any,\n user: PrismUser | null\n ): Promise<Response> {\n return new Response(\"请在子类中重写 handleRequest 方法\", { status: 404 });\n }\n}\n\n// 兼容纯函数式习惯的导出包装器\nexport function withPrismAuth(\n handler: PrismHandler,\n options: PrismWorkerOptions = {}\n): (req: Request, env?: PrismEnv, ctx?: any) => Promise<Response> {\n const instance = new (class extends PrismWorker {\n override isPublic(url: URL, req: Request) {\n return options.isPublic ? options.isPublic(url, req) : super.isPublic(url, req);\n }\n override async handleRequest(req: AuthenticatedRequest, env: PrismEnv, ctx: any, user: PrismUser | null) {\n return handler(req, env, ctx, user);\n }\n })();\n return (req: Request, env?: PrismEnv, ctx?: any) => instance.fetch(req, env, ctx);\n}\n\n// 兼容旧版 Service Worker (addEventListener(\"fetch\")) 模式\nexport function registerServiceWorker(\n handleRequest: (event: any, user: PrismUser | null) => Promise<Response> | Response,\n options: PrismWorkerOptions = {}\n): void {\n const worker = new (class extends PrismWorker {\n override isPublic(url: URL, req: Request) {\n return options.isPublic ? options.isPublic(url, req) : super.isPublic(url, req);\n }\n override async handleRequest(request: AuthenticatedRequest, env: PrismEnv, ctx: any, user: PrismUser | null) {\n // 构造兼容旧版代码的 event 对象\n const fakeEvent = {\n request,\n respondWith: () => { },\n waitUntil: (p: Promise<any>) => (ctx && ctx.waitUntil ? ctx.waitUntil(p) : p),\n user,\n };\n return handleRequest(fakeEvent, user);\n }\n })();\n\n if (typeof addEventListener !== \"undefined\") {\n addEventListener(\"fetch\", (event: any) => {\n event.respondWith(worker.fetch(event.request, (globalThis as any), event));\n });\n }\n}\n\nexport default PrismWorker;\n"],"mappings":";AA4CA,SAAS,gBAAgB,GAAW,GAAoB;AACpD,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,EAAE,WAAW,EAAE,QAAQ;AACzE,WAAO;AAAA,EACX;AACA,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AAC/B,YAAQ,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC;AAAA,EAC5C;AACA,SAAO,SAAS;AACpB;AAGA,IAAI,gBAA+B;AACnC,IAAI,aAA+B;AAEnC,eAAe,WAAW,QAAoC;AAC1D,MAAI,cAAc,kBAAkB,OAAQ,QAAO;AACnD,QAAM,MAAM,IAAI,YAAY;AAC5B,eAAa,MAAM,OAAO,OAAO;AAAA,IAC7B;AAAA,IACA,IAAI,OAAO,MAAM;AAAA,IACjB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EACrB;AACA,kBAAgB;AAChB,SAAO;AACX;AAGA,eAAe,SAAS,MAAc,QAAiC;AACnE,QAAM,MAAM,MAAM,WAAW,MAAM;AACnC,QAAM,MAAM,IAAI,YAAY;AAC5B,QAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,IAAI,OAAO,IAAI,CAAC;AAClE,SAAO,KAAK,OAAO,aAAa,GAAG,IAAI,WAAW,GAAG,CAAC,CAAC,EAClD,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAClE;AAEA,eAAe,WAAW,MAAc,WAAmB,QAAkC;AACzF,QAAM,WAAW,MAAM,SAAS,MAAM,MAAM;AAC5C,SAAO,gBAAgB,UAAU,SAAS;AAC9C;AAGA,SAAS,gBAAgB,KAAqB;AAC1C,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,GAAG;AAC1C,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,YAAY,KAAK;AACvC,cAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,EAC1C;AACA,SAAO,KAAK,MAAM,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AACjF;AAEA,SAAS,gBAAgB,KAAqB;AAC1C,MAAI,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACrD,SAAO,OAAO,SAAS,GAAG;AACtB,cAAU;AAAA,EACd;AACA,QAAM,SAAS,KAAK,MAAM;AAC1B,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACpC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAClC;AACA,SAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AACzC;AAGA,SAAS,aAAa,cAAqD;AACvE,QAAM,UAAkC,CAAC;AACzC,MAAI,CAAC,aAAc,QAAO;AAC1B,aAAW,QAAQ,aAAa,MAAM,GAAG,GAAG;AACxC,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,QAAQ,IAAI;AACZ,YAAM,MAAM,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK;AACpC,YAAM,MAAM,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK;AACrC,cAAQ,GAAG,IAAI;AAAA,IACnB;AAAA,EACJ;AACA,SAAO;AACX;AAGA,SAAS,qBAAqB,MAAyC;AACnE,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,MAAI,KAAK,WAAW,GAAG,KAAK,CAAC,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3E,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAMO,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKrB,SAAS,KAAU,SAA2B;AAC1C,WAAO,IAAI,aAAa;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,SAAkB,KAAgB,KAA8B;AACxE,UAAM,aAAuB,QAAQ,OAAO,eAAe,cAAe,aAAqB,CAAC;AAChG,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAG/B,QAAI,CAAC,WAAW,aAAa,CAAC,WAAW,mBAAmB,CAAC,WAAW,qBAAqB;AACzF,aAAO,IAAI,SAAS,0IAAoE;AAAA,QACpF,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,4BAA4B;AAAA,MAC3D,CAAC;AAAA,IACL;AAGA,QAAI,KAAK,SAAS,KAAK,OAAO,GAAG;AAC7B,aAAO,KAAK,cAAc,SAAiC,YAAY,KAAK,IAAI;AAAA,IACpF;AAEA,UAAM,UAAU,aAAa,QAAQ,QAAQ,IAAI,QAAQ,CAAC;AAC1D,UAAM,YAAY,WAAW,UAAU,QAAQ,QAAQ,EAAE;AAGzD,QAAI,gBAAgB,WAAW,iBAAiB,IAAI,KAAK;AACzD,QAAI,gBAAgB,CAAC,aAAa,WAAW,GAAG,GAAG;AAC/C,qBAAe,MAAM;AAAA,IACzB;AACA,UAAM,aAAa,eAAe,YAAY,YAAY,KAAK;AAG/D,QAAI,IAAI,aAAa,aAAa;AAC9B,aAAO,IAAI,SAAS,0DAAa;AAAA,QAC7B,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,YAAY;AAAA,UACZ,cAAc,oEAAoE,UAAU;AAAA,QAChG;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,QAAI,IAAI,aAAa,kBAAkB;AACnC,YAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAC1C,UAAI,OAAO;AACP,cAAM,OAAO,IAAI,aAAa,IAAI,mBAAmB,KAAK;AAC1D,eAAO,IAAI,SAAS,mCAAe,IAAI,IAAI,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC9D;AAEA,YAAM,OAAO,IAAI,aAAa,IAAI,MAAM;AACxC,YAAM,WAAW,IAAI,aAAa,IAAI,OAAO;AAC7C,UAAI,CAAC,KAAM,QAAO,IAAI,SAAS,yCAAgB,EAAE,QAAQ,IAAI,CAAC;AAG9D,UAAI,aAAa;AACjB,UAAI,YAAY,SAAS,SAAS,GAAG,GAAG;AACpC,cAAM,CAAC,iBAAiB,QAAQ,IAAI,SAAS,MAAM,GAAG;AACtD,YAAI,MAAM,WAAW,iBAAiB,UAAU,WAAW,mBAAmB,GAAG;AAC7E,cAAI;AACA,kBAAM,SAAS,KAAK,MAAM,gBAAgB,eAAe,CAAC;AAE1D,gBAAI,OAAO,OAAO,OAAO,MAAM,KAAK,IAAI,GAAG;AACvC,2BAAa,qBAAqB,OAAO,IAAI;AAAA,YACjD;AAAA,UACJ,SAAS,GAAG;AAAA,UAAE;AAAA,QAClB;AAAA,MACJ;AAGA,YAAM,WAAW,MAAM,MAAM,GAAG,SAAS,oBAAoB;AAAA,QACzD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,QAC/D,MAAM,IAAI,gBAAgB;AAAA,UACtB,YAAY;AAAA,UACZ,WAAW,WAAW;AAAA,UACtB,eAAe,WAAW;AAAA,UAC1B;AAAA,UACA,cAAc,GAAG,IAAI,MAAM;AAAA,QAC/B,CAAC;AAAA,MACL,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AACd,cAAM,UAAU,MAAM,SAAS,KAAK;AACpC,eAAO,IAAI,SAAS,0CAAsB,OAAO,IAAI,EAAE,QAAQ,IAAI,CAAC;AAAA,MACxE;AAEA,YAAM,YAAiB,MAAM,SAAS,KAAK;AAC3C,UAAIA,QAAkB,CAAC;AAGvB,UAAI,UAAU,UAAU;AACpB,YAAI;AACA,gBAAM,YAAY,UAAU,SAAS,MAAM,GAAG,EAAE,CAAC;AACjD,UAAAA,QAAO,KAAK,MAAM,gBAAgB,SAAS,CAAC;AAAA,QAChD,SAAS,GAAG;AAAA,QAAE;AAAA,MAClB,WAAW,UAAU,cAAc;AAC/B,YAAI;AACA,gBAAM,OAAO,MAAM,MAAM,GAAG,SAAS,uBAAuB;AAAA,YACxD,SAAS,EAAE,eAAe,UAAU,UAAU,YAAY,GAAG;AAAA,UACjE,CAAC;AACD,cAAI,KAAK,GAAI,CAAAA,QAAO,MAAM,KAAK,KAAK;AAAA,QACxC,SAAS,GAAG;AAAA,QAAE;AAAA,MAClB;AAGA,YAAM,WAAW,KAAK,IAAI,IAAI,IAAI,QAAQ;AAC1C,YAAM,iBAAiB;AAAA,QACnB,KAAK,WAAW;AAAA,QAChB,KAAK;AAAA,QACL,MAAAA;AAAA,MACJ;AACA,YAAM,aAAa,KAAK,UAAU,cAAc;AAChD,YAAM,iBAAiB,gBAAgB,UAAU;AACjD,YAAM,YAAY,MAAM,SAAS,gBAAgB,WAAW,mBAAmB;AAC/E,YAAM,eAAe,GAAG,cAAc,IAAI,SAAS;AAEnD,aAAO,IAAI,SAAS,MAAM;AAAA,QACtB,QAAQ;AAAA,QACR,SAAS;AAAA,UACL,YAAY;AAAA,UACZ,cAAc,iBAAiB,YAAY,2DAA2D,UAAU;AAAA,QACpH;AAAA,MACJ,CAAC;AAAA,IACL;AAGA,UAAM,gBAAgB,QAAQ,eAAe;AAC7C,QAAI,OAAyB;AAE7B,QAAI,iBAAiB,cAAc,SAAS,GAAG,GAAG;AAC9C,YAAM,CAAC,gBAAgB,SAAS,IAAI,cAAc,MAAM,GAAG;AAC3D,UAAI,kBAAkB,WAAW;AAC7B,cAAM,UAAU,MAAM,WAAW,gBAAgB,WAAW,WAAW,mBAAmB;AAC1F,YAAI,SAAS;AACT,cAAI;AACA,kBAAM,cAAc,KAAK,MAAM,gBAAgB,cAAc,CAAC;AAE9D,gBACI,OAAO,YAAY,GAAG,IAAI,KAAK,IAAI,KACnC,YAAY,QAAQ,WAAW,iBACjC;AACE,qBAAO,YAAY,QAAQ,EAAE,eAAe,KAAK;AAAA,YACrD;AAAA,UACJ,SAAS,GAAG;AAAA,UAAE;AAAA,QAClB;AAAA,MACJ;AAAA,IACJ;AAGA,QAAI,CAAC,MAAM;AAEP,YAAM,QAAQ,IAAI,SAAS,WAAW,OAAO,KACzC,QAAQ,QAAQ,IAAI,QAAQ,GAAG,SAAS,kBAAkB,KAC1D,QAAQ,QAAQ,IAAI,kBAAkB,MAAM;AAEhD,UAAI,OAAO;AACP,eAAO,IAAI;AAAA,UACP,KAAK,UAAU,EAAE,OAAO,gBAAgB,SAAS,yDAAY,CAAC;AAAA,UAC9D;AAAA,YACI,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,kCAAkC;AAAA,UACjE;AAAA,QACJ;AAAA,MACJ;AAGA,YAAM,YAAY,KAAK,UAAU;AAAA,QAC7B,MAAM,IAAI,WAAW,IAAI;AAAA,QACzB,KAAK,KAAK,IAAI,IAAI,MAAM;AAAA,MAC5B,CAAC;AACD,YAAM,WAAW,gBAAgB,SAAS;AAC1C,YAAM,WAAW,MAAM,SAAS,UAAU,WAAW,mBAAmB;AACxE,YAAM,aAAa,GAAG,QAAQ,IAAI,QAAQ;AAG1C,YAAM,UAAU,IAAI,IAAI,GAAG,SAAS,sBAAsB;AAC1D,cAAQ,aAAa,IAAI,aAAa,WAAW,eAAe;AAChE,cAAQ,aAAa,IAAI,gBAAgB,GAAG,IAAI,MAAM,gBAAgB;AACtE,cAAQ,aAAa,IAAI,iBAAiB,MAAM;AAEhD,cAAQ,aAAa,IAAI,SAAS,WAAW,eAAe,sBAAsB;AAClF,cAAQ,aAAa,IAAI,SAAS,UAAU;AAE5C,aAAO,SAAS,SAAS,QAAQ,SAAS,GAAG,GAAG;AAAA,IACpD;AAGA,UAAM,UAAU;AAChB,YAAQ,OAAO;AACf,WAAO,KAAK,cAAc,SAAS,YAAY,KAAK,IAAI;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACF,SACA,KACA,KACA,MACiB;AACjB,WAAO,IAAI,SAAS,yEAA4B,EAAE,QAAQ,IAAI,CAAC;AAAA,EACnE;AACJ;AAGO,SAAS,cACZ,SACA,UAA8B,CAAC,GAC+B;AAC9D,QAAM,WAAW,IAAK,cAAc,YAAY;AAAA,IACnC,SAAS,KAAU,KAAc;AACtC,aAAO,QAAQ,WAAW,QAAQ,SAAS,KAAK,GAAG,IAAI,MAAM,SAAS,KAAK,GAAG;AAAA,IAClF;AAAA,IACA,MAAe,cAAc,KAA2B,KAAe,KAAU,MAAwB;AACrG,aAAO,QAAQ,KAAK,KAAK,KAAK,IAAI;AAAA,IACtC;AAAA,EACJ,EAAG;AACH,SAAO,CAAC,KAAc,KAAgB,QAAc,SAAS,MAAM,KAAK,KAAK,GAAG;AACpF;AAGO,SAAS,sBACZ,eACA,UAA8B,CAAC,GAC3B;AACJ,QAAM,SAAS,IAAK,cAAc,YAAY;AAAA,IACjC,SAAS,KAAU,KAAc;AACtC,aAAO,QAAQ,WAAW,QAAQ,SAAS,KAAK,GAAG,IAAI,MAAM,SAAS,KAAK,GAAG;AAAA,IAClF;AAAA,IACA,MAAe,cAAc,SAA+B,KAAe,KAAU,MAAwB;AAEzG,YAAM,YAAY;AAAA,QACd;AAAA,QACA,aAAa,MAAM;AAAA,QAAE;AAAA,QACrB,WAAW,CAAC,MAAqB,OAAO,IAAI,YAAY,IAAI,UAAU,CAAC,IAAI;AAAA,QAC3E;AAAA,MACJ;AACA,aAAO,cAAc,WAAW,IAAI;AAAA,IACxC;AAAA,EACJ,EAAG;AAEH,MAAI,OAAO,qBAAqB,aAAa;AACzC,qBAAiB,SAAS,CAAC,UAAe;AACtC,YAAM,YAAY,OAAO,MAAM,MAAM,SAAU,YAAoB,KAAK,CAAC;AAAA,IAC7E,CAAC;AAAA,EACL;AACJ;AAEA,IAAO,qBAAQ;","names":["user"]}
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "prism-authts",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Zero-dependency Cloudflare Worker & Edge middleware for Prism OAuth2/OIDC SSO authentication",
5
+ "type": "module",
6
+ "main": "./dist/prism-auth.cjs",
7
+ "module": "./dist/prism-auth.js",
8
+ "types": "./dist/prism-auth.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/prism-auth.d.ts",
12
+ "import": "./dist/prism-auth.js",
13
+ "require": "./dist/prism-auth.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "keywords": [
25
+ "prism",
26
+ "auth",
27
+ "sso",
28
+ "oauth2",
29
+ "oidc",
30
+ "cloudflare-workers",
31
+ "edge"
32
+ ],
33
+ "author": "",
34
+ "license": "MIT",
35
+ "devDependencies": {
36
+ "tsup": "^8.5.1",
37
+ "typescript": "^5.8.2"
38
+ }
39
+ }