Skip to content

AUTH-8 Project server actions - #11

Open
mahikasharma wants to merge 1 commit into
mainfrom
AUTH-8
Open

AUTH-8 Project server actions#11
mahikasharma wants to merge 1 commit into
mainfrom
AUTH-8

Conversation

@mahikasharma

Copy link
Copy Markdown
Collaborator
  • add src/actions/projects.ts for server actions for projects (create, list, get, update, delete, rotate API key)
  • API keys from crypto.randomUUID() (full key only on create/rotate)
  • delete clears related sessions and user–project rows in one transaction

@mahikasharma mahikasharma self-assigned this Apr 12, 2026

@pataniaeli pataniaeli left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review written by a Claude agent.

Ran this through the repo's mandatory pre-merge security checklist (per CLAUDE.md, anything touching credential storage needs this before merge). One framing note up front: several of the findings below only matter once these actions are actually reachable — exported and imported somewhere. If there's a follow-up ticket planned to add an authorization layer before this gets wired into any UI, deferring those specific items is a reasonable sequencing choice. I've split things below so that's an explicit, visible decision rather than an assumption either way.

Must-fix regardless of sequencing:

  • resetProjectAPIKey returns the plaintext key and there's no hashing anywhere for Project.apiKey — worth storing a hash + prefix instead of the raw value (see inline comment).
  • getProject/getProjects load the full plaintext key into memory with no select, even though the returned response is masked (see inline comment).
  • No input validation on any argument — zod is already a dependency, and e.g. name/description currently have no length bound.
  • The blanket catch {} in updateProject/deleteProject/resetProjectAPIKey conflates "not found" with a real infrastructure failure — worth narrowing to Prisma's P2025 and logging anything else.

Pending authz ticket (see framing note above):

  • None of the six actions currently check the caller's identity or project authority before mutating — see inline comments on createProject and resetProjectAPIKey for the two that matter most.

Worth its own ticket, not introduced by this PR: no RLS found on Project/Session/UserProject in the migrations. That's a second, independent path to the same data via the public anon key, orthogonal to whatever this file checks — probably worth tracking separately since it affects several of the open PRs at once.

(Edit: corrected a few inline comment anchors below that landed on the wrong line in my first pass — same content, right lines now.)

Comment thread src/actions/projects.ts
};

/** Registers an external app; returns the full API key once. */
export async function createProject(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the six actions in this file check the caller's identity before touching the DB — this one included. If there's a follow-up ticket adding an authorization layer before this gets exported into a client-facing flow, that's a reasonable way to sequence it; just flagging so it's a deliberate choice. Worth confirming before this (or the action layer that wraps it) goes live, since "use server" exports are reachable as soon as anything imports from this file.

Comment thread src/actions/projects.ts
}

/** Regenerates the API key; returns the full new key once. */
export async function resetProjectAPIKey(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one's worth calling out separately from the general authz note above: even once a caller-identity check is added, this returns the plaintext API key and there's no hashing anywhere for Project.apiKey (see the type at line 11). Worth storing a hash + short prefix instead of the raw value, so a DB read/backup/replica never yields a live downstream credential — that's independent of who's allowed to call this action.

Comment thread src/actions/projects.ts
}

/** Single project; API key is masked. */
export async function getProject(id: string): Promise<ProjectMasked | null> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getProject and getProjects (line 98) both load the full plaintext apiKey into the Node process with no select, even though the response is masked via toMasked. Not exploitable today since the raw value isn't returned, but it means the secret is unnecessarily present in memory — an easy accidental leak the day someone swaps in the raw object instead of the masked one. An explicit select that omits apiKey for these two would close it off entirely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants