@alteran/astro 0.6.1 → 0.7.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.
Files changed (46) hide show
  1. package/README.md +23 -0
  2. package/index.js +8 -0
  3. package/migrations/0009_oauth_session_state.sql +31 -0
  4. package/migrations/meta/0009_snapshot.json +749 -0
  5. package/migrations/meta/_journal.json +7 -0
  6. package/package.json +2 -1
  7. package/src/db/account.ts +134 -1
  8. package/src/db/schema.ts +31 -0
  9. package/src/handlers/root.ts +1 -1
  10. package/src/lib/appview/proxy.ts +11 -8
  11. package/src/lib/auth.ts +34 -3
  12. package/src/lib/jwt.ts +4 -0
  13. package/src/lib/oauth/as-keys.ts +29 -0
  14. package/src/lib/oauth/clients.ts +453 -24
  15. package/src/lib/oauth/consent.ts +180 -0
  16. package/src/lib/oauth/dpop.ts +39 -5
  17. package/src/lib/oauth/resource.ts +93 -21
  18. package/src/lib/oauth/store.ts +64 -7
  19. package/src/lib/refresh-session.ts +16 -0
  20. package/src/lib/session-tokens.ts +33 -5
  21. package/src/lib/token-cleanup.ts +4 -2
  22. package/src/lib/util.ts +0 -1
  23. package/src/pages/.well-known/oauth-authorization-server.ts +16 -3
  24. package/src/pages/.well-known/oauth-protected-resource.ts +8 -4
  25. package/src/pages/oauth/authorize.ts +31 -52
  26. package/src/pages/oauth/consent.ts +163 -66
  27. package/src/pages/oauth/jwks.ts +15 -0
  28. package/src/pages/oauth/par.ts +34 -56
  29. package/src/pages/oauth/revoke.ts +75 -0
  30. package/src/pages/oauth/token.ts +148 -89
  31. package/src/pages/xrpc/[...nsid].ts +7 -6
  32. package/src/pages/xrpc/app.bsky.actor.getPreferences.ts +3 -4
  33. package/src/pages/xrpc/app.bsky.actor.putPreferences.ts +3 -4
  34. package/src/pages/xrpc/app.bsky.unspecced.getAgeAssuranceState.ts +3 -4
  35. package/src/pages/xrpc/chat.bsky.convo.getLog.ts +3 -4
  36. package/src/pages/xrpc/chat.bsky.convo.listConvos.ts +3 -4
  37. package/src/pages/xrpc/com.atproto.identity.getRecommendedDidCredentials.ts +3 -4
  38. package/src/pages/xrpc/com.atproto.identity.requestPlcOperationSignature.ts +3 -4
  39. package/src/pages/xrpc/com.atproto.identity.signPlcOperation.ts +3 -4
  40. package/src/pages/xrpc/com.atproto.identity.submitPlcOperation.ts +3 -4
  41. package/src/pages/xrpc/com.atproto.repo.listMissingBlobs.ts +3 -4
  42. package/src/pages/xrpc/com.atproto.server.checkAccountStatus.ts +3 -4
  43. package/src/pages/xrpc/com.atproto.server.deleteSession.ts +28 -9
  44. package/src/pages/xrpc/com.atproto.server.getSession.ts +3 -4
  45. package/src/worker/runtime.ts +23 -1
  46. package/types/env.d.ts +1 -0
package/README.md CHANGED
@@ -254,6 +254,18 @@ validateConfigOrThrow(env);
254
254
  - Handle format is valid
255
255
  - Numeric values are positive
256
256
 
257
+ ### Cloudflare Security Rules
258
+
259
+ `com.atproto.server.refreshSession` is a valid bodyless `POST`. Production deployments must allow this request shape through to the XRPC handler:
260
+
261
+ ```txt
262
+ (http.request.method eq "POST" and http.request.uri.path eq "/xrpc/com.atproto.server.refreshSession")
263
+ ```
264
+
265
+ Astro's SSR origin-check middleware rejects unsafe requests with no `Origin` header before project middleware runs. Alteran normalizes `/xrpc/*` requests at the Worker entrypoint so bearer-token XRPC clients can send bodyless POSTs without tripping Astro's form CSRF guard.
266
+
267
+ If Cloudflare WAF/API Shield also protects the deployment, keep any exception narrow to the expression above. This exception is not configured in `wrangler.jsonc`; Wrangler only manages the Worker deployment and bindings.
268
+
257
269
  ### Environment-Specific Settings
258
270
 
259
271
  See [`wrangler.jsonc`](wrangler.jsonc:40) for environment-specific configurations:
@@ -380,14 +392,25 @@ USER_PASSWORD=your-password
380
392
  REFRESH_TOKEN=your-access-secret
381
393
  REFRESH_TOKEN_SECRET=your-refresh-secret
382
394
  PDS_SEQ_WINDOW=512
395
+ PDS_OAUTH_CLIENT_HOSTS=client.example,another-client.example
383
396
  ```
384
397
 
398
+ `PDS_OAUTH_CLIENT_HOSTS` is required for OAuth clients that use dynamic
399
+ client metadata. It is a comma-separated allowlist of hostnames that this
400
+ single-user PDS may fetch for client metadata and JWKS documents.
401
+
385
402
  ### 3. Run Database Migration
386
403
  ```bash
