From ec53c5ec17c04463c30bcdaf7ad6c7283e8b87d8 Mon Sep 17 00:00:00 2001 From: wood chen Date: Sat, 8 Feb 2025 01:29:27 +0800 Subject: [PATCH] Migrate Discourse SSO authentication to use Deno's native crypto and encoding modules --- deno.json | 16 ++++++++++++++++ main.ts | 50 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 deno.json diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..ed9dd12 --- /dev/null +++ b/deno.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "allowJs": true, + "lib": ["deno.window"], + "strict": true + }, + "importMap": { + "imports": { + "std/": "https://deno.land/std@0.220.1/" + } + }, + "tasks": { + "start": "deno run --allow-net --allow-env --allow-read main.ts", + "dev": "deno run --watch --allow-net --allow-env --allow-read main.ts" + } +} \ No newline at end of file diff --git a/main.ts b/main.ts index 5b059b6..a761441 100644 --- a/main.ts +++ b/main.ts @@ -1,4 +1,6 @@ -import { serve } from "https://deno.land/std@0.220.1/http/server.ts"; +import { serve } from "std/http/server.ts"; +import { crypto } from "std/crypto/mod.ts"; +import { encode as base64Encode, decode as base64Decode } from "std/encoding/base64.ts"; import { createHmac } from "crypto"; // 类型定义 @@ -113,12 +115,24 @@ async function verifyDiscourseSSO(request: Request): Promise { return session.value.username; } -// 添加登录和登出函数 -function generateSSO(returnUrl: string): string { - const payload = Buffer.from(`return_sso_url=${encodeURIComponent(returnUrl)}`).toString('base64'); - const sig = createHmac('sha256', DISCOURSE_SSO_SECRET) - .update(payload) - .digest('hex'); +// 修改 generateSSO 函数为异步函数 +async function generateSSO(returnUrl: string): Promise { + const payload = base64Encode(`return_sso_url=${encodeURIComponent(returnUrl)}`); + const key = await crypto.subtle.importKey( + "raw", + new TextEncoder().encode(DISCOURSE_SSO_SECRET), + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"] + ); + const signature = await crypto.subtle.sign( + "HMAC", + key, + new TextEncoder().encode(payload) + ); + const sig = Array.from(new Uint8Array(signature)) + .map(b => b.toString(16).padStart(2, '0')) + .join(''); return `${DISCOURSE_URL}/session/sso_provider?sso=${encodeURIComponent(payload)}&sig=${sig}`; } @@ -605,7 +619,7 @@ async function handler(req: Request): Promise { }); } - const ssoUrl = generateSSO(returnUrl); + const ssoUrl = await generateSSO(returnUrl); return new Response(null, { status: 302, headers: { @@ -633,16 +647,28 @@ async function handler(req: Request): Promise { try { // 验证签名 - const expectedSig = createHmac('sha256', DISCOURSE_SSO_SECRET) - .update(sso) - .digest('hex'); + const key = await crypto.subtle.importKey( + "raw", + new TextEncoder().encode(DISCOURSE_SSO_SECRET), + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"] + ); + const signature = await crypto.subtle.sign( + "HMAC", + key, + new TextEncoder().encode(sso) + ); + const expectedSig = Array.from(new Uint8Array(signature)) + .map(b => b.toString(16).padStart(2, '0')) + .join(''); if (sig !== expectedSig) { throw new Error('Invalid signature'); } // 解码 payload - const payload = Buffer.from(sso, 'base64').toString(); + const payload = new TextDecoder().decode(base64Decode(sso)); const payloadParams = new URLSearchParams(payload); const username = payloadParams.get('username');