Skip to content

Commit d8268cd

Browse files
committed
refactor(proxy): replace publicAPIKey with studioAuthKey for authentication in proxy
1 parent 4c4c30a commit d8268cd

3 files changed

Lines changed: 38 additions & 38 deletions

File tree

packages/cli/src/actions/proxy.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type Options = {
3434
port?: number;
3535
logLevel?: string[];
3636
databaseUrl?: string;
37-
publicAPIKey?: string;
37+
studioAuthKey?: string;
3838
signatureToleranceSecs?: number;
3939
};
4040

@@ -59,13 +59,13 @@ function normalizePublicKey(key: string): string {
5959
}
6060

6161
export async function run(options: Options) {
62-
// Resolve public key: CLI arg takes precedence, then ZENSTACK_PUBLIC_KEY env var.
63-
options = { ...options, publicAPIKey: options.publicAPIKey ?? process.env['ZENSTACK_PUBLIC_KEY'] };
64-
if (!options.publicAPIKey) {
62+
// Resolve public key: CLI arg takes precedence, then ZENSTACK_STUDIO_AUTH_KEY env var.
63+
options = { ...options, studioAuthKey: options.studioAuthKey ?? process.env['ZENSTACK_STUDIO_AUTH_KEY'] };
64+
if (!options.studioAuthKey) {
6565
console.warn(
6666
colors.yellow(
6767
'Warning: This proxy has no authentication. Do not expose it to the public network.\n' +
68-
'To secure it, get an API key from ZenStack Studio and set it via the ZENSTACK_PUBLIC_KEY environment variable.',
68+
'To secure it, get an API key from ZenStack Studio and set it via the ZENSTACK_STUDIO_AUTH_KEY environment variable.',
6969
),
7070
);
7171
}
@@ -139,9 +139,9 @@ export async function run(options: Options) {
139139
throw new CliError(`Failed to connect to the database: ${err instanceof Error ? err.message : String(err)}`);
140140
}
141141

142-
// If a publicAPIKey is provided, create an authDb with the policy plugin
142+
// If a studioAuthKey is provided, create an authDb with the policy plugin
143143
let authDb: ClientContract<SchemaDef> | undefined;
144-
if (options.publicAPIKey) {
144+
if (options.studioAuthKey) {
145145
authDb = db.$use(new PolicyPlugin()) as ClientContract<SchemaDef>;
146146
console.log(colors.gray('Access policy plugin enabled for authorization.'));
147147
}
@@ -244,7 +244,7 @@ export function createProxyApp(
244244
client: ClientContract<SchemaDef>,
245245
schema: SchemaDef,
246246
options?: {
247-
publicAPIKey?: string;
247+
studioAuthKey?: string;
248248
authDb?: ClientContract<SchemaDef>;
249249
/** Seconds within which a signed request is considered valid. Defaults to 60. */
250250
signatureToleranceSecs?: number;
@@ -263,10 +263,10 @@ export function createProxyApp(
263263
);
264264
app.use(express.urlencoded({ extended: true, limit: '5mb' }));
265265

266-
if (options?.publicAPIKey) {
266+
if (options?.studioAuthKey) {
267267
// Apply signature-verification middleware to all authenticated endpoints.
268268
const toleranceSecs = options.signatureToleranceSecs ?? 60;
269-
const normalizedKey = normalizePublicKey(options.publicAPIKey);
269+
const normalizedKey = normalizePublicKey(options.studioAuthKey);
270270
app.use(['/api/model', '/api/schema'], createSignatureMiddleware(normalizedKey, toleranceSecs));
271271
}
272272

@@ -370,7 +370,7 @@ function createSignatureMiddleware(publicKey: string, toleranceSeconds: number)
370370
/**
371371
* Resolves the appropriate client for a request based on the Authorization header.
372372
*
373-
* - No publicAPIKey configured (authDb is undefined): always return the base client.
373+
* - No studioAuthKey configured (authDb is undefined): always return the base client.
374374
* - SuperUser claim: return the base client (full access, no policy enforcement).
375375
* - Regular user claim: return authDb with the user identity set via $setAuth.
376376
* - No / invalid token: return the base client.
@@ -415,7 +415,7 @@ function startServer(
415415
authDb?: ClientContract<SchemaDef>,
416416
) {
417417
const app = createProxyApp(client, schema, {
418-
publicAPIKey: options.publicAPIKey,
418+
studioAuthKey: options.studioAuthKey,
419419
authDb,
420420
signatureToleranceSecs: options.signatureToleranceSecs,
421421
});

packages/cli/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,8 @@ Arguments following -- are passed to the seed script. E.g.: "zen db seed -- --us
265265
.addOption(new Option('-l, --logLevel <level...>', 'Query log levels (e.g., query, error)'))
266266
.addOption(
267267
new Option(
268-
'--publicAPIKey <key>',
269-
'public key used to verify request signatures. Can also be set via the ZENSTACK_PUBLIC_KEY environment variable. ',
268+
'--studioAuthKey <key>',
269+
'Authentication key from ZenStack Studio. When set, the proxy will only accept requests signed by your Studio project.\nCan also be set via the ZENSTACK_STUDIO_AUTH_KEY environment variable. ',
270270
),
271271
)
272272
.addOption(

packages/cli/test/proxy.test.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ describe('CLI proxy tests', () => {
230230

231231
// ─── AuthN: signature verification ─────────────────────────────────────────
232232

233-
describe('signature verification (publicAPIKey configured)', () => {
233+
describe('signature verification (studioAuthKey configured)', () => {
234234
const zmodel = `
235235
model User {
236236
id String @id @default(cuid())
@@ -240,7 +240,7 @@ describe('CLI proxy tests', () => {
240240

241241
it('should reject requests missing the signature header with 401', async () => {
242242
const client = await createTestClient(zmodel);
243-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
243+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
244244
const baseUrl = await startAt(app);
245245

246246
const r = await fetch(`${baseUrl}/api/model/user/findMany`);
@@ -252,7 +252,7 @@ describe('CLI proxy tests', () => {
252252

253253
it('should reject requests with an invalid signature with 401', async () => {
254254
const client = await createTestClient(zmodel);
255-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
255+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
256256
const baseUrl = await startAt(app);
257257

258258
const r = await fetch(`${baseUrl}/api/model/user/findMany`, {
@@ -263,7 +263,7 @@ describe('CLI proxy tests', () => {
263263

264264
it('should allow GET requests with a valid signature', async () => {
265265
const client = await createTestClient(zmodel);
266-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
266+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
267267
const baseUrl = await startAt(app);
268268

269269
const path = '/api/model/user/findMany';
@@ -279,7 +279,7 @@ describe('CLI proxy tests', () => {
279279

280280
it('should allow GET request with query params and a valid signature', async () => {
281281
const client = await createTestClient(zmodel);
282-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
282+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
283283
const baseUrl = await startAt(app);
284284

285285
// Pre-seed a record directly via client
@@ -303,7 +303,7 @@ describe('CLI proxy tests', () => {
303303

304304
it('should allow POST (create) requests with a valid signature', async () => {
305305
const client = await createTestClient(zmodel);
306-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
306+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
307307
const baseUrl = await startAt(app);
308308

309309
const reqBody = { data: { email: 'bob@example.com' } };
@@ -327,7 +327,7 @@ describe('CLI proxy tests', () => {
327327

328328
it('should allow PUT (update) requests with a valid signature', async () => {
329329
const client = await createTestClient(zmodel);
330-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
330+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
331331
const baseUrl = await startAt(app);
332332

333333
// Seed a record
@@ -354,7 +354,7 @@ describe('CLI proxy tests', () => {
354354

355355
it('should allow signed schema endpoint', async () => {
356356
const client = await createTestClient(zmodel);
357-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
357+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
358358
const baseUrl = await startAt(app);
359359

360360
const pathWithQuery = '/api/schema';
@@ -368,9 +368,9 @@ describe('CLI proxy tests', () => {
368368
expect(body).toHaveProperty('models');
369369
});
370370

371-
it('should not require signatures when publicAPIKey is not configured', async () => {
371+
it('should not require signatures when studioAuthKey is not configured', async () => {
372372
const client = await createTestClient(zmodel);
373-
// No publicAPIKey — backwards-compatible mode
373+
// No studioAuthKey — backwards-compatible mode
374374
const app = createProxyApp(client, client.$schema);
375375
const baseUrl = await startAt(app);
376376

@@ -399,7 +399,7 @@ describe('CLI proxy tests', () => {
399399
it('should accept a raw base64 DER key (without PEM markers)', async () => {
400400
const client = await createTestClient(zmodel);
401401
// Pass the key as raw base64 DER — no PEM markers
402-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY_DER });
402+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY_DER });
403403
const baseUrl = await startAt(app);
404404

405405
const pathWithQuery = '/api/model/user/findMany';
@@ -410,16 +410,16 @@ describe('CLI proxy tests', () => {
410410
expect(r.status).toBe(200);
411411
});
412412

413-
it('should accept a key supplied via ZENSTACK_PUBLIC_KEY env variable', async () => {
413+
it('should accept a key supplied via ZENSTACK_STUDIO_AUTH_KEY env variable', async () => {
414414
const client = await createTestClient(zmodel);
415415
// createProxyApp receives the already-resolved key (as run() would pass it),
416416
// so we simulate env var resolution by passing the PEM directly.
417-
process.env['ZENSTACK_PUBLIC_KEY'] = TEST_PUBLIC_KEY;
417+
process.env['ZENSTACK_STUDIO_AUTH_KEY'] = TEST_PUBLIC_KEY;
418418
try {
419-
// No publicAPIKey option — would normally fall back to env var via run();
419+
// No studioAuthKey option — would normally fall back to env var via run();
420420
// here we verify the middleware still works when the resolved key is provided.
421421
const app = createProxyApp(client, client.$schema, {
422-
publicAPIKey: process.env['ZENSTACK_PUBLIC_KEY'],
422+
studioAuthKey: process.env['ZENSTACK_STUDIO_AUTH_KEY'],
423423
});
424424
const baseUrl = await startAt(app);
425425

@@ -430,7 +430,7 @@ describe('CLI proxy tests', () => {
430430
});
431431
expect(r.status).toBe(200);
432432
} finally {
433-
delete process.env['ZENSTACK_PUBLIC_KEY'];
433+
delete process.env['ZENSTACK_STUDIO_AUTH_KEY'];
434434
}
435435
});
436436
});
@@ -447,7 +447,7 @@ describe('CLI proxy tests', () => {
447447

448448
it('should reject a request whose timestamp is older than the tolerance window', async () => {
449449
const client = await createTestClient(zmodel);
450-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
450+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
451451
const baseUrl = await startAt(app);
452452

453453
// Timestamp 120 seconds ago — exceeds default 60-second tolerance
@@ -470,7 +470,7 @@ describe('CLI proxy tests', () => {
470470

471471
it('should reject a request whose timestamp is too far in the future', async () => {
472472
const client = await createTestClient(zmodel);
473-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
473+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
474474
const baseUrl = await startAt(app);
475475

476476
// Timestamp 120 seconds in the future — exceeds default 60-second tolerance
@@ -495,7 +495,7 @@ describe('CLI proxy tests', () => {
495495
const client = await createTestClient(zmodel);
496496
// Custom tolerance of 300 seconds
497497
const app = createProxyApp(client, client.$schema, {
498-
publicAPIKey: TEST_PUBLIC_KEY,
498+
studioAuthKey: TEST_PUBLIC_KEY,
499499
signatureToleranceSecs: 300,
500500
});
501501
const baseUrl = await startAt(app);
@@ -520,7 +520,7 @@ describe('CLI proxy tests', () => {
520520
const client = await createTestClient(zmodel);
521521
// Very tight tolerance of 5 seconds
522522
const app = createProxyApp(client, client.$schema, {
523-
publicAPIKey: TEST_PUBLIC_KEY,
523+
studioAuthKey: TEST_PUBLIC_KEY,
524524
signatureToleranceSecs: 5,
525525
});
526526
const baseUrl = await startAt(app);
@@ -554,7 +554,7 @@ describe('CLI proxy tests', () => {
554554

555555
it('should reject a valid signature if it was produced without the Authorization token', async () => {
556556
const client = await createTestClient(zmodel);
557-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
557+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
558558
const baseUrl = await startAt(app);
559559

560560
// Sign without including the auth token
@@ -580,7 +580,7 @@ describe('CLI proxy tests', () => {
580580

581581
it('should accept a request where the signature covers the Authorization token', async () => {
582582
const client = await createTestClient(zmodel);
583-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY });
583+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY });
584584
const baseUrl = await startAt(app);
585585

586586
const authToken = makeUserToken({ type: 'superUser' });
@@ -619,7 +619,7 @@ describe('CLI proxy tests', () => {
619619
const fullZmodel = extraZmodel ? `${zmodel}\n${extraZmodel}` : zmodel;
620620
const client = await createTestClient(fullZmodel);
621621
const authDb = client.$use(new PolicyPlugin());
622-
return { client, app: createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY, authDb }) };
622+
return { client, app: createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY, authDb }) };
623623
}
624624

625625
async function signedFetch(baseUrl: string, path: string, init: RequestInit = {}): Promise<Response> {
@@ -742,7 +742,7 @@ describe('CLI proxy tests', () => {
742742
`;
743743
const client = await createTestClient(fullZmodel);
744744
const authDb = client.$use(new PolicyPlugin());
745-
const app = createProxyApp(client, client.$schema, { publicAPIKey: TEST_PUBLIC_KEY, authDb });
745+
const app = createProxyApp(client, client.$schema, { studioAuthKey: TEST_PUBLIC_KEY, authDb });
746746
const baseUrl = await startAt(app);
747747

748748
// Seed users

0 commit comments

Comments
 (0)