387
404
  bun run db:generate
388
405
  bun run db:apply:local
389
406
  ```
390
407
 
408
+ Upgrade note: migration `0009_oauth_session_state` revokes existing refresh
409
+ tokens when adding OAuth session state. Existing clients may need to sign in
410
+ again after this migration. This is intentional because older refresh-token
411
+ rows cannot prove whether they came from legacy sessions or pre-hardening OAuth
412
+ flows.
413
+
391
414
  ### 4. Run Tests
392
415
  ```bash
393
416
  bun test tests/mst.test.ts
package/index.js CHANGED
@@ -5,7 +5,15 @@ import { fileURLToPath } from 'node:url';
5
5
  const CORE_ROUTES = [
6
6
  { pattern: '/.well-known/atproto-did', entrypoint: './src/pages/.well-known/atproto-did.ts' },
7
7
  { pattern: '/.well-known/did.json', entrypoint: './src/pages/.well-known/did.json.ts' },
8
+ { pattern: '/.well-known/oauth-authorization-server', entrypoint: './src/pages/.well-known/oauth-authorization-server.ts' },
9
+ { pattern: '/.well-known/oauth-protected-resource', entrypoint: './src/pages/.well-known/oauth-protected-resource.ts' },
8
10
  { pattern: '/health', entrypoint: './src/pages/health.ts' },
11
+ { pattern: '/oauth/authorize', entrypoint: './src/pages/oauth/authorize.ts' },
12
+ { pattern: '/oauth/consent', entrypoint: './src/pages/oauth/consent.ts' },
13
+ { pattern: '/oauth/jwks', entrypoint: './src/pages/oauth/jwks.ts' },
14
+ { pattern: '/oauth/par', entrypoint: './src/pages/oauth/par.ts' },
15
+ { pattern: '/oauth/revoke', entrypoint: './src/pages/oauth/revoke.ts' },
16
+ { pattern: '/oauth/token', entrypoint: './src/pages/oauth/token.ts' },
9
17
  { pattern: '/ready', entrypoint: './src/pages/ready.ts' },
10
18
  { pattern: '/xrpc/com.atproto.identity.getRecommendedDidCredentials', entrypoint: './src/pages/xrpc/com.atproto.identity.getRecommendedDidCredentials.ts' },
11
19
  { pattern: '/xrpc/com.atproto.identity.requestPlcOperationSignature', entrypoint: './src/pages/xrpc/com.atproto.identity.requestPlcOperationSignature.ts' },
@@ -0,0 +1,31 @@
1
+ CREATE TABLE `oauth_session` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `did` text NOT NULL,
4
+ `client_id` text NOT NULL,
5
+ `client_auth_method` text NOT NULL,
6
+ `client_auth_key_id` text,
7
+ `dpop_jkt` text NOT NULL,
8
+ `scope` text NOT NULL,
9
+ `current_refresh_token_id` text NOT NULL,
10
+ `access_jti` text NOT NULL,
11
+ `created_at` integer NOT NULL,
12
+ `updated_at` integer NOT NULL,
13
+ `expires_at` integer NOT NULL,
14
+ `revoked_at` integer
15
+ );
16
+ --> statement-breakpoint
17
+ CREATE INDEX `oauth_session_client_idx` ON `oauth_session` (`client_id`);--> statement-breakpoint
18
+ CREATE INDEX `oauth_session_current_refresh_idx` ON `oauth_session` (`current_refresh_token_id`);--> statement-breakpoint
19
+ CREATE INDEX `oauth_session_access_jti_idx` ON `oauth_session` (`access_jti`);--> statement-breakpoint
20
+ ALTER TABLE `refresh_token` ADD `token_kind` text DEFAULT 'legacy' NOT NULL;--> statement-breakpoint
21
+ ALTER TABLE `refresh_token` ADD `oauth_session_id` text;--> statement-breakpoint
22
+ ALTER TABLE `refresh_token` ADD `client_id` text;--> statement-breakpoint
23
+ ALTER TABLE `refresh_token` ADD `client_auth_method` text;--> statement-breakpoint
24
+ ALTER TABLE `refresh_token` ADD `client_auth_key_id` text;--> statement-breakpoint
25
+ ALTER TABLE `refresh_token` ADD `dpop_jkt` text;--> statement-breakpoint
26
+ ALTER TABLE `refresh_token` ADD `oauth_scope` text;--> statement-breakpoint
27
+ ALTER TABLE `refresh_token` ADD `access_jti` text;--> statement-breakpoint
28
+ ALTER TABLE `refresh_token` ADD `revoked_at` integer;--> statement-breakpoint
29
+ UPDATE `refresh_token` SET `revoked_at` = CAST(strftime('%s','now') AS INTEGER) WHERE `revoked_at` IS NULL;--> statement-breakpoint
30
+ CREATE INDEX `refresh_token_oauth_session_idx` ON `refresh_token` (`oauth_session_id`);--> statement-breakpoint
31
+ CREATE INDEX `refresh_token_access_jti_idx` ON `refresh_token` (`access_jti`);