@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 +1 -1
- package/src/lib/jwt.ts +18 -5
- package/src/lib/secrets.ts +50 -2
- package/types/env.d.ts +3 -2
package/package.json
CHANGED
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 =
|
|
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 =
|
|
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
|
|
106
|
+
const keyData = await getRuntimeString(env, 'REPO_SIGNING_KEY');
|
|
94
107
|
if (!keyData) {
|
|
95
|
-
throw new Error('
|
|
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
|
|
130
|
+
const keyData = await getRuntimeString(env, 'REPO_SIGNING_PUBLIC_KEY');
|
|
118
131
|
if (!keyData) {
|
|
119
132
|
return false;
|
|
120
133
|
}
|
package/src/lib/secrets.ts
CHANGED
|
@@ -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 {
|