feat(cloudflare-access): Handle Access organization does not exist and Access not available cases (#898)
parent
cd6c667ee2
commit
b71d817f71
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@hono/cloudflare-access': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Handle Access organization does not exist and Access not available cases
|
|
@ -45,7 +45,6 @@ app.get('/', (c) => {
|
||||||
export default app
|
export default app
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Errors throw by the middleware
|
## Errors throw by the middleware
|
||||||
|
|
||||||
| Error | HTTP Code |
|
| Error | HTTP Code |
|
||||||
|
@ -55,6 +54,8 @@ export default app
|
||||||
| Authentication error: Token is expired | 401 |
|
| Authentication error: Token is expired | 401 |
|
||||||
| Authentication error: Expected team name {your-team-name}, but received ${different-team-signed-token} | 401 |
|
| Authentication error: Expected team name {your-team-name}, but received ${different-team-signed-token} | 401 |
|
||||||
| Authentication error: Invalid 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
|
## Author
|
||||||
|
|
||||||
|
|
|
@ -116,14 +116,17 @@ describe('Cloudflare Access middleware', async () => {
|
||||||
const keyPair2 = await generateJWTKeyPair();
|
const keyPair2 = await generateJWTKeyPair();
|
||||||
const keyPair3 = await generateJWTKeyPair();
|
const keyPair3 = await generateJWTKeyPair();
|
||||||
|
|
||||||
vi.stubGlobal('fetch', async () => {
|
beforeEach(() => {
|
||||||
return Response.json({
|
vi.clearAllMocks();
|
||||||
keys: [
|
vi.stubGlobal('fetch', async () => {
|
||||||
publicKeyToJWK(keyPair1.publicKey),
|
return Response.json({
|
||||||
publicKeyToJWK(keyPair2.publicKey),
|
keys: [
|
||||||
],
|
publicKeyToJWK(keyPair1.publicKey),
|
||||||
|
publicKeyToJWK(keyPair2.publicKey),
|
||||||
|
],
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
});
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
|
|
||||||
|
@ -131,6 +134,12 @@ describe('Cloudflare Access middleware', async () => {
|
||||||
app.get('/hello-behind-access', (c) => c.text('foo'))
|
app.get('/hello-behind-access', (c) => c.text('foo'))
|
||||||
app.get('/access-payload', (c) => c.json(c.get('accessPayload')))
|
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 () => {
|
it('Should be throw Missing bearer token when nothing is sent', async () => {
|
||||||
const res = await app.request('http://localhost/hello-behind-access')
|
const res = await app.request('http://localhost/hello-behind-access')
|
||||||
expect(res).not.toBeNull()
|
expect(res).not.toBeNull()
|
||||||
|
@ -248,4 +257,34 @@ describe('Cloudflare Access middleware', async () => {
|
||||||
"exp":expect.any(Number)
|
"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"})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { createMiddleware } from 'hono/factory'
|
import { createMiddleware } from 'hono/factory'
|
||||||
import { Context } from 'hono'
|
import { Context } from 'hono'
|
||||||
|
import { HTTPException } from 'hono/http-exception'
|
||||||
|
|
||||||
export type CloudflareAccessPayload = {
|
export type CloudflareAccessPayload = {
|
||||||
aud: string[],
|
aud: string[],
|
||||||
|
@ -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()
|
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
|
// Because we keep CryptoKey's in memory between requests, we need to make sure they are refreshed once in a while
|
||||||
|
|
Loading…
Reference in New Issue