From 45f898770cbb30380309817d688beb55945a74c7 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 14 Feb 2024 00:05:56 -0500 Subject: [PATCH] fix signing tokens --- sdk/js/src/auth/adapter/adapter.ts | 10 +- sdk/js/src/auth/adapter/code.ts | 4 +- sdk/js/src/auth/adapter/link.ts | 4 +- sdk/js/src/auth/handler.ts | 148 ++++++++++++++--------------- 4 files changed, 86 insertions(+), 80 deletions(-) diff --git a/sdk/js/src/auth/adapter/adapter.ts b/sdk/js/src/auth/adapter/adapter.ts index f239abcd3..8dad4f718 100644 --- a/sdk/js/src/auth/adapter/adapter.ts +++ b/sdk/js/src/auth/adapter/adapter.ts @@ -10,8 +10,14 @@ export type AdapterRoute = Hono; export interface AdapterOptions { name: string; algorithm: string; - publicKey: Promise; - privateKey: Promise; + encryption: { + publicKey: Promise; + privateKey: Promise; + }; + signing: { + publicKey: Promise; + privateKey: Promise; + }; success: (ctx: Context, properties: Properties) => Promise; forward: (ctx: Context, response: Response) => Response; cookie: (ctx: Context, key: string, value: string, maxAge: number) => void; diff --git a/sdk/js/src/auth/adapter/code.ts b/sdk/js/src/auth/adapter/code.ts index 2cb0d4c99..a95e20d8c 100644 --- a/sdk/js/src/auth/adapter/code.ts +++ b/sdk/js/src/auth/adapter/code.ts @@ -40,7 +40,7 @@ export function CodeAdapter(config: { ), ) .setProtectedHeader({ alg: "RSA-OAEP-512", enc: "A256GCM" }) - .encrypt(await ctx.publicKey); + .encrypt(await ctx.encryption.publicKey); ctx.cookie(c, "authorization", authorization, 60 * 10); return ctx.forward(c, await config.onCodeRequest(code, claims as any)); }); @@ -51,7 +51,7 @@ export function CodeAdapter(config: { const { code, claims } = JSON.parse( new TextDecoder().decode( await jose - .compactDecrypt(authorization!, await ctx.privateKey) + .compactDecrypt(authorization!, await ctx.encryption.privateKey) .then((value) => value.plaintext), ), ); diff --git a/sdk/js/src/auth/adapter/link.ts b/sdk/js/src/auth/adapter/link.ts index 3d2f8e52e..14f1e36fd 100644 --- a/sdk/js/src/auth/adapter/link.ts +++ b/sdk/js/src/auth/adapter/link.ts @@ -9,7 +9,7 @@ export function LinkAdapter(config: { const token = await new jose.SignJWT(c.req.query()) .setProtectedHeader({ alg: ctx.algorithm }) .setExpirationTime("10m") - .sign(await ctx.privateKey); + .sign(await ctx.signing.privateKey); const url = new URL(new URL(c.req.url).origin); url.pathname = `/${ctx.name}/callback`; @@ -27,7 +27,7 @@ export function LinkAdapter(config: { routes.get("/callback", async (c) => { const token = c.req.query("token"); if (!token) throw new Error("Missing token parameter"); - const verified = await jose.jwtVerify(token, await ctx.publicKey); + const verified = await jose.jwtVerify(token, await ctx.signing.publicKey); const resp = await ctx.success(c, { claims: verified.payload as any }); return resp; }); diff --git a/sdk/js/src/auth/handler.ts b/sdk/js/src/auth/handler.ts index 9a8d40873..fe34777e7 100644 --- a/sdk/js/src/auth/handler.ts +++ b/sdk/js/src/auth/handler.ts @@ -89,10 +89,6 @@ export function AuthHandler< }; }) { const auth = Resource[process.env.AUTH_ID!]; - const privateKey = jose.importPKCS8(auth.privateKey, "RSA-OAEP-512"); - const publicKey = jose.importSPKI(auth.publicKey, "RSA-OAEP-512", { - extractable: true, - }); const app = new Hono(); if (!input.callbacks.auth.error) { @@ -106,72 +102,15 @@ export function AuthHandler< }; } - app.get("/token", async (c) => { - const form = await c.req.formData(); - if (form.get("grant_type") !== "authorization_code") { - c.status(400); - return c.text("Invalid grant_type"); - } - const code = form.get("code"); - if (!code) { - c.status(400); - return c.text("Missing code"); - } - - const { payload } = await jose.jwtVerify(code as string, await privateKey); - if (payload.redirect_uri !== form.get("redirect_uri")) { - c.status(400); - return c.text("redirect_uri mismatch"); - } - if (payload.client_id !== form.get("client_id")) { - c.status(400); - return c.text("client_id mismatch"); - } - - return c.json({ - access_token: payload.token, - }); - }); - - app.use("/:provider/authorize", async (c, next) => { - const provider = c.req.param("provider"); - const response_type = - c.req.query("response_type") || getCookie(c, "response_type"); - const redirect_uri = - c.req.query("redirect_uri") || getCookie(c, "redirect_uri"); - const state = c.req.query("state") || getCookie(c, "state"); - - if (!provider) { - c.status(400); - return c.text("Missing provider"); - } - - if (!redirect_uri) { - c.status(400); - return c.text("Missing redirect_uri"); - } - - if (!response_type) { - c.status(400); - return c.text("Missing response_type"); - } - options.cookie(c, "provider", provider, 60 * 10); - options.cookie(c, "response_type", response_type, 60 * 10); - options.cookie(c, "redirect_uri", redirect_uri, 60 * 10); - options.cookie(c, "state", state || "", 60 * 10); - - if (input.callbacks.auth.start) { - await input.callbacks.auth.start(c.req.raw); - } - await next(); - }); - - const options: { - algorithm: AdapterOptions["algorithm"]; - success: AdapterOptions["success"]; - forward: AdapterOptions["forward"]; - cookie: AdapterOptions["cookie"]; - } = { + const options: Omit, "name"> = { + signing: { + privateKey: jose.importPKCS8(auth.privateKey, "RS512"), + publicKey: jose.importSPKI(auth.publicKey, "RS512"), + }, + encryption: { + privateKey: jose.importPKCS8(auth.privateKey, "RSA-OAEP-512"), + publicKey: jose.importSPKI(auth.publicKey, "RSA-OAEP-512"), + }, algorithm: "RS512", async success(ctx: Context, properties: any) { const redirect_uri = getCookie(ctx, "redirect_uri"); @@ -188,7 +127,7 @@ export function AuthHandler< const token = await new jose.SignJWT(session) .setProtectedHeader({ alg: "RS512" }) .setExpirationTime("1yr") - .sign(await privateKey); + .sign(await options.signing.privateKey); deleteCookie(ctx, "provider"); deleteCookie(ctx, "response_type"); @@ -215,7 +154,7 @@ export function AuthHandler< }) .setProtectedHeader({ alg: "RS512" }) .setExpirationTime("30s") - .sign(await privateKey); + .sign(await options.signing.privateKey); const location = new URL(redirect_uri); location.searchParams.set("code", code); location.searchParams.set("state", state || ""); @@ -247,12 +186,73 @@ export function AuthHandler< }, }; + app.get("/token", async (c) => { + const form = await c.req.formData(); + if (form.get("grant_type") !== "authorization_code") { + c.status(400); + return c.text("Invalid grant_type"); + } + const code = form.get("code"); + if (!code) { + c.status(400); + return c.text("Missing code"); + } + + const { payload } = await jose.jwtVerify( + code as string, + await options.signing.publicKey, + ); + if (payload.redirect_uri !== form.get("redirect_uri")) { + c.status(400); + return c.text("redirect_uri mismatch"); + } + if (payload.client_id !== form.get("client_id")) { + c.status(400); + return c.text("client_id mismatch"); + } + + return c.json({ + access_token: payload.token, + }); + }); + + app.use("/:provider/authorize", async (c, next) => { + const provider = c.req.param("provider"); + const response_type = + c.req.query("response_type") || getCookie(c, "response_type"); + const redirect_uri = + c.req.query("redirect_uri") || getCookie(c, "redirect_uri"); + const state = c.req.query("state") || getCookie(c, "state"); + + if (!provider) { + c.status(400); + return c.text("Missing provider"); + } + + if (!redirect_uri) { + c.status(400); + return c.text("Missing redirect_uri"); + } + + if (!response_type) { + c.status(400); + return c.text("Missing response_type"); + } + options.cookie(c, "provider", provider, 60 * 10); + options.cookie(c, "response_type", response_type, 60 * 10); + options.cookie(c, "redirect_uri", redirect_uri, 60 * 10); + options.cookie(c, "state", state || "", 60 * 10); + + if (input.callbacks.auth.start) { + await input.callbacks.auth.start(c.req.raw); + } + await next(); + }); + for (const [name, value] of Object.entries(input.providers)) { const route = new Hono(); value(route, { name, - publicKey, - privateKey, ...options, }); app.route(`/${name}`, route);