diff --git a/.changeset/loud-books-beam.md b/.changeset/loud-books-beam.md new file mode 100644 index 00000000..65da3b55 --- /dev/null +++ b/.changeset/loud-books-beam.md @@ -0,0 +1,5 @@ +--- +'@hono/firebase-auth': patch +--- + +fixed error handling to wrap root errors diff --git a/packages/firebase-auth/package.json b/packages/firebase-auth/package.json index d446dad3..5e369243 100644 --- a/packages/firebase-auth/package.json +++ b/packages/firebase-auth/package.json @@ -45,16 +45,16 @@ "firebase-auth-cloudflare-workers": "^2.0.2" }, "peerDependencies": { - "hono": ">=3.*" + "hono": "*" }, "devDependencies": { "@cloudflare/workers-types": "^4.20240222.0", "firebase-tools": "^13.3.1", - "hono": "3.12.12", + "hono": "^4.2.4", "miniflare": "^3.20240208.0", "prettier": "^3.2.5", "tsup": "^8.0.2", "typescript": "^5.3.3", - "vitest": "^1.3.1" + "vitest": "^1.5.0" } } diff --git a/packages/firebase-auth/src/index.ts b/packages/firebase-auth/src/index.ts index 6a8b5997..b7ed2cef 100644 --- a/packages/firebase-auth/src/index.ts +++ b/packages/firebase-auth/src/index.ts @@ -1,7 +1,7 @@ -import { getCookie } from 'hono/cookie' import type { KeyStorer, FirebaseIdToken } from 'firebase-auth-cloudflare-workers' import { Auth, WorkersKVStoreSingle } from 'firebase-auth-cloudflare-workers' import type { Context, MiddlewareHandler } from 'hono' +import { getCookie } from 'hono/cookie' import { HTTPException } from 'hono/http-exception' export type VerifyFirebaseAuthEnv = { @@ -22,10 +22,10 @@ export interface VerifyFirebaseAuthConfig { const defaultKVStoreJWKCacheKey = 'verify-firebase-auth-cached-public-key' const defaultKeyStoreInitializer = (c: Context<{ Bindings: VerifyFirebaseAuthEnv }>): KeyStorer => { if (c.env.PUBLIC_JWK_CACHE_KV === undefined) { - const res = new Response('Not Implemented', { - status: 501, + const status = 501 + throw new HTTPException(status, { + res: new Response('Not Implemented', { status }), }) - throw new HTTPException(res.status, { res }) } return WorkersKVStoreSingle.getOrInitialize( c.env.PUBLIC_JWK_CACHE_KEY ?? defaultKVStoreJWKCacheKey, @@ -49,10 +49,11 @@ export const verifyFirebaseAuth = (userConfig: VerifyFirebaseAuthConfig): Middle return async (c, next) => { const authorization = c.req.raw.headers.get(config.authorizationHeaderKey) if (authorization === null) { - const res = new Response('Bad Request', { - status: 400, + const status = 400 + throw new HTTPException(status, { + res: new Response('Bad Request', { status }), + message: 'authorization header is empty', }) - throw new HTTPException(res.status, { res, message: 'authorization header is empty' }) } const jwt = authorization.replace(/Bearer\s+/i, '') const auth = Auth.getOrInitialize( @@ -74,12 +75,11 @@ export const verifyFirebaseAuth = (userConfig: VerifyFirebaseAuthConfig): Middle }) } - const res = new Response('Unauthorized', { - status: 401, - }) - throw new HTTPException(res.status, { - res, + const status = 401 + throw new HTTPException(status, { + res: new Response('Unauthorized', { status }), message: `failed to verify the requested firebase token: ${String(err)}`, + cause: err, }) } await next() @@ -131,8 +131,9 @@ export const verifySessionCookieFirebaseAuth = ( ) const session = getCookie(c, config.cookieName) if (session === undefined) { - const res = c.redirect(config.redirects.signIn) - throw new HTTPException(res.status, { res, message: 'session is empty' }) + const status = 302 + const res = c.redirect(config.redirects.signIn, status) + throw new HTTPException(status, { res, message: 'session is empty' }) } try { @@ -142,10 +143,12 @@ export const verifySessionCookieFirebaseAuth = ( }) setFirebaseToken(c, idToken) } catch (err) { - const res = c.redirect(config.redirects.signIn) - throw new HTTPException(res.status, { + const status = 302 + const res = c.redirect(config.redirects.signIn, status) + throw new HTTPException(status, { res, message: `failed to verify the requested firebase token: ${String(err)}`, + cause: err, }) } await next() diff --git a/packages/firebase-auth/test/index.test.ts b/packages/firebase-auth/test/index.test.ts index 954af8b5..79267de7 100644 --- a/packages/firebase-auth/test/index.test.ts +++ b/packages/firebase-auth/test/index.test.ts @@ -1,12 +1,13 @@ import type { Credential, KeyStorer } from 'firebase-auth-cloudflare-workers' import { AdminAuthApiClient, Auth, WorkersKVStoreSingle } from 'firebase-auth-cloudflare-workers' +import type { GoogleOAuthAccessToken } from 'firebase-auth-cloudflare-workers/dist/main/credential' import { Hono } from 'hono' +import { setCookie } from 'hono/cookie' +import { HTTPException } from 'hono/http-exception' import { Miniflare } from 'miniflare' import { describe, it, expect, beforeAll, vi } from 'vitest' import type { VerifyFirebaseAuthEnv } from '../src' import { verifyFirebaseAuth, getFirebaseToken, verifySessionCookieFirebaseAuth } from '../src' -import { GoogleOAuthAccessToken } from 'firebase-auth-cloudflare-workers/dist/main/credential' -import { setCookie } from 'hono/cookie' describe('verifyFirebaseAuth middleware', () => { const emulatorHost = '127.0.0.1:9099' @@ -230,8 +231,13 @@ describe('verifyFirebaseAuth middleware', () => { resetAuth() + let gotError: Error | undefined app.use( '*', + async (c, next) => { + await next() + gotError = c.error + }, verifyFirebaseAuth({ projectId: validProjectId, disableErrorLog: true, @@ -251,6 +257,7 @@ describe('verifyFirebaseAuth middleware', () => { } const res = await app.fetch(req, env) + expect(gotError instanceof HTTPException).toBeTruthy() expect(res).not.toBeNull() expect(res.status).toBe(501) }) @@ -421,8 +428,13 @@ describe('verifySessionCookieFirebaseAuth middleware', () => { PUBLIC_JWK_CACHE_KV, } + let gotError: Error | undefined app.use( '*', + async (c, next) => { + await next() + gotError = c.error + }, verifySessionCookieFirebaseAuth({ ...config, redirects: { @@ -445,6 +457,7 @@ describe('verifySessionCookieFirebaseAuth middleware', () => { expect(res).not.toBeNull() expect(res.status).toBe(wantStatus) if (wantStatus === 302) { + expect(gotError instanceof HTTPException).toBeTruthy() expect(res.headers.get('location')).toBe(signInPath) } }) @@ -537,8 +550,13 @@ describe('verifySessionCookieFirebaseAuth middleware', () => { resetAuth() + let gotError: Error | undefined app.use( '*', + async (c, next) => { + await next() + gotError = c.error + }, verifySessionCookieFirebaseAuth({ projectId: validProjectId, redirects: { @@ -561,6 +579,7 @@ describe('verifySessionCookieFirebaseAuth middleware', () => { PUBLIC_JWK_CACHE_KV: undefined, }) + expect(gotError instanceof HTTPException).toBeTruthy() expect(res).not.toBeNull() expect(res.status).toBe(501) }) diff --git a/yarn.lock b/yarn.lock index d6dc0062..afd74818 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1872,14 +1872,14 @@ __metadata: "@cloudflare/workers-types": "npm:^4.20240222.0" firebase-auth-cloudflare-workers: "npm:^2.0.2" firebase-tools: "npm:^13.3.1" - hono: "npm:3.12.12" + hono: "npm:^4.2.4" miniflare: "npm:^3.20240208.0" prettier: "npm:^3.2.5" tsup: "npm:^8.0.2" typescript: "npm:^5.3.3" - vitest: "npm:^1.3.1" + vitest: "npm:^1.5.0" peerDependencies: - hono: ">=3.*" + hono: "*" languageName: unknown linkType: soft @@ -4510,17 +4510,6 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/expect@npm:1.3.1" - dependencies: - "@vitest/spy": "npm:1.3.1" - "@vitest/utils": "npm:1.3.1" - chai: "npm:^4.3.10" - checksum: ea66a1e912d896a481a27631b68089b885af7e8ed62ba8aaa119c37a9beafe6c094fd672775a20e6e23460af66e294f9ca259e6e0562708d1b7724eaaf53c7bb - languageName: node - linkType: hard - "@vitest/expect@npm:1.4.0": version: 1.4.0 resolution: "@vitest/expect@npm:1.4.0" @@ -4532,6 +4521,17 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/expect@npm:1.5.0" + dependencies: + "@vitest/spy": "npm:1.5.0" + "@vitest/utils": "npm:1.5.0" + chai: "npm:^4.3.10" + checksum: 12138caf0831a9bcdf475750fdff27588f03f11ee5101124f9d39951bbcdec39aa6a19a1a30f3ab0ca17374ddfc7399b861e2fcb1548bba302943e8e88512a9c + languageName: node + linkType: hard + "@vitest/runner@npm:0.34.6": version: 0.34.6 resolution: "@vitest/runner@npm:0.34.6" @@ -4565,17 +4565,6 @@ __metadata: languageName: node linkType: hard -"@vitest/runner@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/runner@npm:1.3.1" - dependencies: - "@vitest/utils": "npm:1.3.1" - p-limit: "npm:^5.0.0" - pathe: "npm:^1.1.1" - checksum: d732de2368d2bc32cbc27f0bbc5477f6e36088ddfb873c036935a45b1b252ebc529b932cf5cd944eed9b692243acebef828f6d3218583cb8a6817a8270712050 - languageName: node - linkType: hard - "@vitest/runner@npm:1.4.0": version: 1.4.0 resolution: "@vitest/runner@npm:1.4.0" @@ -4587,6 +4576,17 @@ __metadata: languageName: node linkType: hard +"@vitest/runner@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/runner@npm:1.5.0" + dependencies: + "@vitest/utils": "npm:1.5.0" + p-limit: "npm:^5.0.0" + pathe: "npm:^1.1.1" + checksum: a7b693e4121b6159a77f6cbffc03c228cd18c3734e27a968fe26579777f6d7a4d55a02fba396d564df996a91881ee76de80ee5a526d27a4b508f44a9af62d95a + languageName: node + linkType: hard + "@vitest/snapshot@npm:0.34.6": version: 0.34.6 resolution: "@vitest/snapshot@npm:0.34.6" @@ -4620,17 +4620,6 @@ __metadata: languageName: node linkType: hard -"@vitest/snapshot@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/snapshot@npm:1.3.1" - dependencies: - magic-string: "npm:^0.30.5" - pathe: "npm:^1.1.1" - pretty-format: "npm:^29.7.0" - checksum: cad0844270852c6d53c1ca6b7ca279034880d2140837ff245d5bd2376f4356cc924929c58dc69bcf9fad83ba934d4a06000c908971cc24b5d7a9ec2656b72d29 - languageName: node - linkType: hard - "@vitest/snapshot@npm:1.4.0": version: 1.4.0 resolution: "@vitest/snapshot@npm:1.4.0" @@ -4642,6 +4631,17 @@ __metadata: languageName: node linkType: hard +"@vitest/snapshot@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/snapshot@npm:1.5.0" + dependencies: + magic-string: "npm:^0.30.5" + pathe: "npm:^1.1.1" + pretty-format: "npm:^29.7.0" + checksum: d897070b3a7c008eb58d44ca0dc324c69dfc1f787335e59fb930ae2e8d02a148e6b3c13497a588083a0654bdc430281fdd84276ead4888230314a2984dd9f93d + languageName: node + linkType: hard + "@vitest/spy@npm:0.34.6": version: 0.34.6 resolution: "@vitest/spy@npm:0.34.6" @@ -4669,15 +4669,6 @@ __metadata: languageName: node linkType: hard -"@vitest/spy@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/spy@npm:1.3.1" - dependencies: - tinyspy: "npm:^2.2.0" - checksum: efc42f679d2a51fc6583ca3136ccd47581cb27c923ed3cb0500f5dee9aac99b681bfdd400c16ef108f2e0761daa642bc190816a6411931a2aba99ebf8b213dd4 - languageName: node - linkType: hard - "@vitest/spy@npm:1.4.0": version: 1.4.0 resolution: "@vitest/spy@npm:1.4.0" @@ -4687,6 +4678,15 @@ __metadata: languageName: node linkType: hard +"@vitest/spy@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/spy@npm:1.5.0" + dependencies: + tinyspy: "npm:^2.2.0" + checksum: 0206f1e8431f543474dc6d252b553227d0f286b27226b987b63babb18865e8320e6c3d822c67782aae68555728a57ecdeb7c4e16283dbad49791608a691f26d1 + languageName: node + linkType: hard + "@vitest/utils@npm:0.34.6": version: 0.34.6 resolution: "@vitest/utils@npm:0.34.6" @@ -4721,18 +4721,6 @@ __metadata: languageName: node linkType: hard -"@vitest/utils@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/utils@npm:1.3.1" - dependencies: - diff-sequences: "npm:^29.6.3" - estree-walker: "npm:^3.0.3" - loupe: "npm:^2.3.7" - pretty-format: "npm:^29.7.0" - checksum: d604c8ad3b1aee30d4dcd889098f591407bfe18547ff96485b1d1ed54eff58219c756a9544a7fbd4e37886863abacd7a89a76334cb3ea7f84c3d496bb757db23 - languageName: node - linkType: hard - "@vitest/utils@npm:1.4.0": version: 1.4.0 resolution: "@vitest/utils@npm:1.4.0" @@ -4745,6 +4733,18 @@ __metadata: languageName: node linkType: hard +"@vitest/utils@npm:1.5.0": + version: 1.5.0 + resolution: "@vitest/utils@npm:1.5.0" + dependencies: + diff-sequences: "npm:^29.6.3" + estree-walker: "npm:^3.0.3" + loupe: "npm:^2.3.7" + pretty-format: "npm:^29.7.0" + checksum: b9d779ea5c1a5759df4f59e3ba3f3a94816d6c600afe7a2d14b963ea114ce1acedbfe678cbfacb0a20d33cabcce890a08b2ce3fd52e3465f2e0969cc39f7686b + languageName: node + linkType: hard + "@yarnpkg/lockfile@npm:^1.1.0": version: 1.1.0 resolution: "@yarnpkg/lockfile@npm:1.1.0" @@ -9612,13 +9612,6 @@ __metadata: languageName: unknown linkType: soft -"hono@npm:3.12.12": - version: 3.12.12 - resolution: "hono@npm:3.12.12" - checksum: 93b77d51c24f1b60d61dbd0790b0a3a7481a762dbf25cc2d2598938b97ffd4f8f0c26f51964060099c0d358f1aea409877b71623b1744b3c049922b6db84f4da - languageName: node - linkType: hard - "hono@npm:^3.11.7": version: 3.11.7 resolution: "hono@npm:3.11.7" @@ -9668,6 +9661,13 @@ __metadata: languageName: node linkType: hard +"hono@npm:^4.2.4": + version: 4.2.4 + resolution: "hono@npm:4.2.4" + checksum: f6e7d21ebd152beb6e91d5d36d0bef66f9126873dc2b652745ab1fb2429ba8dff09c1b07982d0e29c3ca2b6ff2038fd947cb948e8bf27a7f2a9f429a1728c8fe + languageName: node + linkType: hard + "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -17160,6 +17160,13 @@ __metadata: languageName: node linkType: hard +"tinypool@npm:^0.8.3": + version: 0.8.4 + resolution: "tinypool@npm:0.8.4" + checksum: 779c790adcb0316a45359652f4b025958c1dff5a82460fe49f553c864309b12ad732c8288be52f852973bc76317f5e7b3598878aee0beb8a33322c0e72c4a66c + languageName: node + linkType: hard + "tinyspy@npm:^2.1.1, tinyspy@npm:^2.2.0": version: 2.2.0 resolution: "tinyspy@npm:2.2.0" @@ -18333,21 +18340,6 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:1.3.1": - version: 1.3.1 - resolution: "vite-node@npm:1.3.1" - dependencies: - cac: "npm:^6.7.14" - debug: "npm:^4.3.4" - pathe: "npm:^1.1.1" - picocolors: "npm:^1.0.0" - vite: "npm:^5.0.0" - bin: - vite-node: vite-node.mjs - checksum: b50665ef224f3527f856ab88a0cfabab36dd6e2dd1e3edca8f8f25d5d33754e1050495472c2c82147d0dcf7c5280971dae2f37a531c10f3941d8d3344e34ce0b - languageName: node - linkType: hard - "vite-node@npm:1.4.0": version: 1.4.0 resolution: "vite-node@npm:1.4.0" @@ -18363,6 +18355,21 @@ __metadata: languageName: node linkType: hard +"vite-node@npm:1.5.0": + version: 1.5.0 + resolution: "vite-node@npm:1.5.0" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.3.4" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + vite: "npm:^5.0.0" + bin: + vite-node: vite-node.mjs + checksum: 85f9e8616f3612c71193a2bbdfb00f610685108a65eaa7ac535655ca1bd65dda5436ee577465436f715966f34e4b25b54140bbd584fe2c95ea0726acf4ecd351 + languageName: node + linkType: hard + "vite@npm:^3.0.0 || ^4.0.0 || ^5.0.0-0, vite@npm:^3.1.0 || ^4.0.0 || ^5.0.0-0, vite@npm:^5.0.0": version: 5.0.10 resolution: "vite@npm:5.0.10" @@ -18604,56 +18611,6 @@ __metadata: languageName: node linkType: hard -"vitest@npm:^1.3.1": - version: 1.3.1 - resolution: "vitest@npm:1.3.1" - dependencies: - "@vitest/expect": "npm:1.3.1" - "@vitest/runner": "npm:1.3.1" - "@vitest/snapshot": "npm:1.3.1" - "@vitest/spy": "npm:1.3.1" - "@vitest/utils": "npm:1.3.1" - acorn-walk: "npm:^8.3.2" - chai: "npm:^4.3.10" - debug: "npm:^4.3.4" - execa: "npm:^8.0.1" - local-pkg: "npm:^0.5.0" - magic-string: "npm:^0.30.5" - pathe: "npm:^1.1.1" - picocolors: "npm:^1.0.0" - std-env: "npm:^3.5.0" - strip-literal: "npm:^2.0.0" - tinybench: "npm:^2.5.1" - tinypool: "npm:^0.8.2" - vite: "npm:^5.0.0" - vite-node: "npm:1.3.1" - why-is-node-running: "npm:^2.2.2" - peerDependencies: - "@edge-runtime/vm": "*" - "@types/node": ^18.0.0 || >=20.0.0 - "@vitest/browser": 1.3.1 - "@vitest/ui": 1.3.1 - happy-dom: "*" - jsdom: "*" - peerDependenciesMeta: - "@edge-runtime/vm": - optional: true - "@types/node": - optional: true - "@vitest/browser": - optional: true - "@vitest/ui": - optional: true - happy-dom: - optional: true - jsdom: - optional: true - bin: - vitest: vitest.mjs - checksum: 66d312a3dc12e67bba22d31332d939e89cd17d38531893c7b13b8826704564031c1dde795df2799b855660572c19a595301e920710c7775d072ee6332502efc5 - languageName: node - linkType: hard - "vitest@npm:^1.4.0": version: 1.4.0 resolution: "vitest@npm:1.4.0" @@ -18704,6 +18661,56 @@ __metadata: languageName: node linkType: hard +"vitest@npm:^1.5.0": + version: 1.5.0 + resolution: "vitest@npm:1.5.0" + dependencies: + "@vitest/expect": "npm:1.5.0" + "@vitest/runner": "npm:1.5.0" + "@vitest/snapshot": "npm:1.5.0" + "@vitest/spy": "npm:1.5.0" + "@vitest/utils": "npm:1.5.0" + acorn-walk: "npm:^8.3.2" + chai: "npm:^4.3.10" + debug: "npm:^4.3.4" + execa: "npm:^8.0.1" + local-pkg: "npm:^0.5.0" + magic-string: "npm:^0.30.5" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + std-env: "npm:^3.5.0" + strip-literal: "npm:^2.0.0" + tinybench: "npm:^2.5.1" + tinypool: "npm:^0.8.3" + vite: "npm:^5.0.0" + vite-node: "npm:1.5.0" + why-is-node-running: "npm:^2.2.2" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/node": ^18.0.0 || >=20.0.0 + "@vitest/browser": 1.5.0 + "@vitest/ui": 1.5.0 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 87f6666cccd52678a0e1dbf32e7492e4003580a168114221e4c2d404d53097d886a8be4c5c379e347d0e749affa0ea5416e4e3a0356aeca228a77ee54891ca3c + languageName: node + linkType: hard + "walker@npm:^1.0.8": version: 1.0.8 resolution: "walker@npm:1.0.8"