From 13adbc215228fcdc8f9654474cce49d36b2f2886 Mon Sep 17 00:00:00 2001 From: Johannes Waigel Date: Fri, 6 Sep 2024 22:04:29 +0200 Subject: [PATCH] feat: set the subject as sub context variable (#17) This is required for native support of the @hono-middleware/permify middleware --- packages/jwks/src/middleware.test.ts | 24 ++++++++++++++++++++++++ packages/jwks/src/middleware.ts | 20 +++++++++++++++++++- packages/permify/README.md | 2 ++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/packages/jwks/src/middleware.test.ts b/packages/jwks/src/middleware.test.ts index cc84f90..d9a6f0a 100644 --- a/packages/jwks/src/middleware.test.ts +++ b/packages/jwks/src/middleware.test.ts @@ -8,6 +8,7 @@ type Env = { Variables: { // biome-ignore lint/suspicious/noExplicitAny: jwtPayload: any; + sub?: string; }; }; @@ -55,6 +56,10 @@ describe("JWKS", () => { const app = new Hono(); app.use("/auth/*", jwks({ domain: jwksUrl })); + app.use( + "/auth-sub/*", + jwks({ domain: jwksUrl, subjectToSubContextVariable: "message" }), + ); app.use("/auth-unicode/*", jwks({ domain: jwksUrl })); app.use("/nested/*", async (c, next) => { const auth = jwks({ domain: jwksUrl }); @@ -66,6 +71,11 @@ describe("JWKS", () => { const payload = c.get("jwtPayload"); return c.json(payload); }); + app.get("/auth-sub/*", (c) => { + handlerExecuted = true; + const sub = c.get("sub"); + return c.json({ sub }); + }); app.get("/auth-unicode/*", (c) => { handlerExecuted = true; const payload = c.get("jwtPayload"); @@ -98,6 +108,20 @@ describe("JWKS", () => { expect(handlerExecuted).toBeTruthy(); }); + it("should set the 'sub' context variable", async () => { + const credential = + "eyJhbGciOiJSUzI1NiIsImtpZCI6IjIzZmY2ODNjZDIzNGE5MTdmYTcyNWIifQ.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.RSfeJmhhbv0DONbwml-V0TwHLjKHaaON3-keyjacD1-RlvGiXpK2uerkrtgz-on4qLPJlh6c1qe6VCnatYlGeFQ3QQJIqXM-Q2ZNS0kNHz4oeJWdzvPRTM-gUmMb3rmw2EK7TlBAg2mVRCfqNW9jdwnfbd56JmfwTT7rYCVQKzZbgUNLFfB0lHtA86AUWZmpc-es3l-b1mxYLsdQroGS1cpCUsRe7et2nCmJSu3qJybKvYC4gDd8mmMEii-Fej69Esxl4UWgcEwD2cqViyvpClKtrhcgA5Nf0a624NUBVcS-7nHZNX1TJPTbnx6LQThBx7A7GU1b_XB0ig0wZ8Zpew"; + const req = new Request("http://localhost/auth-sub/a"); + req.headers.set("Authorization", `Bearer ${credential}`); + const res = await app.request(req); + expect(res).not.toBeNull(); + expect(res.status).toBe(200); + expect(await res.json()).toEqual({ + sub: "hello world", + }); + expect(handlerExecuted).toBeTruthy(); + }); + it("Should authorize Unicode", async () => { const credential = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjIzZmY2ODNjZDIzNGE5MTdmYTcyNWIifQ.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.RSfeJmhhbv0DONbwml-V0TwHLjKHaaON3-keyjacD1-RlvGiXpK2uerkrtgz-on4qLPJlh6c1qe6VCnatYlGeFQ3QQJIqXM-Q2ZNS0kNHz4oeJWdzvPRTM-gUmMb3rmw2EK7TlBAg2mVRCfqNW9jdwnfbd56JmfwTT7rYCVQKzZbgUNLFfB0lHtA86AUWZmpc-es3l-b1mxYLsdQroGS1cpCUsRe7et2nCmJSu3qJybKvYC4gDd8mmMEii-Fej69Esxl4UWgcEwD2cqViyvpClKtrhcgA5Nf0a624NUBVcS-7nHZNX1TJPTbnx6LQThBx7A7GU1b_XB0ig0wZ8Zpew"; diff --git a/packages/jwks/src/middleware.ts b/packages/jwks/src/middleware.ts index 5b927c1..6b5ea60 100644 --- a/packages/jwks/src/middleware.ts +++ b/packages/jwks/src/middleware.ts @@ -38,6 +38,13 @@ interface MiddlewareOptions { secret?: string | BufferSource; prefixOptions?: CookiePrefixOptions; }; + /** + * Set the JWT subject claim to a context variable. + * + * ctx.set("sub", payload.claim.subjectToContextVariable); + * @default "sub" - The subject claim is set to the "sub" context variable. + */ + subjectToSubContextVariable?: string; } /** @@ -57,12 +64,20 @@ export type JWTPayload = { * The token is checked to ensure it is not issued in the future. */ iat?: number; + /** + * The subject of the token. This is usually the identifier of the user the token represents. + */ + sub?: string; }; export function createJWKSMiddleware( options: MiddlewareOptions, ): MiddlewareHandler { - const { domain, getJwksOptions } = options; + const { + domain, + getJwksOptions, + subjectToSubContextVariable = "sub", + } = options; const getJwks = buildGetJwks({ ...getJwksOptions, @@ -101,6 +116,9 @@ export function createJWKSMiddleware( } ctx.set("jwtPayload", payload); + if (subjectToSubContextVariable && subjectToSubContextVariable in payload) { + ctx.set("sub", payload[subjectToSubContextVariable]); + } await next(); }); diff --git a/packages/permify/README.md b/packages/permify/README.md index 08642c8..ac717e9 100644 --- a/packages/permify/README.md +++ b/packages/permify/README.md @@ -20,6 +20,8 @@ const { checkPermission } = createCheckPermissionMiddleware({ }); // Set the subject id for permify validation +// If you using the @hono-middlewares/jwks middleware, you can skip this step +// because the middleware will set the subject id automatically app.use("*", createMiddleware(async (c, next) => { c.set("sub", "acct_01j6wwsyzteqqbe76dt28vdfdr"); await next();