Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions tools/haip-connect-gpt/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ PORT=8080
# deployment domain if left unset; set it to your production domain for a stable spec.
PUBLIC_BASE_URL=http://localhost:8080

# --- Caller authentication (REQUIRED unless GATEWAY_ALLOW_PUBLIC=true) ---
# Credential the ChatGPT Action must present (Authorization: Bearer <key>, or
# x-api-key). The gateway holds HAIP's privileged Connect key, so leaving the
# gateway open lets anyone who finds the URL drive the Connect API. Set this here
# AND in the ChatGPT Action's Authentication (API Key / Bearer) config.
# --- Caller authentication (optional; OPEN demo when unset) ---
# This is a demo/template gateway: with no key set it is OPEN (the public demo so
# hotels can see how to connect their own GPT). To LOCK IT DOWN for a real
# deployment, set GATEWAY_API_KEY to a strong secret — then every action request
# must present it (Authorization: Bearer <key>, or x-api-key), and you put the
# same value in the ChatGPT Action's Authentication (API Key / Bearer) config.
# The gateway holds HAIP's privileged Connect key, so set this in production.
GATEWAY_API_KEY=
# Explicit opt-out to keep the action routes public (the unauthenticated demo).
# Leave unset/false in any real deployment.
GATEWAY_ALLOW_PUBLIC=false

# --- Tool-call logging (Supabase project: haip-demo, direct Postgres) ---
# Connection string for the haip_tool_calls table. Leave blank to disable
Expand Down
1 change: 0 additions & 1 deletion tools/haip-connect-gpt/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const app = buildApp({
adapter: new HaipConnectAdapter({ baseUrl, apiKey }),
publicBaseUrl,
gatewayApiKey: process.env['GATEWAY_API_KEY'],
allowPublic: process.env['GATEWAY_ALLOW_PUBLIC'] === 'true',
});
const ready = app.ready();

Expand Down
9 changes: 1 addition & 8 deletions tools/haip-connect-gpt/src/app.auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,9 @@ describe('gateway authentication', () => {
await app.close();
});

it('fails closed: no key configured and not explicitly public → 401', async () => {
it('is open when no key is configured (demo posture)', async () => {
const app = buildApp({ adapter: stubAdapter(), publicBaseUrl: 'http://x' });
const res = await app.inject({ method: 'POST', url: '/hotels/search', payload: {} });
expect(res.statusCode).toBe(401);
await app.close();
});

it('explicit opt-out (allowPublic) keeps the demo open', async () => {
const app = buildApp({ adapter: stubAdapter(), publicBaseUrl: 'http://x', allowPublic: true });
const res = await app.inject({ method: 'POST', url: '/hotels/search', payload: {} });
expect(res.statusCode).toBe(200);
await app.close();
});
Expand Down
19 changes: 8 additions & 11 deletions tools/haip-connect-gpt/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ export interface AppOptions {
* internet who finds the URL can drive the Connect API. Configure via GATEWAY_API_KEY.
*/
gatewayApiKey?: string;
/**
* Explicit opt-out that leaves the action routes public (the unauthenticated
* demo). Mirrors HAIP_ALLOW_INSECURE — secure-by-default otherwise.
*/
allowPublic?: boolean;
}

/** Public routes that never require a caller credential. */
Expand Down Expand Up @@ -64,16 +59,18 @@ export function buildApp(opts: AppOptions): FastifyInstance {

app.register(cors, { origin: true });

// Require a caller credential on every non-public route. The gateway proxies
// requests with HAIP's privileged upstream `x-api-key`, so an unauthenticated
// gateway is an open door to the Connect API. Fail-closed: if no key is
// configured and `allowPublic` was not explicitly set, refuse action routes.
// Caller auth on the action routes. This is a demo/template gateway, so it is
// OPEN by default — the public demo shows hotels how to connect their own GPT
// with zero setup. A real deployment locks it down simply by setting
// GATEWAY_API_KEY: when a key is configured it is REQUIRED (Authorization:
// Bearer <key> or x-api-key), validated timing-safe. The gateway holds HAIP's
// upstream Connect key, so anyone enabling this in production should set a key.
app.addHook('onRequest', async (req, reply) => {
if (!opts.gatewayApiKey) return; // no key configured → open (demo posture)
const path = (req.url.split('?')[0] ?? req.url).replace(/\/+$/, '') || '/';
if (PUBLIC_PATHS.has(path)) return;
if (opts.allowPublic) return;
const provided = extractCredential(req);
if (!opts.gatewayApiKey || !provided || !timingSafeEqualStr(provided, opts.gatewayApiKey)) {
if (!provided || !timingSafeEqualStr(provided, opts.gatewayApiKey)) {
reply.code(401).send({ error: 'unauthorized', message: 'A valid gateway credential is required.' });
}
});
Expand Down
1 change: 0 additions & 1 deletion tools/haip-connect-gpt/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const app = buildApp({
adapter,
publicBaseUrl,
gatewayApiKey: process.env['GATEWAY_API_KEY'],
allowPublic: process.env['GATEWAY_ALLOW_PUBLIC'] === 'true',
});

app
Expand Down
Loading