fix(firebase-auth): error handling to wrap root errors (#456)

pull/461/head
Kei Kamikawa 2024-04-18 07:38:42 +09:00 committed by GitHub
parent dde9854e51
commit 2d7af647ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 184 additions and 150 deletions

View File

@ -0,0 +1,5 @@
---
'@hono/firebase-auth': patch
---
fixed error handling to wrap root errors

View File

@ -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"
}
}

View File

@ -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()

View File

@ -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)
})

265
yarn.lock
View File

@ -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"