bezzie 0.2.1 → 1.0.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/README.md +75 -34
- package/dist/adapters/cloudflare-kv.d.ts +5 -1
- package/dist/adapters/cloudflare-kv.d.ts.map +1 -1
- package/dist/adapters/cloudflare-kv.js +8 -1
- package/dist/adapters/cloudflare-kv.js.map +1 -1
- package/dist/adapters/memory.d.ts +17 -1
- package/dist/adapters/memory.d.ts.map +1 -1
- package/dist/adapters/memory.js +32 -1
- package/dist/adapters/memory.js.map +1 -1
- package/dist/adapters/redis.d.ts +38 -1
- package/dist/adapters/redis.d.ts.map +1 -1
- package/dist/adapters/redis.js +39 -0
- package/dist/adapters/redis.js.map +1 -1
- package/dist/adapters/types.d.ts +19 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/discovery.d.ts +13 -2
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +5 -4
- package/dist/discovery.js.map +1 -1
- package/dist/errors.d.ts +28 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +31 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +109 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -14
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +65 -7
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +32 -10
- package/dist/middleware.js.map +1 -1
- package/dist/routes.d.ts +2 -2
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +153 -22
- package/dist/routes.js.map +1 -1
- package/dist/session.d.ts +9 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js.map +1 -1
- package/package.json +23 -8
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Bezzie
|
|
2
2
|
|
|
3
|
-
**Bezzie** — your BFF's BFF. Handles the Backend for Frontend OAuth pattern so you don't have to.
|
|
4
|
-
|
|
5
3
|
[](https://www.npmjs.com/package/bezzie)
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
> Bezzie is a BFF (Backend for Frontend) OAuth 2.0 library for Cloudflare Workers. It implements [BCP212](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps) — your frontend never sees a JWT.
|
|
6
|
+
|
|
7
|
+
**Bezzie** — your BFF's BFF. Handles the Backend for Frontend OAuth pattern so you don't have to.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The BFF owns the OAuth flow and issues a session cookie to the frontend instead of handing tokens to the browser.
|
|
10
10
|
|
|
11
11
|
```
|
|
12
12
|
npm install bezzie
|
|
@@ -33,13 +33,13 @@ wrangler secret put AUTH0_CLIENT_SECRET
|
|
|
33
33
|
|
|
34
34
|
**4. Wire it up:**
|
|
35
35
|
```typescript
|
|
36
|
-
import { createBezzie, providers,
|
|
36
|
+
import { createBezzie, providers, cloudflareKVAdapter } from 'bezzie'
|
|
37
37
|
|
|
38
38
|
const auth = createBezzie({
|
|
39
39
|
...providers.auth0('your-tenant.auth0.com'),
|
|
40
40
|
clientId: 'xxx',
|
|
41
41
|
clientSecret: env.AUTH0_CLIENT_SECRET,
|
|
42
|
-
adapter:
|
|
42
|
+
adapter: cloudflareKVAdapter(env.SESSION_KV),
|
|
43
43
|
baseUrl: 'https://app.yourproject.com',
|
|
44
44
|
})
|
|
45
45
|
|
|
@@ -75,14 +75,14 @@ There's no open source library for this specific combination (BFF OAuth on Cloud
|
|
|
75
75
|
## Usage
|
|
76
76
|
|
|
77
77
|
```typescript
|
|
78
|
-
import { createBezzie, providers,
|
|
78
|
+
import { createBezzie, providers, cloudflareKVAdapter } from 'bezzie'
|
|
79
79
|
|
|
80
80
|
const auth = createBezzie({
|
|
81
81
|
...providers.auth0('your-tenant.auth0.com'),
|
|
82
82
|
clientId: 'xxx',
|
|
83
83
|
clientSecret: env.AUTH0_CLIENT_SECRET,
|
|
84
84
|
audience: 'https://api.yourproject.com',
|
|
85
|
-
adapter:
|
|
85
|
+
adapter: cloudflareKVAdapter(env.SESSION_KV),
|
|
86
86
|
baseUrl: 'https://app.yourproject.com',
|
|
87
87
|
})
|
|
88
88
|
|
|
@@ -139,18 +139,44 @@ app.all('/api/proxy/*', async (c) => {
|
|
|
139
139
|
|
|
140
140
|
## How It Works
|
|
141
141
|
|
|
142
|
-
###
|
|
142
|
+
### System context
|
|
143
143
|
|
|
144
|
+
```mermaid
|
|
145
|
+
C4Context
|
|
146
|
+
title System Context — Bezzie
|
|
147
|
+
|
|
148
|
+
Person(user, "User", "Browser application user")
|
|
149
|
+
System(bezzie, "Cloudflare Worker (bezzie)", "BFF: owns the OAuth flow, issues session cookies to the browser")
|
|
150
|
+
System_Ext(idp, "Identity Provider", "Auth0 / Okta / Keycloak / Google — issues tokens")
|
|
151
|
+
System_Ext(upstream, "Upstream API", "Your backend — trusts Bearer tokens forwarded by the Worker")
|
|
152
|
+
|
|
153
|
+
Rel(user, bezzie, "HTTPS requests + session cookie")
|
|
154
|
+
Rel(bezzie, idp, "OIDC discovery, token exchange, token refresh")
|
|
155
|
+
Rel(bezzie, upstream, "Proxied requests with Authorization: Bearer")
|
|
156
|
+
Rel(idp, user, "Redirect back after login")
|
|
144
157
|
```
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
|
|
159
|
+
### Containers
|
|
160
|
+
|
|
161
|
+
```mermaid
|
|
162
|
+
C4Container
|
|
163
|
+
title Container — bezzie deployment
|
|
164
|
+
|
|
165
|
+
Person(user, "User")
|
|
166
|
+
Container(spa, "React SPA", "Cloudflare Pages", "Public landing page + protected dashboard")
|
|
167
|
+
Container(worker, "Cloudflare Worker", "Hono + bezzie", "BFF: auth routes + request middleware + token management")
|
|
168
|
+
ContainerDb(kv, "Cloudflare KV", "KVNamespace", "Stores sessions and PKCE state")
|
|
169
|
+
System_Ext(idp, "Identity Provider", "Auth0 / Okta / Keycloak")
|
|
170
|
+
System_Ext(upstream, "Upstream API", "Backend services")
|
|
171
|
+
|
|
172
|
+
Rel(user, spa, "HTTPS")
|
|
173
|
+
Rel(spa, worker, "API calls + __Host-session cookie")
|
|
174
|
+
Rel(worker, kv, "Session read / write / delete")
|
|
175
|
+
Rel(worker, idp, "OIDC discovery + token exchange + token refresh + JWKS")
|
|
176
|
+
Rel(worker, upstream, "Authorization: Bearer {accessToken}")
|
|
151
177
|
```
|
|
152
178
|
|
|
153
|
-
### Per-
|
|
179
|
+
### Per-request flow
|
|
154
180
|
|
|
155
181
|
1. Browser sends request to BFF with session cookie
|
|
156
182
|
2. BFF looks up session in KV, retrieves access token
|
|
@@ -158,16 +184,6 @@ Browser → BFF /auth/login → Auth0 (Authorization Code + PKCE)
|
|
|
158
184
|
4. If expired, BFF uses refresh token to get a new one and updates KV
|
|
159
185
|
5. BFF forwards request upstream with `Authorization: Bearer <token>`
|
|
160
186
|
|
|
161
|
-
### Session Storage
|
|
162
|
-
|
|
163
|
-
Sessions are stored in Cloudflare KV:
|
|
164
|
-
|
|
165
|
-
```
|
|
166
|
-
sessionId → { accessToken, refreshToken, expiresAt, user }
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
KV TTL is aligned with the refresh token lifetime. When the refresh token expires, the user must log in again.
|
|
170
|
-
|
|
171
187
|
---
|
|
172
188
|
|
|
173
189
|
## Adapters
|
|
@@ -177,17 +193,22 @@ Bezzie supports multiple session storage backends:
|
|
|
177
193
|
### Cloudflare KV
|
|
178
194
|
Recommended for production on Cloudflare Workers.
|
|
179
195
|
```typescript
|
|
180
|
-
import {
|
|
196
|
+
import { cloudflareKVAdapter } from 'bezzie'
|
|
181
197
|
// ...
|
|
182
|
-
adapter:
|
|
198
|
+
adapter: cloudflareKVAdapter(env.SESSION_KV)
|
|
183
199
|
```
|
|
184
200
|
|
|
185
201
|
### Redis (Upstash)
|
|
186
|
-
Good for cross-region session consistency.
|
|
202
|
+
Good for cross-region session consistency. Works with [Upstash Redis](https://upstash.com) (recommended for Cloudflare Workers) and any Redis client with `get`/`set`/`del` methods.
|
|
203
|
+
|
|
187
204
|
```typescript
|
|
188
|
-
import {
|
|
189
|
-
|
|
190
|
-
|
|
205
|
+
import { redisAdapter } from 'bezzie'
|
|
206
|
+
import { Redis } from '@upstash/redis/cloudflare'
|
|
207
|
+
|
|
208
|
+
adapter: redisAdapter(new Redis({
|
|
209
|
+
url: env.UPSTASH_REDIS_REST_URL,
|
|
210
|
+
token: env.UPSTASH_REDIS_REST_TOKEN,
|
|
211
|
+
}))
|
|
191
212
|
```
|
|
192
213
|
|
|
193
214
|
### Memory
|
|
@@ -208,9 +229,9 @@ adapter: new MemoryAdapter()
|
|
|
208
229
|
| `clientId` | `string` | OAuth client ID |
|
|
209
230
|
| `clientSecret` | `string` | OAuth client secret — keep in Workers secrets |
|
|
210
231
|
| `audience` | `string` | API audience identifier |
|
|
211
|
-
| `adapter` | `SessionAdapter` | Session adapter (e.g. `
|
|
232
|
+
| `adapter` | `SessionAdapter` | Session adapter (e.g. `cloudflareKVAdapter(env.SESSION_KV)`) |
|
|
212
233
|
| `baseUrl` | `string` | Base URL of your application (used for callback and redirects) |
|
|
213
|
-
| `
|
|
234
|
+
| `providerOverrides` | `object` | Hard overrides for specific provider values (`logoutUrl`, `tokenEndpoint`) |
|
|
214
235
|
|
|
215
236
|
---
|
|
216
237
|
|
|
@@ -232,6 +253,20 @@ wrangler secret put AUTH0_CLIENT_SECRET
|
|
|
232
253
|
|
|
233
254
|
---
|
|
234
255
|
|
|
256
|
+
## Alternatives
|
|
257
|
+
|
|
258
|
+
| | Bezzie | Auth.js (NextAuth) | Lucia | Roll your own (oauth4webapi) |
|
|
259
|
+
|---|---|---|---|---|
|
|
260
|
+
| BFF pattern (tokens never in browser) | ✅ | ✅ (some adapters) | ❌ | You decide |
|
|
261
|
+
| Cloudflare Workers native | ✅ | ⚠️ Edge adapter | ⚠️ | ✅ |
|
|
262
|
+
| Hono integration | ✅ | ❌ | ❌ | ✅ |
|
|
263
|
+
| OIDC discovery | ✅ | ✅ | ❌ | ✅ |
|
|
264
|
+
| Token refresh | ✅ | ✅ | Manual | Manual |
|
|
265
|
+
| Pluggable session storage | ✅ (KV, Redis, Memory) | ✅ | ✅ | Manual |
|
|
266
|
+
| Zero Node.js deps | ✅ | ❌ | ✅ | ✅ |
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
235
270
|
## Stack
|
|
236
271
|
|
|
237
272
|
| Component | Choice |
|
|
@@ -249,6 +284,12 @@ v0.1.0 — pre-release
|
|
|
249
284
|
|
|
250
285
|
---
|
|
251
286
|
|
|
287
|
+
## Changelog
|
|
288
|
+
|
|
289
|
+
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
252
293
|
## License
|
|
253
294
|
|
|
254
295
|
MIT
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Session } from '../session';
|
|
2
|
-
import { PKCEState, SessionAdapter } from './types';
|
|
2
|
+
import { PKCEState, SessionAdapter, SessionAdapterFactory } from './types';
|
|
3
3
|
export declare class CloudflareKVAdapter<TUser extends Record<string, unknown> = Record<string, unknown>> implements SessionAdapter<TUser> {
|
|
4
4
|
private kv;
|
|
5
5
|
constructor(kv: KVNamespace);
|
|
@@ -7,4 +7,8 @@ export declare class CloudflareKVAdapter<TUser extends Record<string, unknown> =
|
|
|
7
7
|
set(sessionId: string, session: Session<TUser> | PKCEState, ttlSeconds: number): Promise<void>;
|
|
8
8
|
delete(sessionId: string): Promise<void>;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a Cloudflare KV session adapter factory.
|
|
12
|
+
*/
|
|
13
|
+
export declare function cloudflareKVAdapter(kv: KVNamespace): SessionAdapterFactory;
|
|
10
14
|
//# sourceMappingURL=cloudflare-kv.d.ts.map
|
|
@@ -1 +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,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;
|
|
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,SAAS,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAG1E,qBAAa,mBAAmB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAC9F,YAAW,cAAc,CAAC,KAAK,CAAC;IAEpB,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,WAAW;IAE7B,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;IAIlE,GAAG,CACP,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,EACnC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAYV,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,WAAW,GAAG,qBAAqB,CAG1E"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SessionStoreError } from '../errors';
|
|
1
2
|
export class CloudflareKVAdapter {
|
|
2
3
|
kv;
|
|
3
4
|
constructor(kv) {
|
|
@@ -8,7 +9,7 @@ export class CloudflareKVAdapter {
|
|
|
8
9
|
}
|
|
9
10
|
async set(sessionId, session, ttlSeconds) {
|
|
10
11
|
if (ttlSeconds < 60) {
|
|
11
|
-
throw new
|
|
12
|
+
throw new SessionStoreError('session_storage_failed', 'Bezzie: KV TTL must be at least 60 seconds');
|
|
12
13
|
}
|
|
13
14
|
await this.kv.put(sessionId, JSON.stringify(session), {
|
|
14
15
|
expirationTtl: ttlSeconds,
|
|
@@ -18,4 +19,10 @@ export class CloudflareKVAdapter {
|
|
|
18
19
|
await this.kv.delete(sessionId);
|
|
19
20
|
}
|
|
20
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a Cloudflare KV session adapter factory.
|
|
24
|
+
*/
|
|
25
|
+
export function cloudflareKVAdapter(kv) {
|
|
26
|
+
return () => new CloudflareKVAdapter(kv);
|
|
27
|
+
}
|
|
21
28
|
//# sourceMappingURL=cloudflare-kv.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-kv.js","sourceRoot":"","sources":["../../src/adapters/cloudflare-kv.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloudflare-kv.js","sourceRoot":"","sources":["../../src/adapters/cloudflare-kv.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAE7C,MAAM,OAAO,mBAAmB;IAGV;IAApB,YAAoB,EAAe;QAAf,OAAE,GAAF,EAAE,CAAa;IAAG,CAAC;IAEvC,KAAK,CAAC,GAAG,CAAC,SAAiB;QACzB,OAAO,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAA6B,SAAS,EAAE,MAAM,CAAC,CAAA;IACzE,CAAC;IAED,KAAK,CAAC,GAAG,CACP,SAAiB,EACjB,OAAmC,EACnC,UAAkB;QAElB,IAAI,UAAU,GAAG,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,iBAAiB,CACzB,wBAAwB,EACxB,4CAA4C,CAC7C,CAAA;QACH,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;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAAe;IACjD,OAAO,GAA2F,EAAE,CAClG,IAAI,mBAAmB,CAAQ,EAAE,CAAC,CAAA;AACtC,CAAC"}
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { Session } from '../session';
|
|
2
|
-
import { SessionAdapter, PKCEState } from './types';
|
|
2
|
+
import { SessionAdapter, SessionAdapterFactory, PKCEState } from './types';
|
|
3
3
|
export declare class MemoryAdapter<TUser extends Record<string, unknown> = Record<string, unknown>> implements SessionAdapter<TUser> {
|
|
4
4
|
private store;
|
|
5
|
+
private lastCleanup;
|
|
6
|
+
private static readonly CLEANUP_INTERVAL_MS;
|
|
7
|
+
/**
|
|
8
|
+
* Evicts all entries whose TTL has expired.
|
|
9
|
+
*
|
|
10
|
+
* Workers runtimes don't support long-lived timers (`setInterval`), so rather
|
|
11
|
+
* than scheduling cleanup we trigger it opportunistically from `get()` at
|
|
12
|
+
* most once every `CLEANUP_INTERVAL_MS`. This bounds memory growth for
|
|
13
|
+
* long-running processes (e.g. local dev, tests) without adding overhead to
|
|
14
|
+
* every read.
|
|
15
|
+
*/
|
|
16
|
+
cleanup(): void;
|
|
5
17
|
get(sessionId: string): Promise<Session<TUser> | PKCEState | null>;
|
|
6
18
|
set(sessionId: string, session: Session<TUser> | PKCEState, ttlSeconds: number): Promise<void>;
|
|
7
19
|
delete(sessionId: string): Promise<void>;
|
|
8
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates an in-memory session adapter factory. Useful for tests and local dev.
|
|
23
|
+
*/
|
|
24
|
+
export declare function memoryAdapter(): SessionAdapterFactory;
|
|
9
25
|
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -1 +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;
|
|
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,qBAAqB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAO1E,qBAAa,aAAa,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACxF,YAAW,cAAc,CAAC,KAAK,CAAC;IAEhC,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,WAAW,CAAI;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAEpD;;;;;;;;OAQG;IACH,OAAO,IAAI,IAAI;IAUT,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;IAgBlE,GAAG,CACP,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,EACnC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAOV,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,qBAAqB,CAGrD"}
|
package/dist/adapters/memory.js
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
export class MemoryAdapter {
|
|
2
2
|
store = new Map();
|
|
3
|
+
lastCleanup = 0;
|
|
4
|
+
static CLEANUP_INTERVAL_MS = 60_000;
|
|
5
|
+
/**
|
|
6
|
+
* Evicts all entries whose TTL has expired.
|
|
7
|
+
*
|
|
8
|
+
* Workers runtimes don't support long-lived timers (`setInterval`), so rather
|
|
9
|
+
* than scheduling cleanup we trigger it opportunistically from `get()` at
|
|
10
|
+
* most once every `CLEANUP_INTERVAL_MS`. This bounds memory growth for
|
|
11
|
+
* long-running processes (e.g. local dev, tests) without adding overhead to
|
|
12
|
+
* every read.
|
|
13
|
+
*/
|
|
14
|
+
cleanup() {
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
for (const [id, entry] of this.store) {
|
|
17
|
+
if (now > entry.expiresAt) {
|
|
18
|
+
this.store.delete(id);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
this.lastCleanup = now;
|
|
22
|
+
}
|
|
3
23
|
async get(sessionId) {
|
|
24
|
+
// Proactive TTL eviction (C6): run at most once per CLEANUP_INTERVAL_MS.
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
if (now - this.lastCleanup > MemoryAdapter.CLEANUP_INTERVAL_MS) {
|
|
27
|
+
this.cleanup();
|
|
28
|
+
}
|
|
4
29
|
const entry = this.store.get(sessionId);
|
|
5
30
|
if (!entry)
|
|
6
31
|
return null;
|
|
7
|
-
if (
|
|
32
|
+
if (now > entry.expiresAt) {
|
|
8
33
|
this.store.delete(sessionId);
|
|
9
34
|
return null;
|
|
10
35
|
}
|
|
@@ -20,4 +45,10 @@ export class MemoryAdapter {
|
|
|
20
45
|
this.store.delete(sessionId);
|
|
21
46
|
}
|
|
22
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Creates an in-memory session adapter factory. Useful for tests and local dev.
|
|
50
|
+
*/
|
|
51
|
+
export function memoryAdapter() {
|
|
52
|
+
return () => new MemoryAdapter();
|
|
53
|
+
}
|
|
23
54
|
//# sourceMappingURL=memory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,aAAa;IAGhB,KAAK,GAAG,IAAI,GAAG,EAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,aAAa;IAGhB,KAAK,GAAG,IAAI,GAAG,EAAgC,CAAA;IAC/C,WAAW,GAAG,CAAC,CAAA;IACf,MAAM,CAAU,mBAAmB,GAAG,MAAM,CAAA;IAEpD;;;;;;;;OAQG;IACH,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACrC,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YACvB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,GAAG,CAAA;IACxB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,SAAiB;QACzB,yEAAyE;QACzE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,GAAG,GAAG,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC,mBAAmB,EAAE,CAAC;YAC/D,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACvC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QACvB,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YAC1B,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,CACP,SAAiB,EACjB,OAAmC,EACnC,UAAkB;QAElB,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;;AAGH;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,GAA2F,EAAE,CAClG,IAAI,aAAa,EAAS,CAAA;AAC9B,CAAC"}
|
package/dist/adapters/redis.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Session } from '../session';
|
|
2
|
-
import { SessionAdapter, PKCEState } from './types';
|
|
2
|
+
import { SessionAdapter, SessionAdapterFactory, PKCEState } from './types';
|
|
3
3
|
export interface RedisClient {
|
|
4
4
|
get(key: string): Promise<string | null>;
|
|
5
5
|
set(key: string, value: string, options?: {
|
|
@@ -7,6 +7,24 @@ export interface RedisClient {
|
|
|
7
7
|
}): Promise<unknown>;
|
|
8
8
|
del(key: string): Promise<unknown>;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Redis-backed session adapter.
|
|
12
|
+
*
|
|
13
|
+
* Compatible with any Redis client that implements the {@link RedisClient} interface,
|
|
14
|
+
* including [Upstash Redis](https://upstash.com) — the recommended choice for Cloudflare Workers
|
|
15
|
+
* because it communicates over HTTP rather than TCP.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { redisAdapter } from 'bezzie'
|
|
20
|
+
* import { Redis } from '@upstash/redis/cloudflare'
|
|
21
|
+
*
|
|
22
|
+
* adapter: redisAdapter(new Redis({
|
|
23
|
+
* url: env.UPSTASH_REDIS_REST_URL,
|
|
24
|
+
* token: env.UPSTASH_REDIS_REST_TOKEN,
|
|
25
|
+
* }))
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
10
28
|
export declare class RedisAdapter<TUser extends Record<string, unknown> = Record<string, unknown>> implements SessionAdapter<TUser> {
|
|
11
29
|
private redis;
|
|
12
30
|
constructor(redis: RedisClient);
|
|
@@ -14,4 +32,23 @@ export declare class RedisAdapter<TUser extends Record<string, unknown> = Record
|
|
|
14
32
|
set(sessionId: string, session: Session<TUser> | PKCEState, ttlSeconds: number): Promise<void>;
|
|
15
33
|
delete(sessionId: string): Promise<void>;
|
|
16
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a Redis-backed session adapter factory.
|
|
37
|
+
*
|
|
38
|
+
* Accepts any client that satisfies the {@link RedisClient} interface, including
|
|
39
|
+
* [Upstash Redis](https://upstash.com) (`@upstash/redis/cloudflare`) — recommended for
|
|
40
|
+
* Cloudflare Workers because it uses the Upstash REST API over HTTP rather than a TCP socket.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* import { redisAdapter } from 'bezzie'
|
|
45
|
+
* import { Redis } from '@upstash/redis/cloudflare'
|
|
46
|
+
*
|
|
47
|
+
* adapter: redisAdapter(new Redis({
|
|
48
|
+
* url: env.UPSTASH_REDIS_REST_URL,
|
|
49
|
+
* token: env.UPSTASH_REDIS_REST_TOKEN,
|
|
50
|
+
* }))
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function redisAdapter(client: RedisClient): SessionAdapterFactory;
|
|
17
54
|
//# sourceMappingURL=redis.d.ts.map
|
|
@@ -1 +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;
|
|
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,qBAAqB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAE1E,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;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,YAAY,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACvF,YAAW,cAAc,CAAC,KAAK,CAAC;IAEpB,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,WAAW;IAEhC,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;IAMlE,GAAG,CACP,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,EACnC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC;IAIV,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,qBAAqB,CAGvE"}
|
package/dist/adapters/redis.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redis-backed session adapter.
|
|
3
|
+
*
|
|
4
|
+
* Compatible with any Redis client that implements the {@link RedisClient} interface,
|
|
5
|
+
* including [Upstash Redis](https://upstash.com) — the recommended choice for Cloudflare Workers
|
|
6
|
+
* because it communicates over HTTP rather than TCP.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { redisAdapter } from 'bezzie'
|
|
11
|
+
* import { Redis } from '@upstash/redis/cloudflare'
|
|
12
|
+
*
|
|
13
|
+
* adapter: redisAdapter(new Redis({
|
|
14
|
+
* url: env.UPSTASH_REDIS_REST_URL,
|
|
15
|
+
* token: env.UPSTASH_REDIS_REST_TOKEN,
|
|
16
|
+
* }))
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
1
19
|
export class RedisAdapter {
|
|
2
20
|
redis;
|
|
3
21
|
constructor(redis) {
|
|
@@ -16,4 +34,25 @@ export class RedisAdapter {
|
|
|
16
34
|
await this.redis.del(sessionId);
|
|
17
35
|
}
|
|
18
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates a Redis-backed session adapter factory.
|
|
39
|
+
*
|
|
40
|
+
* Accepts any client that satisfies the {@link RedisClient} interface, including
|
|
41
|
+
* [Upstash Redis](https://upstash.com) (`@upstash/redis/cloudflare`) — recommended for
|
|
42
|
+
* Cloudflare Workers because it uses the Upstash REST API over HTTP rather than a TCP socket.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import { redisAdapter } from 'bezzie'
|
|
47
|
+
* import { Redis } from '@upstash/redis/cloudflare'
|
|
48
|
+
*
|
|
49
|
+
* adapter: redisAdapter(new Redis({
|
|
50
|
+
* url: env.UPSTASH_REDIS_REST_URL,
|
|
51
|
+
* token: env.UPSTASH_REDIS_REST_TOKEN,
|
|
52
|
+
* }))
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function redisAdapter(client) {
|
|
56
|
+
return () => new RedisAdapter(client);
|
|
57
|
+
}
|
|
19
58
|
//# sourceMappingURL=redis.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/adapters/redis.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,YAAY;IAGH;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,CAA+B,CAAA;IAC1D,CAAC;IAED,KAAK,CAAC,GAAG,CACP,SAAiB,EACjB,OAAmC,EACnC,UAAkB;QAElB,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"}
|
|
1
|
+
{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/adapters/redis.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,YAAY;IAGH;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,CAA+B,CAAA;IAC1D,CAAC;IAED,KAAK,CAAC,GAAG,CACP,SAAiB,EACjB,OAAmC,EACnC,UAAkB;QAElB,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;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmB;IAC9C,OAAO,GAA2F,EAAE,CAClG,IAAI,YAAY,CAAQ,MAAM,CAAC,CAAA;AACnC,CAAC"}
|
package/dist/adapters/types.d.ts
CHANGED
|
@@ -15,6 +15,17 @@ export interface PKCEState {
|
|
|
15
15
|
* URL to redirect to after successful authentication.
|
|
16
16
|
*/
|
|
17
17
|
returnTo?: string;
|
|
18
|
+
/**
|
|
19
|
+
* CSRF token bound to the user's browser session via the `__Host-pkce-csrf` cookie.
|
|
20
|
+
* Used to prevent login-CSRF attacks (S4).
|
|
21
|
+
*/
|
|
22
|
+
csrfToken: string;
|
|
23
|
+
/**
|
|
24
|
+
* OIDC `nonce` value. Generated at `/login`, passed in the authorization
|
|
25
|
+
* request, and verified against the `nonce` claim of the returned ID token
|
|
26
|
+
* at `/callback` to prevent ID token replay attacks (S8).
|
|
27
|
+
*/
|
|
28
|
+
nonce: string;
|
|
18
29
|
}
|
|
19
30
|
/**
|
|
20
31
|
* Interface for session storage adapters.
|
|
@@ -42,4 +53,12 @@ export interface SessionAdapter<TUser extends Record<string, unknown> = Record<s
|
|
|
42
53
|
*/
|
|
43
54
|
delete(sessionId: string): Promise<void>;
|
|
44
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Factory function that produces a {@link SessionAdapter} for a given `TUser`.
|
|
58
|
+
*
|
|
59
|
+
* Consumers construct adapters via the factory form (e.g. `memoryAdapter()`,
|
|
60
|
+
* `cloudflareKVAdapter(env.SESSION_KV)`) so `TUser` is inferred from
|
|
61
|
+
* `createBezzie<TUser>(...)` rather than needing to be specified twice.
|
|
62
|
+
*/
|
|
63
|
+
export type SessionAdapterFactory = <TUser extends Record<string, unknown> = Record<string, unknown>>() => SessionAdapter<TUser>;
|
|
45
64
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +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,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;
|
|
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,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC7F;;;;;OAKG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC,CAAA;IAElE;;;;;;OAMG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE9F;;;;OAIG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,cAAc,CAAC,KAAK,CAAC,CAAA"}
|
package/dist/discovery.d.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import * as oauth from 'oauth4webapi';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Minimal config shape that discovery needs — it only reads `issuer` and
|
|
4
|
+
* `providerOverrides.tokenEndpoint`. Accepting this interface (rather than
|
|
5
|
+
* `BezzieConfig`) avoids a generic parameter that has no bearing on discovery.
|
|
6
|
+
*/
|
|
7
|
+
interface DiscoveryConfig {
|
|
8
|
+
issuer: string;
|
|
9
|
+
providerOverrides?: {
|
|
10
|
+
tokenEndpoint?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
3
13
|
export interface DiscoveryCache {
|
|
4
14
|
cachedAS: oauth.AuthorizationServer | null;
|
|
5
15
|
cacheExpiresAt: number;
|
|
6
16
|
jwksCache: oauth.JWKSCacheInput;
|
|
7
17
|
}
|
|
8
18
|
export declare function createDiscoveryCache(): DiscoveryCache;
|
|
9
|
-
export declare function getAuthorizationServer(config:
|
|
19
|
+
export declare function getAuthorizationServer(config: DiscoveryConfig, cache: DiscoveryCache): Promise<oauth.AuthorizationServer>;
|
|
20
|
+
export {};
|
|
10
21
|
//# sourceMappingURL=discovery.d.ts.map
|
package/dist/discovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAGrC;;;;GAIG;AACH,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,CAAC,EAAE;QAClB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,CAAA;CACF;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,KAAK,CAAC,mBAAmB,GAAG,IAAI,CAAA;IAC1C,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,KAAK,CAAC,cAAc,CAAA;CAChC;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAoBpC"}
|
package/dist/discovery.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as oauth from 'oauth4webapi';
|
|
2
|
+
import { DiscoveryError } from './errors';
|
|
2
3
|
export function createDiscoveryCache() {
|
|
3
4
|
return { cachedAS: null, cacheExpiresAt: 0, jwksCache: {} };
|
|
4
5
|
}
|
|
@@ -8,17 +9,17 @@ export async function getAuthorizationServer(config, cache) {
|
|
|
8
9
|
}
|
|
9
10
|
const issuer = new URL(config.issuer);
|
|
10
11
|
try {
|
|
11
|
-
const response = await oauth.discoveryRequest(issuer);
|
|
12
|
+
const response = await oauth.discoveryRequest(issuer, { signal: AbortSignal.timeout(5000) });
|
|
12
13
|
const as = await oauth.processDiscoveryResponse(issuer, response);
|
|
13
|
-
const cachedAS = config.
|
|
14
|
-
? { ...as, token_endpoint: config.
|
|
14
|
+
const cachedAS = config.providerOverrides?.tokenEndpoint
|
|
15
|
+
? { ...as, token_endpoint: config.providerOverrides.tokenEndpoint }
|
|
15
16
|
: as;
|
|
16
17
|
cache.cachedAS = cachedAS;
|
|
17
18
|
cache.cacheExpiresAt = Date.now() + 60 * 60 * 1000;
|
|
18
19
|
return cachedAS;
|
|
19
20
|
}
|
|
20
21
|
catch (err) {
|
|
21
|
-
throw new
|
|
22
|
+
throw new DiscoveryError(`Bezzie: OIDC discovery failed for ${config.issuer}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
//# sourceMappingURL=discovery.js.map
|
package/dist/discovery.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAoBzC,MAAM,UAAU,oBAAoB;IAClC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAA;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAuB,EACvB,KAAqB;IAErB,IAAI,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC,QAAQ,CAAA;IACvB,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACrC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC5F,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,wBAAwB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QACjE,MAAM,QAAQ,GAAG,MAAM,CAAC,iBAAiB,EAAE,aAAa;YACtD,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,cAAc,EAAE,MAAM,CAAC,iBAAiB,CAAC,aAAa,EAAE;YACnE,CAAC,CAAC,EAAE,CAAA;QACN,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACzB,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;QAClD,OAAO,QAAQ,CAAA;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,cAAc,CACtB,qCAAqC,MAAM,CAAC,MAAM,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EACzG,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;IACH,CAAC;AACH,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type BezzieErrorCode = 'discovery_failed' | 'callback_invalid_state' | 'callback_provider_error' | 'token_exchange_failed' | 'refresh_failed' | 'refresh_token_revoked' | 'session_storage_failed' | 'session_not_found' | 'config_invalid';
|
|
2
|
+
export interface BezzieErrorOptions {
|
|
3
|
+
cause?: unknown;
|
|
4
|
+
httpStatus?: number;
|
|
5
|
+
oauthError?: string;
|
|
6
|
+
oauthErrorDescription?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class BezzieError extends Error {
|
|
9
|
+
readonly code: BezzieErrorCode;
|
|
10
|
+
readonly httpStatus?: number;
|
|
11
|
+
readonly oauthError?: string;
|
|
12
|
+
readonly oauthErrorDescription?: string;
|
|
13
|
+
constructor(code: BezzieErrorCode, message: string, opts?: BezzieErrorOptions);
|
|
14
|
+
}
|
|
15
|
+
export declare class DiscoveryError extends BezzieError {
|
|
16
|
+
constructor(message: string, opts?: BezzieErrorOptions);
|
|
17
|
+
}
|
|
18
|
+
export declare class CallbackError extends BezzieError {
|
|
19
|
+
}
|
|
20
|
+
export declare class TokenExchangeError extends BezzieError {
|
|
21
|
+
}
|
|
22
|
+
export declare class RefreshError extends BezzieError {
|
|
23
|
+
}
|
|
24
|
+
export declare class SessionStoreError extends BezzieError {
|
|
25
|
+
}
|
|
26
|
+
export declare class ConfigError extends BezzieError {
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,kBAAkB,GAClB,wBAAwB,GACxB,yBAAyB,GACzB,uBAAuB,GACvB,gBAAgB,GAChB,uBAAuB,GACvB,wBAAwB,GACxB,mBAAmB,GACnB,gBAAgB,CAAA;AAEpB,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,qBAAqB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAA;gBAE3B,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB;CASlF;AAED,qBAAa,cAAe,SAAQ,WAAW;gBACjC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB;CAGvD;AAED,qBAAa,aAAc,SAAQ,WAAW;CAAG;AACjD,qBAAa,kBAAmB,SAAQ,WAAW;CAAG;AACtD,qBAAa,YAAa,SAAQ,WAAW;CAAG;AAChD,qBAAa,iBAAkB,SAAQ,WAAW;CAAG;AACrD,qBAAa,WAAY,SAAQ,WAAW;CAAG"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class BezzieError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
httpStatus;
|
|
4
|
+
oauthError;
|
|
5
|
+
oauthErrorDescription;
|
|
6
|
+
constructor(code, message, opts = {}) {
|
|
7
|
+
super(message, { cause: opts.cause });
|
|
8
|
+
this.name = this.constructor.name;
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.httpStatus = opts.httpStatus;
|
|
11
|
+
this.oauthError = opts.oauthError;
|
|
12
|
+
this.oauthErrorDescription = opts.oauthErrorDescription;
|
|
13
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class DiscoveryError extends BezzieError {
|
|
17
|
+
constructor(message, opts) {
|
|
18
|
+
super('discovery_failed', message, opts);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class CallbackError extends BezzieError {
|
|
22
|
+
}
|
|
23
|
+
export class TokenExchangeError extends BezzieError {
|
|
24
|
+
}
|
|
25
|
+
export class RefreshError extends BezzieError {
|
|
26
|
+
}
|
|
27
|
+
export class SessionStoreError extends BezzieError {
|
|
28
|
+
}
|
|
29
|
+
export class ConfigError extends BezzieError {
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAkBA,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,CAAiB;IACrB,UAAU,CAAS;IACnB,UAAU,CAAS;IACnB,qBAAqB,CAAS;IAEvC,YAAY,IAAqB,EAAE,OAAe,EAAE,OAA2B,EAAE;QAC/E,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;QACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QACjC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QACjC,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAA;QACvD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IACnD,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,WAAW;IAC7C,YAAY,OAAe,EAAE,IAAyB;QACpD,KAAK,CAAC,kBAAkB,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;IAC1C,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,WAAW;CAAG;AACjD,MAAM,OAAO,kBAAmB,SAAQ,WAAW;CAAG;AACtD,MAAM,OAAO,YAAa,SAAQ,WAAW;CAAG;AAChD,MAAM,OAAO,iBAAkB,SAAQ,WAAW;CAAG;AACrD,MAAM,OAAO,WAAY,SAAQ,WAAW;CAAG"}
|