Skip to content

Commit b71d817

Browse files
authored
feat(cloudflare-access): Handle Access organization does not exist and Access not available cases (#898)
1 parent cd6c667 commit b71d817

File tree

4 files changed

+63
-9
lines changed

4 files changed

+63
-9
lines changed

.changeset/heavy-bugs-perform.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hono/cloudflare-access': minor
3+
---
4+
5+
Handle Access organization does not exist and Access not available cases

packages/cloudflare-access/README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,13 @@ const app = new Hono<{ Variables: myVariables & CloudflareAccessVariables }>()
3838
app.use('*', cloudflareAccess('my-access-team-name'))
3939
app.get('/', (c) => {
4040
const payload = c.get('accessPayload')
41-
41+
4242
return c.text(`You just authenticated with the email ${payload.email}`)
4343
})
4444

4545
export default app
4646
```
4747

48-
4948
## Errors throw by the middleware
5049

5150
| Error | HTTP Code |
@@ -55,6 +54,8 @@ export default app
5554
| Authentication error: Token is expired | 401 |
5655
| Authentication error: Expected team name {your-team-name}, but received ${different-team-signed-token} | 401 |
5756
| Authentication error: Invalid Token | 401 |
57+
| Authentication error: The Access Organization 'my-team-name' does not exist | 500 |
58+
| Authentication error: Received unexpected HTTP code 500 from Cloudflare Access | 500 |
5859

5960
## Author
6061

packages/cloudflare-access/src/index.test.ts

+46-7
Original file line numberDiff line numberDiff line change
@@ -116,21 +116,30 @@ describe('Cloudflare Access middleware', async () => {
116116
const keyPair2 = await generateJWTKeyPair();
117117
const keyPair3 = await generateJWTKeyPair();
118118

119-
vi.stubGlobal('fetch', async () => {
120-
return Response.json({
121-
keys: [
122-
publicKeyToJWK(keyPair1.publicKey),
123-
publicKeyToJWK(keyPair2.publicKey),
124-
],
119+
beforeEach(() => {
120+
vi.clearAllMocks();
121+
vi.stubGlobal('fetch', async () => {
122+
return Response.json({
123+
keys: [
124+
publicKeyToJWK(keyPair1.publicKey),
125+
publicKeyToJWK(keyPair2.publicKey),
126+
],
127+
})
125128
})
126-
})
129+
});
127130

128131
const app = new Hono()
129132

130133
app.use('/*', cloudflareAccess('my-cool-team-name'))
131134
app.get('/hello-behind-access', (c) => c.text('foo'))
132135
app.get('/access-payload', (c) => c.json(c.get('accessPayload')))
133136

137+
app.onError((err, c) => {
138+
return c.json({
139+
err: err.toString(),
140+
}, 500)
141+
})
142+
134143
it('Should be throw Missing bearer token when nothing is sent', async () => {
135144
const res = await app.request('http://localhost/hello-behind-access')
136145
expect(res).not.toBeNull()
@@ -248,4 +257,34 @@ describe('Cloudflare Access middleware', async () => {
248257
"exp":expect.any(Number)
249258
})
250259
})
260+
261+
it('Should throw an error, if the access organization does not exist', async () => {
262+
vi.stubGlobal('fetch', async () => {
263+
return Response.json({success: false}, {status: 404})
264+
})
265+
266+
const res = await app.request('http://localhost/hello-behind-access', {
267+
headers: {
268+
'cf-access-jwt-assertion': 'asdads'
269+
}
270+
})
271+
expect(res).not.toBeNull()
272+
expect(res.status).toBe(500)
273+
expect(await res.json()).toEqual({"err":"Error: Authentication error: The Access Organization 'my-cool-team-name' does not exist"})
274+
})
275+
276+
it('Should throw an error, if the access certs url is unavailable', async () => {
277+
vi.stubGlobal('fetch', async () => {
278+
return Response.json({success: false}, {status: 500})
279+
})
280+
281+
const res = await app.request('http://localhost/hello-behind-access', {
282+
headers: {
283+
'cf-access-jwt-assertion': 'asdads'
284+
}
285+
})
286+
expect(res).not.toBeNull()
287+
expect(res.status).toBe(500)
288+
expect(await res.json()).toEqual({"err":"Error: Authentication error: Received unexpected HTTP code 500 from Cloudflare Access"})
289+
})
251290
})

packages/cloudflare-access/src/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createMiddleware } from 'hono/factory'
22
import { Context } from 'hono'
3+
import { HTTPException } from 'hono/http-exception'
34

45
export type CloudflareAccessPayload = {
56
aud: string[],
@@ -89,6 +90,14 @@ async function getPublicKeys(accessTeamName: string) {
8990
},
9091
})
9192

93+
if (!result.ok) {
94+
if (result.status === 404) {
95+
throw new HTTPException(500, { message: `Authentication error: The Access Organization '${accessTeamName}' does not exist` })
96+
}
97+
98+
throw new HTTPException(500, { message: `Authentication error: Received unexpected HTTP code ${result.status} from Cloudflare Access` })
99+
}
100+
92101
const data: any = await result.json()
93102

94103
// Because we keep CryptoKey's in memory between requests, we need to make sure they are refreshed once in a while

0 commit comments

Comments
 (0)