Skip to content
Open
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
3 changes: 3 additions & 0 deletions apps/api/src/plugins/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ const AGENT_KEY_SECRETS = [
"OPENAI_API_KEY",
"CLAUDE_CODE_OAUTH_TOKEN",
"COPILOT_GITHUB_TOKEN",
"GEMINI_API_KEY",
"GOOGLE_CLOUD_PROJECT",
"CLAUDE_VERTEX_PROJECT_ID",
];

let _setupCompleteCache: { value: boolean; expires: number } | null = null;
Expand Down
7 changes: 6 additions & 1 deletion apps/api/src/routes/github-token.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@ describe("POST /api/github-token/rotate", () => {
const body = res.json();
expect(body.success).toBe(true);
expect(body.user).toEqual({ login: "newuser", name: "New User" });
expect(mockStoreSecret).toHaveBeenCalledWith("GITHUB_TOKEN", "ghp_newvalidtoken", "global");
expect(mockStoreSecret).toHaveBeenCalledWith(
"GITHUB_TOKEN",
"ghp_newvalidtoken",
"global",
undefined,
);
});

it("rejects an invalid token without storing it", async () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/routes/github-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export async function githubTokenRoutes(rawApp: FastifyInstance) {

const user = (await res.json()) as { login: string; name: string };

await storeSecret("GITHUB_TOKEN", token.trim(), "global");
await storeSecret("GITHUB_TOKEN", token.trim(), "global", req.user?.workspaceId);

return reply.send({
success: true,
Expand Down
6 changes: 5 additions & 1 deletion apps/api/src/routes/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,11 @@ export async function setupRoutes(rawApp: FastifyInstance) {
const [, owner, repo] = match;
const headers: Record<string, string> = { "User-Agent": "Optio" };
let repoToken: string | null = token ?? null;
if (!repoToken) repoToken = await retrieveSecret("GITHUB_TOKEN").catch(() => null);
if (!repoToken) {
repoToken = await retrieveSecret("GITHUB_TOKEN", "global", req.user?.workspaceId).catch(
() => null,
);
}
if (!repoToken && isGitHubAppConfigured()) {
repoToken = await getInstallationToken().catch(() => null);
}
Expand Down
4 changes: 4 additions & 0 deletions apps/api/src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,16 @@ describe("error handler", () => {

describe("Zod error sanitization", () => {
const originalNodeEnv = process.env.NODE_ENV;
const originalAuthDisabled = process.env.OPTIO_AUTH_DISABLED;

afterEach(() => {
process.env.NODE_ENV = originalNodeEnv;
process.env.OPTIO_AUTH_DISABLED = originalAuthDisabled;
});

it("returns field names only for Zod errors in production", async () => {
process.env.NODE_ENV = "production";
process.env.OPTIO_AUTH_DISABLED = "true";
const testApp = await buildServer();

// Use /api/setup/ path to bypass auth middleware
Expand Down Expand Up @@ -240,6 +243,7 @@ describe("Zod error sanitization", () => {

it("returns full Zod error details in development", async () => {
process.env.NODE_ENV = "development";
process.env.OPTIO_AUTH_DISABLED = "true";
const testApp = await buildServer();

testApp.post("/api/setup/test-zod-dev", async () => {
Expand Down
Loading