bezzie 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +202 -0
- package/dist/adapters/cloudflare-kv.d.ts +10 -0
- package/dist/adapters/cloudflare-kv.d.ts.map +1 -0
- package/dist/adapters/cloudflare-kv.js +22 -0
- package/dist/adapters/cloudflare-kv.js.map +1 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +5 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/memory.d.ts +9 -0
- package/dist/adapters/memory.d.ts.map +1 -0
- package/dist/adapters/memory.js +23 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/redis.d.ts +17 -0
- package/dist/adapters/redis.d.ts.map +1 -0
- package/dist/adapters/redis.js +19 -0
- package/dist/adapters/redis.js.map +1 -0
- package/dist/adapters/types.d.ts +41 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +22 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +81 -0
- package/dist/middleware.js.map +1 -0
- package/dist/routes.d.ts +5 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +131 -0
- package/dist/routes.js.map +1 -0
- package/dist/session.d.ts +33 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +2 -0
- package/dist/session.js.map +1 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 neilpmas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Bezzie
|
|
2
|
+
|
|
3
|
+
**Bezzie** — your BFF's BFF. Handles the Backend for Frontend OAuth pattern so you don't have to.
|
|
4
|
+
|
|
5
|
+
A BFF (Backend for Frontend) OAuth 2.0 auth library for Cloudflare Workers.
|
|
6
|
+
|
|
7
|
+
Implements the [OAuth 2.0 for Browser-Based Apps (BCP212)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps) pattern — JWTs never touch the browser. The BFF owns the OAuth flow and issues a session cookie to the frontend instead.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install bezzie
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why
|
|
16
|
+
|
|
17
|
+
Most OAuth libraries hand tokens directly to the browser. BCP212 says you shouldn't — it's a significant attack surface. Bezzie keeps tokens server-side in Cloudflare KV and gives the browser a session cookie instead.
|
|
18
|
+
|
|
19
|
+
There's no open source library for this specific combination (BFF OAuth on Cloudflare Workers). The closest alternatives are Duende BFF (.NET) and `@auth0/nextjs-auth0` — both tied to specific frameworks.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { createBezzie, providers, cloudflareKV } from 'bezzie'
|
|
27
|
+
|
|
28
|
+
const auth = createBezzie({
|
|
29
|
+
...providers.auth0('your-tenant.auth0.com'),
|
|
30
|
+
clientId: 'xxx',
|
|
31
|
+
clientSecret: env.AUTH0_CLIENT_SECRET,
|
|
32
|
+
audience: 'https://api.yourproject.com',
|
|
33
|
+
adapter: cloudflareKV(env.SESSION_KV),
|
|
34
|
+
baseUrl: 'https://app.yourproject.com',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Mount auth routes
|
|
38
|
+
app.route('/auth', auth.routes())
|
|
39
|
+
|
|
40
|
+
// Protect API routes
|
|
41
|
+
app.use('/api/*', auth.middleware())
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This gives you:
|
|
45
|
+
|
|
46
|
+
| Route | Description |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `GET /auth/login` | Redirects to provider, initiates Authorization Code + PKCE flow. Supports `returnTo` query param for post-login redirect. |
|
|
49
|
+
| `GET /auth/callback` | Exchanges code for tokens, stores session in KV, sets cookie. |
|
|
50
|
+
| `GET /auth/logout` | Clears session, clears cookie, redirects to provider logout. |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Accessing User Identity
|
|
55
|
+
|
|
56
|
+
After `auth.middleware()`, downstream handlers can access the user identity and the current access token via `c.var`:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
app.get('/api/me', (c) => {
|
|
60
|
+
const user = c.var.user
|
|
61
|
+
const token = c.var.accessToken
|
|
62
|
+
return c.json({ user })
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Forwarding Upstream
|
|
67
|
+
|
|
68
|
+
The `accessToken` on the context is intended for the app to forward to an upstream service (e.g., a Spring Boot API or any other microservice), since Bezzie doesn't mutate request headers directly.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
app.all('/api/proxy/*', async (c) => {
|
|
72
|
+
const url = new URL(c.req.url)
|
|
73
|
+
const target = `https://api.upstream.com${url.pathname}${url.search}`
|
|
74
|
+
|
|
75
|
+
return fetch(target, {
|
|
76
|
+
method: c.req.method,
|
|
77
|
+
headers: {
|
|
78
|
+
...c.req.header(),
|
|
79
|
+
'Authorization': `Bearer ${c.var.accessToken}`
|
|
80
|
+
},
|
|
81
|
+
body: c.req.raw.body
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## How It Works
|
|
89
|
+
|
|
90
|
+
### Login Flow
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Browser → BFF /auth/login → Auth0 (Authorization Code + PKCE)
|
|
94
|
+
│
|
|
95
|
+
code returned
|
|
96
|
+
│
|
|
97
|
+
BFF exchanges code → tokens stored in KV
|
|
98
|
+
BFF issues HttpOnly session cookie → Browser
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Per-Request Flow
|
|
102
|
+
|
|
103
|
+
1. Browser sends request to BFF with session cookie
|
|
104
|
+
2. BFF looks up session in KV, retrieves access token
|
|
105
|
+
3. BFF validates JWT (via JWKS, using Web Crypto API)
|
|
106
|
+
4. If expired, BFF uses refresh token to get a new one and updates KV
|
|
107
|
+
5. BFF forwards request upstream with `Authorization: Bearer <token>`
|
|
108
|
+
|
|
109
|
+
### Session Storage
|
|
110
|
+
|
|
111
|
+
Sessions are stored in Cloudflare KV:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
sessionId → { accessToken, refreshToken, expiresAt, user }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
KV TTL is aligned with the refresh token lifetime. When the refresh token expires, the user must log in again.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Adapters
|
|
122
|
+
|
|
123
|
+
Bezzie supports multiple session storage backends:
|
|
124
|
+
|
|
125
|
+
### Cloudflare KV
|
|
126
|
+
Recommended for production on Cloudflare Workers.
|
|
127
|
+
```typescript
|
|
128
|
+
import { cloudflareKV } from 'bezzie'
|
|
129
|
+
// ...
|
|
130
|
+
adapter: cloudflareKV(env.SESSION_KV)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Redis (Upstash)
|
|
134
|
+
Good for cross-region session consistency.
|
|
135
|
+
```typescript
|
|
136
|
+
import { RedisAdapter } from 'bezzie'
|
|
137
|
+
// ...
|
|
138
|
+
adapter: new RedisAdapter({ url: env.REDIS_URL, token: env.REDIS_TOKEN })
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Memory
|
|
142
|
+
Useful for local development and testing. Do not use in production.
|
|
143
|
+
```typescript
|
|
144
|
+
import { MemoryAdapter } from 'bezzie'
|
|
145
|
+
// ...
|
|
146
|
+
adapter: new MemoryAdapter()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Configuration
|
|
152
|
+
|
|
153
|
+
| Option | Type | Description |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `issuer` | `string` | Your OIDC provider issuer URL (e.g. `https://tenant.auth0.com`) |
|
|
156
|
+
| `clientId` | `string` | OAuth client ID |
|
|
157
|
+
| `clientSecret` | `string` | OAuth client secret — keep in Workers secrets |
|
|
158
|
+
| `audience` | `string` | API audience identifier |
|
|
159
|
+
| `adapter` | `SessionAdapter` | Session adapter (e.g. `cloudflareKV(env.SESSION_KV)`) |
|
|
160
|
+
| `baseUrl` | `string` | Base URL of your application (used for callback and redirects) |
|
|
161
|
+
| `providerHints` | `object` | Optional tweaks for specific providers (`logoutUrl`, `tokenEndpoint`) |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Cloudflare Setup
|
|
166
|
+
|
|
167
|
+
Add a KV namespace to your `wrangler.toml`:
|
|
168
|
+
|
|
169
|
+
```toml
|
|
170
|
+
[[kv_namespaces]]
|
|
171
|
+
binding = "SESSION_KV"
|
|
172
|
+
id = "<your-kv-namespace-id>"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Add your client secret as a Workers secret:
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
wrangler secret put AUTH0_CLIENT_SECRET
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Stack
|
|
184
|
+
|
|
185
|
+
| Component | Choice |
|
|
186
|
+
|---|---|
|
|
187
|
+
| Runtime | Cloudflare Workers |
|
|
188
|
+
| Router | Hono |
|
|
189
|
+
| OAuth | `oauth4webapi` (spec-compliant, no Node.js deps) |
|
|
190
|
+
| Session storage | Cloudflare KV |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Status
|
|
195
|
+
|
|
196
|
+
v0.1.0 — pre-release
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Session } from '../session';
|
|
2
|
+
import { SessionAdapter, PKCEState } from './types';
|
|
3
|
+
export declare class CloudflareKVAdapter implements SessionAdapter {
|
|
4
|
+
private kv;
|
|
5
|
+
constructor(kv: KVNamespace);
|
|
6
|
+
get(sessionId: string): Promise<Session | PKCEState | null>;
|
|
7
|
+
set(sessionId: string, session: Session | PKCEState, ttlSeconds: number): Promise<void>;
|
|
8
|
+
delete(sessionId: string): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=cloudflare-kv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-kv.d.ts","sourceRoot":"","sources":["../../src/adapters/cloudflare-kv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACpC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEnD,qBAAa,mBAAoB,YAAW,cAAc;IAC5C,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,WAAW;IAE7B,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IAK3D,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASvF,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class CloudflareKVAdapter {
|
|
2
|
+
kv;
|
|
3
|
+
constructor(kv) {
|
|
4
|
+
this.kv = kv;
|
|
5
|
+
}
|
|
6
|
+
async get(sessionId) {
|
|
7
|
+
const session = await this.kv.get(sessionId, 'json');
|
|
8
|
+
return session;
|
|
9
|
+
}
|
|
10
|
+
async set(sessionId, session, ttlSeconds) {
|
|
11
|
+
if (ttlSeconds < 60) {
|
|
12
|
+
throw new Error('Bezzie: KV TTL must be at least 60 seconds');
|
|
13
|
+
}
|
|
14
|
+
await this.kv.put(sessionId, JSON.stringify(session), {
|
|
15
|
+
expirationTtl: ttlSeconds,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async delete(sessionId) {
|
|
19
|
+
await this.kv.delete(sessionId);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=cloudflare-kv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-kv.js","sourceRoot":"","sources":["../../src/adapters/cloudflare-kv.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,mBAAmB;IACV;IAApB,YAAoB,EAAe;QAAf,OAAE,GAAF,EAAE,CAAa;IAAG,CAAC;IAEvC,KAAK,CAAC,GAAG,CAAC,SAAiB;QACzB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAsB,SAAS,EAAE,MAAM,CAAC,CAAA;QACzE,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,SAAiB,EAAE,OAA4B,EAAE,UAAkB;QAC3E,IAAI,UAAU,GAAG,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;QAC/D,CAAC;QACD,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE;YACpD,aAAa,EAAE,UAAU;SAC1B,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAiB;QAC5B,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Session } from '../session';
|
|
2
|
+
import { SessionAdapter, PKCEState } from './types';
|
|
3
|
+
export declare class MemoryAdapter implements SessionAdapter {
|
|
4
|
+
private store;
|
|
5
|
+
get(sessionId: string): Promise<Session | PKCEState | null>;
|
|
6
|
+
set(sessionId: string, session: Session | PKCEState, ttlSeconds: number): Promise<void>;
|
|
7
|
+
delete(sessionId: string): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACpC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAOnD,qBAAa,aAAc,YAAW,cAAc;IAClD,OAAO,CAAC,KAAK,CAAmC;IAE1C,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IAU3D,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvF,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class MemoryAdapter {
|
|
2
|
+
store = new Map();
|
|
3
|
+
async get(sessionId) {
|
|
4
|
+
const entry = this.store.get(sessionId);
|
|
5
|
+
if (!entry)
|
|
6
|
+
return null;
|
|
7
|
+
if (Date.now() > entry.expiresAt) {
|
|
8
|
+
this.store.delete(sessionId);
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return entry.session;
|
|
12
|
+
}
|
|
13
|
+
async set(sessionId, session, ttlSeconds) {
|
|
14
|
+
this.store.set(sessionId, {
|
|
15
|
+
session,
|
|
16
|
+
expiresAt: Date.now() + ttlSeconds * 1000,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async delete(sessionId) {
|
|
20
|
+
this.store.delete(sessionId);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,aAAa;IAChB,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAA;IAEhD,KAAK,CAAC,GAAG,CAAC,SAAiB;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACvC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QACvB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAC5B,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAC,OAAO,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,SAAiB,EAAE,OAA4B,EAAE,UAAkB;QAC3E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE;YACxB,OAAO;YACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,GAAG,IAAI;SAC1C,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAiB;QAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IAC9B,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Session } from '../session';
|
|
2
|
+
import { SessionAdapter, PKCEState } from './types';
|
|
3
|
+
export interface RedisClient {
|
|
4
|
+
get(key: string): Promise<string | null>;
|
|
5
|
+
set(key: string, value: string, options?: {
|
|
6
|
+
ex?: number;
|
|
7
|
+
}): Promise<unknown>;
|
|
8
|
+
del(key: string): Promise<unknown>;
|
|
9
|
+
}
|
|
10
|
+
export declare class RedisAdapter implements SessionAdapter {
|
|
11
|
+
private redis;
|
|
12
|
+
constructor(redis: RedisClient);
|
|
13
|
+
get(sessionId: string): Promise<Session | PKCEState | null>;
|
|
14
|
+
set(sessionId: string, session: Session | PKCEState, ttlSeconds: number): Promise<void>;
|
|
15
|
+
delete(sessionId: string): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=redis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/adapters/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACpC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEnD,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5E,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACnC;AAED,qBAAa,YAAa,YAAW,cAAc;IACrC,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,WAAW;IAEhC,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IAM3D,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvF,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class RedisAdapter {
|
|
2
|
+
redis;
|
|
3
|
+
constructor(redis) {
|
|
4
|
+
this.redis = redis;
|
|
5
|
+
}
|
|
6
|
+
async get(sessionId) {
|
|
7
|
+
const session = await this.redis.get(sessionId);
|
|
8
|
+
if (!session)
|
|
9
|
+
return null;
|
|
10
|
+
return JSON.parse(session);
|
|
11
|
+
}
|
|
12
|
+
async set(sessionId, session, ttlSeconds) {
|
|
13
|
+
await this.redis.set(sessionId, JSON.stringify(session), { ex: ttlSeconds });
|
|
14
|
+
}
|
|
15
|
+
async delete(sessionId) {
|
|
16
|
+
await this.redis.del(sessionId);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/adapters/redis.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,YAAY;IACH;IAApB,YAAoB,KAAkB;QAAlB,UAAK,GAAL,KAAK,CAAa;IAAG,CAAC;IAE1C,KAAK,CAAC,GAAG,CAAC,SAAiB;QACzB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC/C,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QACzB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAwB,CAAA;IACnD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,SAAiB,EAAE,OAA4B,EAAE,UAAkB;QAC3E,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAA;IAC9E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAiB;QAC5B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Session } from '../session';
|
|
2
|
+
/**
|
|
3
|
+
* Temporary state for the PKCE OAuth flow.
|
|
4
|
+
*/
|
|
5
|
+
export interface PKCEState {
|
|
6
|
+
/**
|
|
7
|
+
* Code verifier for PKCE.
|
|
8
|
+
*/
|
|
9
|
+
codeVerifier: string;
|
|
10
|
+
/**
|
|
11
|
+
* URL to redirect to after successful authentication.
|
|
12
|
+
*/
|
|
13
|
+
returnTo?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Interface for session storage adapters.
|
|
17
|
+
*/
|
|
18
|
+
export interface SessionAdapter {
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves a session or PKCE state by ID.
|
|
21
|
+
*
|
|
22
|
+
* @param sessionId Session ID
|
|
23
|
+
* @returns Session, PKCE state, or null if not found
|
|
24
|
+
*/
|
|
25
|
+
get(sessionId: string): Promise<Session | PKCEState | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Stores a session or PKCE state.
|
|
28
|
+
*
|
|
29
|
+
* @param sessionId Session ID
|
|
30
|
+
* @param session Session or PKCE state
|
|
31
|
+
* @param ttlSeconds Time-to-live in seconds
|
|
32
|
+
*/
|
|
33
|
+
set(sessionId: string, session: Session | PKCEState, ttlSeconds: number): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Deletes a session or PKCE state.
|
|
36
|
+
*
|
|
37
|
+
* @param sessionId Session ID
|
|
38
|
+
*/
|
|
39
|
+
delete(sessionId: string): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpC;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC,CAAA;IAE3D;;;;;;OAMG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEvF;;;;OAIG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Hono, type MiddlewareHandler } from 'hono';
|
|
2
|
+
import { type Variables } from './middleware';
|
|
3
|
+
import { type SessionAdapter } from './session';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for Bezzie.
|
|
6
|
+
*/
|
|
7
|
+
export interface BezzieConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Your OIDC provider issuer URL (e.g. `https://tenant.auth0.com`).
|
|
10
|
+
*/
|
|
11
|
+
issuer: string;
|
|
12
|
+
/**
|
|
13
|
+
* OAuth client ID.
|
|
14
|
+
*/
|
|
15
|
+
clientId: string;
|
|
16
|
+
/**
|
|
17
|
+
* OAuth client secret — keep this in Workers secrets.
|
|
18
|
+
*/
|
|
19
|
+
clientSecret: string;
|
|
20
|
+
/**
|
|
21
|
+
* Optional API audience identifier.
|
|
22
|
+
*/
|
|
23
|
+
audience?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Session adapter (e.g. `cloudflareKV(env.SESSION_KV)`).
|
|
26
|
+
*/
|
|
27
|
+
adapter: SessionAdapter;
|
|
28
|
+
/**
|
|
29
|
+
* Base URL of your application (used for callback and redirects).
|
|
30
|
+
*/
|
|
31
|
+
baseUrl: string;
|
|
32
|
+
/**
|
|
33
|
+
* Optional tweaks for specific providers.
|
|
34
|
+
*/
|
|
35
|
+
providerHints?: {
|
|
36
|
+
/**
|
|
37
|
+
* Custom logout URL if different from the default OIDC logout.
|
|
38
|
+
*/
|
|
39
|
+
logoutUrl?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Custom token endpoint if different from the discovery metadata.
|
|
42
|
+
*/
|
|
43
|
+
tokenEndpoint?: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Common OIDC provider configurations.
|
|
48
|
+
*/
|
|
49
|
+
export declare const providers: {
|
|
50
|
+
/**
|
|
51
|
+
* Auth0 provider configuration.
|
|
52
|
+
*/
|
|
53
|
+
auth0: (domain: string) => {
|
|
54
|
+
issuer: string;
|
|
55
|
+
providerHints: {
|
|
56
|
+
logoutUrl: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Okta provider configuration.
|
|
61
|
+
*/
|
|
62
|
+
okta: (domain: string) => {
|
|
63
|
+
issuer: string;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Keycloak provider configuration.
|
|
67
|
+
*/
|
|
68
|
+
keycloak: (baseUrl: string, realm: string) => {
|
|
69
|
+
issuer: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Google provider configuration.
|
|
73
|
+
*/
|
|
74
|
+
google: () => {
|
|
75
|
+
issuer: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Creates a Cloudflare KV session adapter.
|
|
80
|
+
*/
|
|
81
|
+
declare function cloudflareKV(kv: KVNamespace): SessionAdapter;
|
|
82
|
+
/**
|
|
83
|
+
* The main Bezzie interface.
|
|
84
|
+
*/
|
|
85
|
+
export interface Bezzie {
|
|
86
|
+
/**
|
|
87
|
+
* Returns a Hono app containing the auth routes (/login, /callback, /logout).
|
|
88
|
+
*/
|
|
89
|
+
routes: () => Hono;
|
|
90
|
+
/**
|
|
91
|
+
* Returns a Hono middleware that protects routes and manages sessions.
|
|
92
|
+
*/
|
|
93
|
+
middleware: () => MiddlewareHandler<{
|
|
94
|
+
Variables: Variables;
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new Bezzie instance.
|
|
99
|
+
*
|
|
100
|
+
* @param config Bezzie configuration
|
|
101
|
+
* @returns Bezzie instance
|
|
102
|
+
* @throws {Error} if required configuration is missing or invalid
|
|
103
|
+
*/
|
|
104
|
+
declare function createBezzie(config: BezzieConfig): Bezzie;
|
|
105
|
+
export { createBezzie, cloudflareKV };
|
|
106
|
+
export type { Variables } from './middleware';
|
|
107
|
+
export type { SessionAdapter, PKCEState, Session } from './session';
|
|
108
|
+
export { CloudflareKVAdapter, RedisAdapter, MemoryAdapter } from './session';
|
|
109
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAEnD,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAEzD,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AAEpE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IAEd;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IAEpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,OAAO,EAAE,cAAc,CAAA;IAEvB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,aAAa,CAAC,EAAE;QACd;;WAEG;QACH,SAAS,CAAC,EAAE,MAAM,CAAA;QAElB;;WAEG;QACH,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,CAAA;CACF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS;IACpB;;OAEG;oBACa,MAAM;;;;;;IAOtB;;OAEG;mBACY,MAAM;;;IAIrB;;OAEG;wBACiB,MAAM,SAAS,MAAM;;;IAIzC;;OAEG;;;;CAIJ,CAAA;AAED;;GAEG;AACH,iBAAS,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,cAAc,CAErD;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAA;IAElB;;OAEG;IACH,UAAU,EAAE,MAAM,iBAAiB,CAAC;QAAE,SAAS,EAAE,SAAS,CAAA;KAAE,CAAC,CAAA;CAC9D;AAED;;;;;;GAMG;AACH,iBAAS,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAwBlD;AAED,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAA;AACrC,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { authRoutes } from './routes';
|
|
2
|
+
import { middleware } from './middleware';
|
|
3
|
+
import { CloudflareKVAdapter } from './session';
|
|
4
|
+
/**
|
|
5
|
+
* Common OIDC provider configurations.
|
|
6
|
+
*/
|
|
7
|
+
export const providers = {
|
|
8
|
+
/**
|
|
9
|
+
* Auth0 provider configuration.
|
|
10
|
+
*/
|
|
11
|
+
auth0: (domain) => ({
|
|
12
|
+
issuer: `https://${domain}`,
|
|
13
|
+
providerHints: {
|
|
14
|
+
logoutUrl: `https://${domain}/v2/logout`,
|
|
15
|
+
},
|
|
16
|
+
}),
|
|
17
|
+
/**
|
|
18
|
+
* Okta provider configuration.
|
|
19
|
+
*/
|
|
20
|
+
okta: (domain) => ({
|
|
21
|
+
issuer: `https://${domain}/oauth2/default`,
|
|
22
|
+
}),
|
|
23
|
+
/**
|
|
24
|
+
* Keycloak provider configuration.
|
|
25
|
+
*/
|
|
26
|
+
keycloak: (baseUrl, realm) => ({
|
|
27
|
+
issuer: `${baseUrl}/realms/${realm}`,
|
|
28
|
+
}),
|
|
29
|
+
/**
|
|
30
|
+
* Google provider configuration.
|
|
31
|
+
*/
|
|
32
|
+
google: () => ({
|
|
33
|
+
issuer: 'https://accounts.google.com',
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Creates a Cloudflare KV session adapter.
|
|
38
|
+
*/
|
|
39
|
+
function cloudflareKV(kv) {
|
|
40
|
+
return new CloudflareKVAdapter(kv);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a new Bezzie instance.
|
|
44
|
+
*
|
|
45
|
+
* @param config Bezzie configuration
|
|
46
|
+
* @returns Bezzie instance
|
|
47
|
+
* @throws {Error} if required configuration is missing or invalid
|
|
48
|
+
*/
|
|
49
|
+
function createBezzie(config) {
|
|
50
|
+
const required = ['issuer', 'clientId', 'clientSecret', 'adapter', 'baseUrl'];
|
|
51
|
+
for (const key of required) {
|
|
52
|
+
if (!config[key]) {
|
|
53
|
+
throw new Error(`Bezzie: missing required config: ${key}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!config.issuer.startsWith('https://')) {
|
|
57
|
+
throw new Error('Bezzie: issuer must start with https://');
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
new URL(config.issuer);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
throw new Error('Bezzie: issuer must be a valid URL');
|
|
64
|
+
}
|
|
65
|
+
const router = authRoutes(config);
|
|
66
|
+
return {
|
|
67
|
+
routes: () => router,
|
|
68
|
+
middleware: () => middleware(config),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export { createBezzie, cloudflareKV };
|
|
72
|
+
export { CloudflareKVAdapter, RedisAdapter, MemoryAdapter } from './session';
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,UAAU,EAAkB,MAAM,cAAc,CAAA;AAEzD,OAAO,EAAE,mBAAmB,EAAuB,MAAM,WAAW,CAAA;AAoDpE;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;;OAEG;IACH,KAAK,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,CAAC;QAC1B,MAAM,EAAE,WAAW,MAAM,EAAE;QAC3B,aAAa,EAAE;YACb,SAAS,EAAE,WAAW,MAAM,YAAY;SACzC;KACF,CAAC;IAEF;;OAEG;IACH,IAAI,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,CAAC;QACzB,MAAM,EAAE,WAAW,MAAM,iBAAiB;KAC3C,CAAC;IAEF;;OAEG;IACH,QAAQ,EAAE,CAAC,OAAe,EAAE,KAAa,EAAE,EAAE,CAAC,CAAC;QAC7C,MAAM,EAAE,GAAG,OAAO,WAAW,KAAK,EAAE;KACrC,CAAC;IAEF;;OAEG;IACH,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACb,MAAM,EAAE,6BAA6B;KACtC,CAAC;CACH,CAAA;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,EAAe;IACnC,OAAO,IAAI,mBAAmB,CAAC,EAAE,CAAC,CAAA;AACpC,CAAC;AAiBD;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,MAAoB;IACxC,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;IAC7E,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,CAAC,GAAyB,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,oCAAoC,GAAG,EAAE,CAAC,CAAA;QAC5D,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAA;IAC5D,CAAC;IAED,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACxB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACvD,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAA;IAEjC,OAAO;QACL,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM;QACpB,UAAU,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;KACrC,CAAA;AACH,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAA;AAGrC,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { Session } from './session';
|
|
3
|
+
import type { BezzieConfig } from './index';
|
|
4
|
+
/**
|
|
5
|
+
* Hono context variables provided by Bezzie middleware.
|
|
6
|
+
* These are what downstream route handlers read from `c.var`.
|
|
7
|
+
*/
|
|
8
|
+
export type Variables = {
|
|
9
|
+
/**
|
|
10
|
+
* The authenticated user's information.
|
|
11
|
+
*/
|
|
12
|
+
user: Session['user'];
|
|
13
|
+
/**
|
|
14
|
+
* The current OAuth access token.
|
|
15
|
+
*/
|
|
16
|
+
accessToken: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function _resetDiscoveryCache(): void;
|
|
19
|
+
export declare function middleware(config: BezzieConfig): MiddlewareHandler<{
|
|
20
|
+
Variables: Variables;
|
|
21
|
+
}>;
|
|
22
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAG7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB;;OAEG;IACH,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACrB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAkBD,wBAAgB,oBAAoB,SAGnC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,iBAAiB,CAAC;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC,CAuE5F"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getCookie } from 'hono/cookie';
|
|
2
|
+
import * as oauth from 'oauth4webapi';
|
|
3
|
+
const jwksCache = {};
|
|
4
|
+
let cachedAS = null;
|
|
5
|
+
let cacheExpiresAt = 0;
|
|
6
|
+
async function getAuthorizationServer(config) {
|
|
7
|
+
if (cachedAS && Date.now() < cacheExpiresAt)
|
|
8
|
+
return cachedAS;
|
|
9
|
+
const issuerUrl = new URL(config.issuer);
|
|
10
|
+
const response = await oauth.discoveryRequest(issuerUrl);
|
|
11
|
+
const as = await oauth.processDiscoveryResponse(issuerUrl, response);
|
|
12
|
+
cachedAS = config.providerHints?.tokenEndpoint
|
|
13
|
+
? { ...as, token_endpoint: config.providerHints.tokenEndpoint }
|
|
14
|
+
: as;
|
|
15
|
+
cacheExpiresAt = Date.now() + 60 * 60 * 1000; // 1 hour
|
|
16
|
+
return cachedAS;
|
|
17
|
+
}
|
|
18
|
+
export function _resetDiscoveryCache() {
|
|
19
|
+
cachedAS = null;
|
|
20
|
+
cacheExpiresAt = 0;
|
|
21
|
+
}
|
|
22
|
+
export function middleware(config) {
|
|
23
|
+
const sessionStore = config.adapter;
|
|
24
|
+
return async (c, next) => {
|
|
25
|
+
// 1. Read the sessionId cookie from the request
|
|
26
|
+
const sessionId = getCookie(c, 'sessionId');
|
|
27
|
+
// 2. If no cookie → return 401
|
|
28
|
+
if (!sessionId) {
|
|
29
|
+
return c.text('Unauthorized', 401);
|
|
30
|
+
}
|
|
31
|
+
// 3. Look up the session in KV using SessionStore
|
|
32
|
+
const session = await sessionStore.get(sessionId);
|
|
33
|
+
// 4. If no session found or it's a PKCE state → return 401
|
|
34
|
+
if (!session || 'codeVerifier' in session) {
|
|
35
|
+
return c.text('Unauthorized', 401);
|
|
36
|
+
}
|
|
37
|
+
const as = await getAuthorizationServer(config);
|
|
38
|
+
// 5. Check if the access token is expired (with 60s buffer)
|
|
39
|
+
if (session.expiresAt < (Date.now() / 1000) + 60) {
|
|
40
|
+
// 6. If expired → use oauth4webapi to perform a refresh token grant
|
|
41
|
+
const client = {
|
|
42
|
+
client_id: config.clientId,
|
|
43
|
+
client_secret: config.clientSecret,
|
|
44
|
+
token_endpoint_auth_method: 'client_secret_post',
|
|
45
|
+
};
|
|
46
|
+
const response = await oauth.refreshTokenGrantRequest(as, client, session.refreshToken);
|
|
47
|
+
const result = await oauth.processRefreshTokenResponse(as, client, response);
|
|
48
|
+
if (oauth.isOAuth2Error(result)) {
|
|
49
|
+
await sessionStore.delete(sessionId);
|
|
50
|
+
return c.text('Unauthorized', 401);
|
|
51
|
+
}
|
|
52
|
+
// Update the session in KV with new tokens and new expiresAt
|
|
53
|
+
session.accessToken = result.access_token;
|
|
54
|
+
if (result.refresh_token) {
|
|
55
|
+
session.refreshToken = result.refresh_token;
|
|
56
|
+
}
|
|
57
|
+
session.expiresAt = Math.floor(Date.now() / 1000) + (result.expires_in || 3600);
|
|
58
|
+
await sessionStore.set(sessionId, session, 30 * 24 * 60 * 60); // 30 days, matches initial session TTL
|
|
59
|
+
}
|
|
60
|
+
// 8. Validate the JWT using JWKS
|
|
61
|
+
try {
|
|
62
|
+
// We need a Request object that has the Authorization header for validateJwtAccessToken
|
|
63
|
+
const mockReq = new Request(c.req.raw.url, {
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: `Bearer ${session.accessToken}`,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
await oauth.validateJwtAccessToken(as, mockReq, config.audience ?? '', { [oauth.jwksCache]: jwksCache });
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
// 9. If JWT invalid → return 401
|
|
72
|
+
return c.text('Unauthorized', 401);
|
|
73
|
+
}
|
|
74
|
+
// 10. Attach the user and accessToken to Hono context
|
|
75
|
+
c.set('user', session.user);
|
|
76
|
+
c.set('accessToken', session.accessToken);
|
|
77
|
+
// 12. Call next()
|
|
78
|
+
await next();
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { BezzieConfig } from './index';
|
|
3
|
+
export declare function _resetDiscoveryCache(): void;
|
|
4
|
+
export declare function authRoutes(config: BezzieConfig): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
5
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAI3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAiB3C,wBAAgB,oBAAoB,SAGnC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,8EA0I9C"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { getCookie, setCookie, deleteCookie } from 'hono/cookie';
|
|
3
|
+
import * as oauth from 'oauth4webapi';
|
|
4
|
+
let cachedAS = null;
|
|
5
|
+
let cacheExpiresAt = 0;
|
|
6
|
+
async function getAuthorizationServer(config) {
|
|
7
|
+
if (cachedAS && Date.now() < cacheExpiresAt)
|
|
8
|
+
return cachedAS;
|
|
9
|
+
const issuerUrl = new URL(config.issuer);
|
|
10
|
+
const response = await oauth.discoveryRequest(issuerUrl);
|
|
11
|
+
const as = await oauth.processDiscoveryResponse(issuerUrl, response);
|
|
12
|
+
cachedAS = config.providerHints?.tokenEndpoint
|
|
13
|
+
? { ...as, token_endpoint: config.providerHints.tokenEndpoint }
|
|
14
|
+
: as;
|
|
15
|
+
cacheExpiresAt = Date.now() + 60 * 60 * 1000; // 1 hour
|
|
16
|
+
return cachedAS;
|
|
17
|
+
}
|
|
18
|
+
export function _resetDiscoveryCache() {
|
|
19
|
+
cachedAS = null;
|
|
20
|
+
cacheExpiresAt = 0;
|
|
21
|
+
}
|
|
22
|
+
export function authRoutes(config) {
|
|
23
|
+
const router = new Hono();
|
|
24
|
+
const sessionStore = config.adapter;
|
|
25
|
+
router.get('/login', async (c) => {
|
|
26
|
+
const code_verifier = oauth.generateRandomCodeVerifier();
|
|
27
|
+
const code_challenge = await oauth.calculatePKCECodeChallenge(code_verifier);
|
|
28
|
+
const state = oauth.generateRandomState();
|
|
29
|
+
const returnTo = c.req.query('returnTo');
|
|
30
|
+
// Store state and codeVerifier in adapter
|
|
31
|
+
await config.adapter.set(`pkce:${state}`, { codeVerifier: code_verifier, returnTo }, 600); // 10 minutes
|
|
32
|
+
const as = await getAuthorizationServer(config);
|
|
33
|
+
if (!as.authorization_endpoint) {
|
|
34
|
+
return c.text('Missing authorization_endpoint', 500);
|
|
35
|
+
}
|
|
36
|
+
const authorizationUrl = new URL(as.authorization_endpoint);
|
|
37
|
+
authorizationUrl.searchParams.set('client_id', config.clientId);
|
|
38
|
+
authorizationUrl.searchParams.set('response_type', 'code');
|
|
39
|
+
authorizationUrl.searchParams.set('redirect_uri', `${config.baseUrl}/auth/callback`);
|
|
40
|
+
authorizationUrl.searchParams.set('scope', 'openid profile email offline_access');
|
|
41
|
+
authorizationUrl.searchParams.set('state', state);
|
|
42
|
+
authorizationUrl.searchParams.set('code_challenge', code_challenge);
|
|
43
|
+
authorizationUrl.searchParams.set('code_challenge_method', 'S256');
|
|
44
|
+
if (config.audience) {
|
|
45
|
+
authorizationUrl.searchParams.set('audience', config.audience);
|
|
46
|
+
}
|
|
47
|
+
return c.redirect(authorizationUrl.toString());
|
|
48
|
+
});
|
|
49
|
+
router.get('/callback', async (c) => {
|
|
50
|
+
const error = c.req.query('error');
|
|
51
|
+
if (error) {
|
|
52
|
+
return c.text(`OAuth error: ${error}`, 400);
|
|
53
|
+
}
|
|
54
|
+
const state = c.req.query('state');
|
|
55
|
+
const code = c.req.query('code');
|
|
56
|
+
if (!state || !code) {
|
|
57
|
+
return c.text('Missing state or code', 400);
|
|
58
|
+
}
|
|
59
|
+
const stored = await config.adapter.get(`pkce:${state}`);
|
|
60
|
+
if (!stored) {
|
|
61
|
+
return c.text('Invalid or expired state', 400);
|
|
62
|
+
}
|
|
63
|
+
const { codeVerifier, returnTo } = stored;
|
|
64
|
+
await config.adapter.delete(`pkce:${state}`);
|
|
65
|
+
const as = await getAuthorizationServer(config);
|
|
66
|
+
const client = {
|
|
67
|
+
client_id: config.clientId,
|
|
68
|
+
client_secret: config.clientSecret,
|
|
69
|
+
token_endpoint_auth_method: 'client_secret_post',
|
|
70
|
+
};
|
|
71
|
+
const response = await oauth.authorizationCodeGrantRequest(as, client, new URL(c.req.url).searchParams, `${config.baseUrl}/auth/callback`, codeVerifier);
|
|
72
|
+
const result = await oauth.processAuthorizationCodeOpenIDResponse(as, client, response);
|
|
73
|
+
if (oauth.isOAuth2Error(result)) {
|
|
74
|
+
return c.text('OAuth 2.0 error', 400);
|
|
75
|
+
}
|
|
76
|
+
const { access_token, refresh_token, expires_in } = result;
|
|
77
|
+
const claims = oauth.getValidatedIdTokenClaims(result);
|
|
78
|
+
const sessionId = crypto.randomUUID();
|
|
79
|
+
const session = {
|
|
80
|
+
accessToken: access_token,
|
|
81
|
+
refreshToken: refresh_token || '',
|
|
82
|
+
expiresAt: Math.floor(Date.now() / 1000) + (expires_in || 3600),
|
|
83
|
+
user: {
|
|
84
|
+
...claims,
|
|
85
|
+
sub: claims.sub,
|
|
86
|
+
email: claims.email,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
// TTL for session in KV. Set to 30 days as per bug fix 3.
|
|
90
|
+
await sessionStore.set(sessionId, session, 30 * 24 * 60 * 60);
|
|
91
|
+
setCookie(c, 'sessionId', sessionId, {
|
|
92
|
+
httpOnly: true,
|
|
93
|
+
secure: true,
|
|
94
|
+
sameSite: 'Strict',
|
|
95
|
+
path: '/',
|
|
96
|
+
});
|
|
97
|
+
if (returnTo && returnTo.startsWith('/') && !returnTo.startsWith('//')) {
|
|
98
|
+
return c.redirect(returnTo);
|
|
99
|
+
}
|
|
100
|
+
return c.redirect('/');
|
|
101
|
+
});
|
|
102
|
+
router.get('/logout', async (c) => {
|
|
103
|
+
const sessionId = getCookie(c, 'sessionId');
|
|
104
|
+
if (sessionId) {
|
|
105
|
+
await sessionStore.delete(sessionId);
|
|
106
|
+
}
|
|
107
|
+
deleteCookie(c, 'sessionId', {
|
|
108
|
+
path: '/',
|
|
109
|
+
secure: true,
|
|
110
|
+
});
|
|
111
|
+
const as = await getAuthorizationServer(config);
|
|
112
|
+
let logoutUrl;
|
|
113
|
+
if (config.providerHints?.logoutUrl) {
|
|
114
|
+
logoutUrl = new URL(config.providerHints.logoutUrl);
|
|
115
|
+
logoutUrl.searchParams.set('client_id', config.clientId);
|
|
116
|
+
logoutUrl.searchParams.set('returnTo', config.baseUrl);
|
|
117
|
+
}
|
|
118
|
+
else if (as.end_session_endpoint) {
|
|
119
|
+
logoutUrl = new URL(as.end_session_endpoint);
|
|
120
|
+
logoutUrl.searchParams.set('client_id', config.clientId);
|
|
121
|
+
logoutUrl.searchParams.set('post_logout_redirect_uri', config.baseUrl);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// If no endpoint found, we just redirect to base URL
|
|
125
|
+
return c.redirect('/');
|
|
126
|
+
}
|
|
127
|
+
return c.redirect(logoutUrl.toString());
|
|
128
|
+
});
|
|
129
|
+
return router;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a user session.
|
|
3
|
+
*/
|
|
4
|
+
export interface Session {
|
|
5
|
+
/**
|
|
6
|
+
* OAuth access token.
|
|
7
|
+
*/
|
|
8
|
+
accessToken: string;
|
|
9
|
+
/**
|
|
10
|
+
* OAuth refresh token.
|
|
11
|
+
*/
|
|
12
|
+
refreshToken: string;
|
|
13
|
+
/**
|
|
14
|
+
* Expiration time of the access token as a Unix timestamp (seconds).
|
|
15
|
+
*/
|
|
16
|
+
expiresAt: number;
|
|
17
|
+
/**
|
|
18
|
+
* User information from the ID token or userinfo endpoint.
|
|
19
|
+
*/
|
|
20
|
+
user: {
|
|
21
|
+
/**
|
|
22
|
+
* Unique identifier for the user.
|
|
23
|
+
*/
|
|
24
|
+
sub: string;
|
|
25
|
+
/**
|
|
26
|
+
* User's email address.
|
|
27
|
+
*/
|
|
28
|
+
email?: string;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export * from './adapters';
|
|
33
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,IAAI,EAAE;QACJ;;WAEG;QACH,GAAG,EAAE,MAAM,CAAA;QACX;;WAEG;QACH,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,CAAA;CACF;AAED,cAAc,YAAY,CAAA"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAgCA,cAAc,YAAY,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bezzie",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BFF OAuth 2.0 auth library for Cloudflare Workers",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/neilpmas/bezzie"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"dev": "wrangler dev",
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"lint": "eslint src test --ext .ts",
|
|
26
|
+
"format": "prettier --write src test",
|
|
27
|
+
"prepublishOnly": "npm run build && npm test && npm run lint"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"oauth4webapi": "^2.0.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"hono": "^4.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@cloudflare/vitest-pool-workers": "^0.5.0",
|
|
37
|
+
"@cloudflare/workers-types": "^4.0.0",
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
39
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
40
|
+
"eslint": "^8.0.0",
|
|
41
|
+
"prettier": "^3.0.0",
|
|
42
|
+
"typescript": "^5.0.0",
|
|
43
|
+
"vitest": "^2.1.0",
|
|
44
|
+
"wrangler": "^3.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|