@alteran/astro 0.1.8 → 0.1.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alteran/astro",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Astro integration for running a Cloudflare-hosted Bluesky PDS with Alteran.",
5
5
  "module": "index.js",
6
6
  "types": "index.d.ts",
package/src/lib/jwt.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Env } from '../env';
2
+ import { getRuntimeString } from './secrets';
2
3
 
3
4
  export interface JwtClaims {
4
5
  sub: string; // DID
@@ -31,7 +32,14 @@ export async function signJwt(env: Env, claims: JwtClaims, kind: 'access' | 'ref
31
32
  if (claims.scope) payload.scope = claims.scope;
32
33
  if (claims.jti) payload.jti = claims.jti;
33
34
 
34
- const secret = kind === 'access' ? (env.ACCESS_TOKEN_SECRET ?? 'dev-access') : (env.REFRESH_TOKEN_SECRET ?? 'dev-refresh');
35
+ const secret = await getRuntimeString(
36
+ env,
37
+ kind === 'access' ? 'ACCESS_TOKEN_SECRET' : 'REFRESH_TOKEN_SECRET',
38
+ kind === 'access' ? 'dev-access' : 'dev-refresh'
39
+ );
40
+ if (!secret) {
41
+ throw new Error(`Missing ${kind === 'access' ? 'ACCESS_TOKEN_SECRET' : 'REFRESH_TOKEN_SECRET'}`);
42
+ }
35
43
  const algorithm = (env.JWT_ALGORITHM as string | undefined) ?? 'HS256';
36
44
 
37
45
  if (algorithm === 'EdDSA') {
@@ -50,7 +58,12 @@ export async function verifyJwt(env: Env, token: string): Promise<{ valid: boole
50
58
 
51
59
  let ok = false;
52
60
  if (header.alg === 'HS256' && header.typ === 'JWT') {
53
- const secret = (payload.t === 'refresh') ? (env.REFRESH_TOKEN_SECRET ?? 'dev-refresh') : (env.ACCESS_TOKEN_SECRET ?? 'dev-access');
61
+ const secret = await getRuntimeString(
62
+ env,
63
+ payload.t === 'refresh' ? 'REFRESH_TOKEN_SECRET' : 'ACCESS_TOKEN_SECRET',
64
+ payload.t === 'refresh' ? 'dev-refresh' : 'dev-access'
65
+ );
66
+ if (!secret) return null;
54
67
  ok = await hmacJwtVerify(parts[0] + '.' + parts[1], parts[2], secret);
55
68
  } else if (header.alg === 'EdDSA' && header.typ === 'JWT') {
56
69
  ok = await eddsaJwtVerify(parts[0] + '.' + parts[1], parts[2], env);
@@ -90,9 +103,9 @@ async function eddsaJwtSign(payload: any, env: Env): Promise<string> {
90
103
  const data = `${h}.${p}`;
91
104
 
92
105
  // Import Ed25519 private key from env
93
- const keyData = env.JWT_ED25519_PRIVATE_KEY as string | undefined;
106
+ const keyData = await getRuntimeString(env, 'REPO_SIGNING_KEY');
94
107
  if (!keyData) {
95
- throw new Error('JWT_ED25519_PRIVATE_KEY not configured');
108
+ throw new Error('REPO_SIGNING_KEY not configured for EdDSA JWTs');
96
109
  }
97
110
 
98
111
  // Decode base64 private key
@@ -114,7 +127,7 @@ async function eddsaJwtVerify(data: string, sigB64: string, env: Env): Promise<b
114
127
  const enc = new TextEncoder();
115
128
 
116
129
  // Import Ed25519 public key from env
117
- const keyData = env.JWT_ED25519_PUBLIC_KEY as string | undefined;
130
+ const keyData = await getRuntimeString(env, 'REPO_SIGNING_PUBLIC_KEY');
118
131
  if (!keyData) {
119
132
  return false;
120
133
  }
@@ -1,3 +1,4 @@
1
+ import { setGetEnv } from 'astro/env/setup';
1
2
  import type { Env } from '../env';
2
3
  import type { SecretsStoreSecret } from '../../types/env';
3
4
 
@@ -9,8 +10,6 @@ const SECRET_KEYS = [
9
10
  'REFRESH_TOKEN_SECRET',
10
11
  'REPO_SIGNING_KEY',
11
12
  'REPO_SIGNING_PUBLIC_KEY',
12
- 'JWT_ED25519_PRIVATE_KEY',
13
- 'JWT_ED25519_PUBLIC_KEY',
14
13
  ] as const satisfies readonly (keyof Env)[];
15
14
 
16
15
  function isSecretStoreBinding(value: unknown): value is SecretsStoreSecret {
@@ -42,6 +41,55 @@ export async function resolveEnvSecrets<E extends Env>(env: E): Promise<E> {
42
41
  })
43
42
  );
44
43
 
44
+ setGetEnv((key) => {
45
+ const local = resolved[key];
46
+ if (typeof local === 'string') return local;
47
+ if (typeof local === 'number' || typeof local === 'boolean') return String(local);
48
+ const fallback = process.env[key];
49
+ return typeof fallback === 'string' ? fallback : undefined;
50
+ });
51
+
45
52
  return resolved as E;
46
53
  }
47
54
 
55
+ type AstroGetSecret = (key: string) => string | undefined;
56
+
57
+ let astroGetSecret: AstroGetSecret | null | undefined;
58
+
59
+ async function loadAstroGetSecret(): Promise<AstroGetSecret | null> {
60
+ if (astroGetSecret !== undefined) return astroGetSecret;
61
+ try {
62
+ const mod = await import('astro:env/server');
63
+ astroGetSecret = mod.getSecret as AstroGetSecret;
64
+ } catch {
65
+ astroGetSecret = null;
66
+ }
67
+ return astroGetSecret;
68
+ }
69
+
70
+ export async function getRuntimeString<K extends keyof Env>(
71
+ env: Env,
72
+ key: K,
73
+ fallback?: string
74
+ ): Promise<string | undefined> {
75
+ const current = env[key];
76
+ if (typeof current === 'string' && current !== '') {
77
+ return current;
78
+ }
79
+
80
+ const secretFn = await loadAstroGetSecret();
81
+ if (secretFn) {
82
+ try {
83
+ const value = secretFn(String(key));
84
+ if (typeof value === 'string' && value !== '') {
85
+ return value;
86
+ }
87
+ } catch (error) {
88
+ if (fallback === undefined) {
89
+ throw error;
90
+ }
91
+ }
92
+ }
93
+
94
+ return fallback;
95
+ }
package/types/env.d.ts CHANGED
@@ -24,6 +24,7 @@ declare global {
24
24
  PDS_HANDLE?: string | SecretsStoreSecret;
25
25
  PDS_DID?: string | SecretsStoreSecret;
26
26
  PDS_HOSTNAME?: string;
27
+ PDS_ALLOWED_MIME?: string;
27
28
  USER_PASSWORD?: string | SecretsStoreSecret;
28
29
  PDS_MAX_BLOB_SIZE?: string;
29
30
  ACCESS_TOKEN_SECRET?: string | SecretsStoreSecret;
@@ -31,13 +32,13 @@ declare global {
31
32
  PDS_ACCESS_TTL_SEC?: string;
32
33
  PDS_REFRESH_TTL_SEC?: string;
33
34
  JWT_ALGORITHM?: string;
34
- JWT_ED25519_PRIVATE_KEY?: string | SecretsStoreSecret;
35
- JWT_ED25519_PUBLIC_KEY?: string | SecretsStoreSecret;
36
35
  REPO_SIGNING_KEY?: string | SecretsStoreSecret;
37
36
  REPO_SIGNING_PUBLIC_KEY?: string | SecretsStoreSecret;
38
37
  PDS_RATE_LIMIT_PER_MIN?: string;
39
38
  PDS_MAX_JSON_BYTES?: string;
40
39
  PDS_CORS_ORIGIN?: string;
40
+ PDS_SEQ_WINDOW?: string;
41
+ ENVIRONMENT?: string;
41
42
  }
42
43
 
43
44
  namespace App {