Skip to content

Commit

Permalink
feat(cloudflare-access): Handle Access organization does not exist an…
Browse files Browse the repository at this point in the history
…d Access not available cases (#898)
  • Loading branch information
G4brym authored Dec 23, 2024
1 parent cd6c667 commit b71d817
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-bugs-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hono/cloudflare-access': minor
---

Handle Access organization does not exist and Access not available cases
5 changes: 3 additions & 2 deletions packages/cloudflare-access/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,13 @@ const app = new Hono<{ Variables: myVariables & CloudflareAccessVariables }>()
app.use('*', cloudflareAccess('my-access-team-name'))
app.get('/', (c) => {
const payload = c.get('accessPayload')

return c.text(`You just authenticated with the email ${payload.email}`)
})

export default app
```


## Errors throw by the middleware

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

## Author

Expand Down
53 changes: 46 additions & 7 deletions packages/cloudflare-access/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,30 @@ describe('Cloudflare Access middleware', async () => {
const keyPair2 = await generateJWTKeyPair();
const keyPair3 = await generateJWTKeyPair();

vi.stubGlobal('fetch', async () => {
return Response.json({
keys: [
publicKeyToJWK(keyPair1.publicKey),
publicKeyToJWK(keyPair2.publicKey),
],
beforeEach(() => {
vi.clearAllMocks();
vi.stubGlobal('fetch', async () => {
return Response.json({
keys: [
publicKeyToJWK(keyPair1.publicKey),
publicKeyToJWK(keyPair2.publicKey),
],
})
})
})
});

const app = new Hono()

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

app.onError((err, c) => {
return c.json({
err: err.toString(),
}, 500)
})

it('Should be throw Missing bearer token when nothing is sent', async () => {
const res = await app.request('http://localhost/hello-behind-access')
expect(res).not.toBeNull()
Expand Down Expand Up @@ -248,4 +257,34 @@ describe('Cloudflare Access middleware', async () => {
"exp":expect.any(Number)
})
})

it('Should throw an error, if the access organization does not exist', async () => {
vi.stubGlobal('fetch', async () => {
return Response.json({success: false}, {status: 404})
})

const res = await app.request('http://localhost/hello-behind-access', {
headers: {
'cf-access-jwt-assertion': 'asdads'
}
})
expect(res).not.toBeNull()
expect(res.status).toBe(500)
expect(await res.json()).toEqual({"err":"Error: Authentication error: The Access Organization 'my-cool-team-name' does not exist"})
})

it('Should throw an error, if the access certs url is unavailable', async () => {
vi.stubGlobal('fetch', async () => {
return Response.json({success: false}, {status: 500})
})

const res = await app.request('http://localhost/hello-behind-access', {
headers: {
'cf-access-jwt-assertion': 'asdads'
}
})
expect(res).not.toBeNull()
expect(res.status).toBe(500)
expect(await res.json()).toEqual({"err":"Error: Authentication error: Received unexpected HTTP code 500 from Cloudflare Access"})
})
})
9 changes: 9 additions & 0 deletions packages/cloudflare-access/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createMiddleware } from 'hono/factory'
import { Context } from 'hono'
import { HTTPException } from 'hono/http-exception'

export type CloudflareAccessPayload = {
aud: string[],
Expand Down Expand Up @@ -89,6 +90,14 @@ async function getPublicKeys(accessTeamName: string) {
},
})

if (!result.ok) {
if (result.status === 404) {
throw new HTTPException(500, { message: `Authentication error: The Access Organization '${accessTeamName}' does not exist` })
}

throw new HTTPException(500, { message: `Authentication error: Received unexpected HTTP code ${result.status} from Cloudflare Access` })
}

const data: any = await result.json()

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

0 comments on commit b71d817

Please sign in to comment.