Skip to content

Commit e9d5272

Browse files
authored
[CRCR] Use standard checkAuthWithApiToken for HUD API auth (#8144)
## Summary After #8143, the CRCR relay Lambda sends the standard `x-hud-internal-bot` header (with `HUD_BOT_KEY`) instead of the custom `X-OOT-Relay-Token` header. This PR uses the standard `checkAuthWithApiToken()` from `lib/auth/auth.ts` — the same function used by other HUD internal APIs (e.g. `/api/benchmark/get_time_series`). **What changes:** - **`results.ts`**: Replaces the custom 10-line auth block (crypto import, `timingSafeEqual`, manual env var check) with a 3-line call to `checkAuthWithApiToken()` - **`ootResults.test.ts`**: Mocks `checkAuthWithApiToken` instead of manually setting env vars and headers for each auth test case **Why this works:** - `checkAuthWithApiToken()` checks the `x-hud-internal-bot` header against `process.env.INTERNAL_API_TOKEN` — exactly what the relay now sends - `INTERNAL_API_TOKEN` is already provisioned on Vercel (no new secrets needed) - `OOT_RELAY_TOKEN` was never provisioned — this eliminates a dead env var dependency ## Test plan - [ ] Confirm relay Lambda's `HUD_BOT_KEY` (in Secrets Manager) matches Vercel's `INTERNAL_API_TOKEN` - [ ] Trigger a CRCR dispatch from `pytorch/crcr-test` and verify the callback reaches HUD without 401 CC @groenenboomj @jewelkm89
1 parent d45eb38 commit e9d5272

2 files changed

Lines changed: 13 additions & 48 deletions

File tree

torchci/pages/api/oot/results.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { timingSafeEqual } from "crypto";
1+
import { checkAuthWithApiToken } from "lib/auth/auth";
22
import {
33
ApiError,
44
extractDynamoRecord,
@@ -24,18 +24,8 @@ export default async function handler(
2424
}
2525

2626
try {
27-
// 1. Auth: dedicated X-OOT-Relay-Token header (timing-safe comparison)
28-
const expected = process.env.OOT_RELAY_TOKEN;
29-
if (!expected) {
30-
return res.status(500).json({ error: "Server misconfigured" });
31-
}
32-
const raw = req.headers["x-oot-relay-token"];
33-
if (typeof raw !== "string") {
34-
return res.status(401).json({ error: "Unauthorized" });
35-
}
36-
const a = new Uint8Array(Buffer.from(raw));
37-
const b = new Uint8Array(Buffer.from(expected));
38-
if (a.length !== b.length || !timingSafeEqual(a, b)) {
27+
const auth = await checkAuthWithApiToken(req, res);
28+
if (!auth.ok) {
3929
return res.status(401).json({ error: "Unauthorized" });
4030
}
4131

torchci/test/ootResults.test.ts

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { NextApiRequest, NextApiResponse } from "next";
2+
import * as authModule from "../lib/auth/auth";
23
import * as ootUtils from "../lib/oot/ootUtils";
34
import handler from "../pages/api/oot/results";
45

@@ -10,12 +11,16 @@ jest.mock("../lib/oot/ootUtils", () => {
1011
};
1112
});
1213

13-
const VALID_TOKEN = "test-relay-token-abc123";
14+
jest.mock("../lib/auth/auth", () => ({
15+
checkAuthWithApiToken: jest.fn(),
16+
}));
17+
18+
const mockCheckAuth = authModule.checkAuthWithApiToken as jest.Mock;
1419

1520
function mockReq(overrides: Partial<NextApiRequest> = {}): NextApiRequest {
1621
return {
1722
method: "POST",
18-
headers: { "x-oot-relay-token": VALID_TOKEN },
23+
headers: { "x-hud-internal-bot": "valid-token" },
1924
body: {
2025
trusted: {
2126
verified_repo: "Ascend/pytorch",
@@ -64,15 +69,9 @@ function mockRes(): NextApiResponse & { _status: number; _json: any } {
6469
}
6570

6671
describe("POST /api/oot/results", () => {
67-
const originalEnv = process.env;
68-
6972
beforeEach(() => {
70-
process.env = { ...originalEnv, OOT_RELAY_TOKEN: VALID_TOKEN };
7173
jest.clearAllMocks();
72-
});
73-
74-
afterAll(() => {
75-
process.env = originalEnv;
74+
mockCheckAuth.mockResolvedValue({ ok: true, type: "header" });
7675
});
7776

7877
test("rejects non-POST methods with 405", async () => {
@@ -82,34 +81,10 @@ describe("POST /api/oot/results", () => {
8281
expect(res._json.error).toBe("Method not allowed");
8382
});
8483

85-
test("returns 500 when OOT_RELAY_TOKEN env is not set", async () => {
86-
delete process.env.OOT_RELAY_TOKEN;
84+
test("returns 401 when auth fails", async () => {
85+
mockCheckAuth.mockResolvedValue({ ok: false });
8786
const res = mockRes();
8887
await handler(mockReq(), res);
89-
expect(res._status).toBe(500);
90-
expect(res._json.error).toBe("Server misconfigured");
91-
});
92-
93-
test("returns 401 when token header is missing", async () => {
94-
const res = mockRes();
95-
await handler(mockReq({ headers: {} }), res);
96-
expect(res._status).toBe(401);
97-
expect(res._json.error).toBe("Unauthorized");
98-
});
99-
100-
test("returns 401 when token header is wrong", async () => {
101-
const res = mockRes();
102-
await handler(
103-
mockReq({ headers: { "x-oot-relay-token": "wrong-token" } }),
104-
res
105-
);
106-
expect(res._status).toBe(401);
107-
expect(res._json.error).toBe("Unauthorized");
108-
});
109-
110-
test("returns 401 when token has different length", async () => {
111-
const res = mockRes();
112-
await handler(mockReq({ headers: { "x-oot-relay-token": "short" } }), res);
11388
expect(res._status).toBe(401);
11489
expect(res._json.error).toBe("Unauthorized");
11590
});

0 commit comments

Comments
 (0)