Skip to content

Commit d8254f5

Browse files
ekremneyclaude
andauthored
test(it): add JWT auth for PostgREST mutation access control (#1875)
## Summary Adapts IT tests for [mysticat-data-service PR #92](adobe/mysticat-data-service#92) which introduces the `postgrest_writer` role for JWT-based mutation access control. - Add `postgrest-jwt.js` utility for HS256 JWT generation (mirrors `_make_jwt()` from mysticat-data-service) - Configure `PGRST_JWT_SECRET` in docker-compose.yml - Pass `POSTGREST_API_KEY` to dev server for postgres mode - Bump mysticat-data-service image to v1.13.0 After this change, DELETE and UPDATE operations through PostgREST require JWT authentication, while SELECT and INSERT remain anonymous. ## Test plan - [x] PostgreSQL IT tests pass (277 passing) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9468258 commit d8254f5

File tree

3 files changed

+73
-1
lines changed

3 files changed

+73
-1
lines changed

test/it/env.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13+
import { POSTGREST_WRITER_JWT } from './shared/postgrest-jwt.js';
14+
1315
/**
1416
* Builds the full mandatory env matrix for the IT dev server.
1517
*
@@ -77,6 +79,7 @@ export function buildEnv(mode, publicKeyB64) {
7779
DATA_SERVICE_PROVIDER: 'postgres',
7880
POSTGREST_URL: 'http://localhost:3300',
7981
POSTGREST_SCHEMA: 'public',
82+
POSTGREST_API_KEY: POSTGREST_WRITER_JWT,
8083
});
8184
}
8285

test/it/postgres/docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ services:
2626
retries: 30
2727

2828
data-service:
29-
image: 682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service:v1.11.0
29+
image: 682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service:v1.13.0
3030
container_name: spacecat-it-data-service
3131
depends_on:
3232
db:
@@ -39,6 +39,7 @@ services:
3939
PGRST_DB_ANON_ROLE: postgrest_anon
4040
PGRST_DB_EXTRA_SEARCH_PATH: ""
4141
PGRST_LOG_LEVEL: warn
42+
PGRST_JWT_SECRET: local-dev-jwt-secret-for-postgrest-only
4243
PGRST_ADMIN_SERVER_PORT: 3001
4344
PGRST_OPENAPI_SERVER_PROXY_URI: "http://localhost:${IT_POSTGREST_PORT:-3300}"
4445
command: ["sh", "-c", "dbmate -d db/migrations up && exec postgrest"]

test/it/shared/postgrest-jwt.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
/**
14+
* PostgREST JWT utilities for IT tests.
15+
*
16+
* Creates minimal HS256 JWTs for PostgREST authentication.
17+
* Mirrors the _make_jwt() function from mysticat-data-service/tests/conftest.py
18+
*
19+
* After PR #92 (postgrest_writer role), DELETE and UPDATE operations require
20+
* JWT authentication. The dev server uses POSTGREST_API_KEY to authenticate
21+
* to PostgREST via @adobe/spacecat-shared-data-access.
22+
*/
23+
24+
import crypto from 'crypto';
25+
26+
// Must match PGRST_JWT_SECRET in docker-compose.yml
27+
export const POSTGREST_JWT_SECRET = 'local-dev-jwt-secret-for-postgrest-only';
28+
29+
/**
30+
* Base64url encode (RFC 4648 §5).
31+
* @param {string|Buffer} data - Data to encode
32+
* @returns {string} Base64url-encoded string
33+
*/
34+
function base64url(data) {
35+
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
36+
return buf
37+
.toString('base64')
38+
.replace(/\+/g, '-')
39+
.replace(/\//g, '_')
40+
.replace(/=+$/, '');
41+
}
42+
43+
/**
44+
* Creates a minimal HS256 JWT for PostgREST authentication.
45+
*
46+
* @param {string} secret - JWT secret (must match PGRST_JWT_SECRET)
47+
* @param {string} role - PostgreSQL role to assume (e.g., 'postgrest_writer')
48+
* @returns {string} Signed JWT token
49+
*/
50+
export function makePostgrestJwt(secret, role) {
51+
const header = base64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
52+
const payload = base64url(JSON.stringify({ role }));
53+
const signature = base64url(
54+
crypto.createHmac('sha256', secret)
55+
.update(`${header}.${payload}`)
56+
.digest(),
57+
);
58+
return `${header}.${payload}.${signature}`;
59+
}
60+
61+
/**
62+
* Pre-generated JWT for postgrest_writer role.
63+
* Used by the dev server to authenticate DELETE/UPDATE operations to PostgREST.
64+
*/
65+
export const POSTGREST_WRITER_JWT = makePostgrestJwt(
66+
POSTGREST_JWT_SECRET,
67+
'postgrest_writer',
68+
);

0 commit comments

Comments
 (0)