Skip to content

feat(user-api): User API — platform accounts & self-service API keys (backend)#2044

Open
brunod-e wants to merge 23 commits into
devfrom
feat/user-api
Open

feat(user-api): User API — platform accounts & self-service API keys (backend)#2044
brunod-e wants to merge 23 commits into
devfrom
feat/user-api

Conversation

@brunod-e

@brunod-e brunod-e commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

What

The entire backend for platform accounts, consolidated into one PR (was #2044 + #2046 + #2047):

New service: apps/user-api (:4003)

  • better-auth sessions: SIWE (DB-backed single-use nonces, EIP-1271/ERC-6492 via publicClient.verifyMessage), magic link (Resend, env-gated) and Google OAuth (env-gated). No account linking in v1 — wallet and email/Google logins are separate users by design.
  • Per-host auth instances derived from AUTH_SIWE_DOMAINS (one list drives SIWE domain, baseURL, cookies/CSRF and trusted origins) — no BETTER_AUTH_URL; whitelabel domains just get added to the list. The dashboard proxy forwards the browser host via x-anticapture-host (Railway's edge overwrites x-forwarded-*).
  • GET /auth/methods: public capability discovery so the frontend only offers sign-in methods this deployment actually serves.
  • User-scoped drafts: identity from the session, daoId column filter, server-generated ids, public share GET with server-derived isOwner, claim-on-first-SIWE-login for migrated rows, 100 drafts/user cap.
  • Self-service API keys (DEV-950): minted through Authful under a per-user user:<id> tenant, unbounded rate (rateLimitPerMin: 0 — free & unlimited for now), plaintext shown once, quota of 10 keys/user, lastUsedAt enrichment best-effort.

Breaking: apps/api

  • Draft-proposal routes/service/repository/mapper and general-schema removed — drafts belong to the User API now. One-shot migration script (apps/user-api/scripts/migrate-drafts.ts, --dry-run supported) preserves ids and lowercases author addresses.

apps/authful

  • Scoped provisioning key: may only mint/revoke user:* tenants and list a single user:* tenant via ?tenant=; admin key unrestricted. Timing-safe key compare; foreign tenants answer 404 (no oracle).

Infra

  • infra/user-api Dockerfile + railway.json (migrate-then-serve entrypoint, /health healthcheck); scripts/dev.sh lane so pnpm dev boots the user-api locally.

Architecture note

The User API is reached only via the dashboard's same-origin /api/user proxy (cookie sessions) — it does not sit behind Gateful. Bearer/API-key traffic keeps flowing through Gateful; user-api → Authful runs over the Railway private network.

Testing

  • 30 integration/unit tests green (PGlite + drizzle pushSchema; offline EOA verifier).
  • Deployed to Railway dev and exercised E2E through the local dashboard proxy: SIWE nonce → verify → session cookie → create key (201 + plaintext) → list (no plaintext) → revoke (204).

Frontend counterpart: the stacked dashboard PR (base = this branch).
Replaces #2046 and #2047, which are closed in favor of this consolidation.

🤖 Generated with Claude Code

brunod-e and others added 5 commits July 9, 2026 20:57
New apps/user-api service — the user identity & session layer decided in
the auth architecture discussion. Reached only via the dashboard
/api/user proxy (HTTP-only cookies), outside Gateful.

This first increment is the foundation:
- better-auth wired with the SIWE plugin; EOA + EIP-1271/ERC-6492
  (smart-contract wallets) verified via viem.
- One better-auth instance per host in AUTH_SIWE_DOMAINS so main and
  whitelabel domains each sign SIWE against their real host; unlisted
  hosts fail closed.
- Own Postgres database (default public schema) — better-auth core +
  SIWE walletAddress tables generated via the better-auth CLI, drizzle
  migration emitted.
- Hono app with health (DB probe) and Prometheus metrics, mirroring the
  authful service layout.

Follow-ups (separate increments): drafts moved to user scope, the
/api/user Next proxy, dashboard login modal + drafts rewire, Authful
provisioning for user API keys, Google OAuth + magic link.

Refs DEV-978, DEV-979, DEV-950.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drafts move off the DAO APIs into the User API, keyed by userId with a
daoId filter column — this structurally kills the DEV-978 hijack and
DEV-979 enumeration (identity comes only from the session; there is no
address parameter to spoof).

- drafts table: server-generated UUID (the id is the share capability, so
  a client-chosen id would mint guessable links), nullable userId +
  authorAddress for migrated rows, jsonb actions, bigint ms timestamps.
- Endpoints (Hono + zod-openapi): list (session-scoped, daoId filter),
  create (author = session, id server-side, 100/user quota), public
  share GET (isOwner derived from the session — replaces the dashboard's
  author-vs-wallet comparison), owner-only update/delete via a compound
  WHERE, identical 404s for foreign and missing rows (no existence oracle).
- claim-on-first-login: migrated rows (userId NULL) are adopted by
  matching the user's SIWE wallet address, case-insensitively.
- auth refactored to a dependency-injected createAuthResolver so the whole
  ceremony is testable offline.
- 12 integration tests over the real SIWE nonce->verify->cookie->CRUD
  chain on in-memory PGlite: unauth 401, untrusted-host 400, ownership
  404s, share isOwner per session, quota 403, migration claim.

Refs DEV-978, DEV-979.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Behind the dashboard's /api/user proxy the Host header is the internal
service host, never a configured SIWE domain. Resolve the per-host
better-auth instance (and fail-closed check) from x-forwarded-host,
falling back to Host for direct calls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both methods are wired but activate only when configured, so the service
stays SIWE-only out of the box:
- magic link: enabled when RESEND_API_KEY is set; email delivered via
  Resend (mirrors the dashboard's existing Resend usage), sender from
  RESEND_FROM_EMAIL. Uses better-auth's verification table — no new schema.
- Google OAuth: enabled when GOOGLE_CLIENT_ID/SECRET are set.
- No account linking (product decision): wallet, Google and email logins
  are separate users.
Integration tests cover the magic-link endpoint firing the injected sender
and being absent when unconfigured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
anticapture-storybook Ready Ready Preview, Comment Jul 17, 2026 12:40pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
anticapture Ignored Ignored Jul 17, 2026 12:40pm

Request Review

@isadorable-png isadorable-png 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.

🎨 UI Review

Automated review · scope check only

This PR adds a new backend-only service, apps/user-api (better-auth SIWE/magic-link/Google sessions, user-scoped drafts — controllers, repositories, services, database schema, integration tests). No .tsx/component/style files, no dashboard integration in this PR — the dashboard counterpart ships separately in #2045. No rendered UI surface. Out of scope for a UI review; no findings to report.


Generated by Claude Code

This comment was marked as resolved.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83e8429765

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/user-api/src/services/drafts/index.ts Outdated
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🔍 Vercel preview: https://anticapture-l33swexbl-ful.vercel.app

@railway-app

railway-app Bot commented Jul 10, 2026

Copy link
Copy Markdown

🚅 Deployed to the anticapture-pr-2044 environment in anticapture-infra

Service Status Web Updated (UTC)
tempo ✅ Success (View Logs) Jul 17, 2026 at 12:41 pm
mcp-server ✅ Success (View Logs) Jul 17, 2026 at 12:40 pm
otelcol ✅ Success (View Logs) Jul 17, 2026 at 12:39 pm
authful ✅ Success (View Logs) Web Jul 17, 2026 at 12:38 pm
grafana ✅ Success (View Logs) Web Jul 17, 2026 at 12:38 pm
loki ✅ Success (View Logs) Web Jul 17, 2026 at 12:38 pm
prometheus ✅ Success (View Logs) Web Jul 17, 2026 at 12:38 pm
gateful ✅ Success (View Logs) Web Jul 17, 2026 at 12:38 pm
alertmanager ✅ Success (View Logs) Web Jul 17, 2026 at 12:38 pm
aave-api ✅ Success (View Logs) Jul 13, 2026 at 11:35 am
fluid-api ✅ Success (View Logs) Jul 13, 2026 at 11:35 am
shutter-api ✅ Success (View Logs) Jul 13, 2026 at 11:34 am
obol-api ✅ Success (View Logs) Jul 13, 2026 at 11:34 am
uniswap-api ✅ Success (View Logs) Jul 13, 2026 at 11:34 am
compound-api ✅ Success (View Logs) Jul 13, 2026 at 11:34 am
tornado-api ✅ Success (View Logs) Jul 13, 2026 at 11:34 am
ens-api ✅ Success (View Logs) Jul 13, 2026 at 11:33 am
nouns-api ✅ Success (View Logs) Jul 13, 2026 at 11:33 am
scroll-api ✅ Success (View Logs) Jul 13, 2026 at 11:33 am
gitcoin-api ✅ Success (View Logs) Jul 13, 2026 at 11:33 am
lil-nouns-api ✅ Success (View Logs) Jul 13, 2026 at 11:33 am
compound-indexer-offchain ✅ Success (View Logs) Jul 10, 2026 at 9:52 am
ens-indexer-offchain ✅ Success (View Logs) Jul 10, 2026 at 9:52 am
gitcoin-indexer-offchain ✅ Success (View Logs) Jul 10, 2026 at 9:52 am
mcp-docs ✅ Success (View Logs) Jul 10, 2026 at 9:52 am
uniswap-indexer-offchain ✅ Success (View Logs) Jul 10, 2026 at 9:52 am
mcp-proxy ✅ Success (View Logs) Web Jul 10, 2026 at 9:52 am
nodeful 🚨 Crashed (View Logs) Jul 10, 2026 at 12:42 am
address-enrichment ✅ Success (View Logs) Web Jul 10, 2026 at 12:38 am
uniswap-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
gitcoin-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
lil-nouns-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
nouns-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
shutter-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
ens-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
scroll-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
aave-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
obol-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
compound-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
fluid-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
tornado-indexer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
ens-relayer ✅ Success (View Logs) Jul 10, 2026 at 12:38 am
erpc ✅ Success (View Logs) Web Jul 10, 2026 at 12:38 am

Mirrors the authful service (same Hono + drizzle stack): multi-stage
turbo-pruned build, /health healthcheck, and an entrypoint that runs
db:migrate before serving so the schema is applied on each deploy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c457bb2008

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/user-api/src/auth.ts Outdated
A managed edge (Railway) in front of this service overwrites x-forwarded-*
with its own host, so the dashboard proxy's forwarded host was lost and
every request 400'd as untrusted_host. Prefer the custom x-anticapture-host
header the proxy now sets; keep x-forwarded-host as the edge-less fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BETTER_AUTH_URL is the dashboard origin, not the /api/user proxy path —
a path there makes better-auth mount at /api/user/api/auth/* and 404
everything (the proxy strips /api/user; the service serves /api/auth via
basePath). Documents the x-anticapture-host + PORT=4003 deploy notes,
drops the stale Identful name, and force-adds .env.example to match the
other services (all tracked despite the .env* ignore).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d9b3e25c22

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/user-api/src/services/api-keys/index.ts
updateUserInfoOnLink: a Google sign-in linking onto a magic-link-born
user (same email) now carries the Google name/photo onto the local
profile, so the account chip shows the picture regardless of which
method came first. Local email/emailVerified are never overwritten.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- drop the never-recorded auth ceremony counter (dead metric surface)
- share ErrorResponseSchema + unauthorizedResponses in mappers/errors
  instead of duplicating the block and cross-importing from drafts
- build all auth instances in one uniform pass (no primary special case)
- run the api-keys DB read and Authful usage lookup concurrently
- skip the drafts claim UPDATE once a user is settled (process-local
  memo; the claimable set is fixed pre-migration rows only)
- validate migration addresses with viem's isAddress (non-strict)
- csv env helper: plain schema, not a zero-arg factory

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 20f461b806

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/user-api/src/services/drafts/index.ts Outdated

@pikonha pikonha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

isn't this supposed to remove the drafts from the APIs?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wouldn't commit this given that it will be used only once

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It can't run before the prod cutover happens, so deleting it now would orphan the migration plan: this is the documented, tested path that moves general.proposal_drafts from each DAO DB into the user-api when the accounts release ships. Plan: run it once per DAO at cutover, then delete script + test in the cleanup PR. If you'd rather keep the tree clean now and run it from a branch at cutover time, happy to remove — your call.

Comment thread scripts/dev.sh
Comment thread apps/user-api/package.json Outdated
Comment thread apps/user-api/package.json
Comment thread apps/user-api/drizzle.config.ts
Comment thread apps/user-api/src/controllers/api-keys/index.ts
const { key, plaintext } = await service.create(userId, label);
return c.json(
CreatedApiKeyResponseSchema.parse({
...toResponse(key),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

again, why do you need this instead of relying on the zod schema

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Same seam as above: the schema validates the wire shape; the mapper converts driver types (Date→ISO). Without it, parse() rejects the rows.

Comment thread apps/user-api/src/controllers/drafts/index.ts
Comment thread apps/user-api/src/controllers/drafts/index.ts Outdated
Comment thread apps/user-api/src/controllers/drafts/index.ts Outdated
Comment thread .github/workflows/tests.yaml
Comment thread apps/user-api/src/services/drafts/index.ts Outdated
Comment thread apps/user-api/src/services/drafts/index.ts Outdated
Comment thread apps/user-api/src/services/drafts/index.ts
Comment thread apps/user-api/src/email/magic-link.ts
Comment thread apps/user-api/src/middlewares/logger.ts Outdated
Comment thread apps/authful/src/index.ts
Comment thread apps/user-api/src/database/index.ts Outdated
Comment thread apps/user-api/drizzle/0000_lazy_lake.sql
Comment thread .claude/skills/local-dev-stack/SKILL.md Outdated
codex P2s:
- authful client trims trailing slashes off AUTHFUL_URL (hono 404s
  doubled paths)
- blank AUTHFUL_URL / AUTHFUL_PROVISIONING_API_KEY count as unset so a
  SIWE-only boot can't fail on an empty env template value
- drop the drafts claim memo: the one-shot migration can insert
  claimable rows after a user was first seen, so claim-on-touch must
  stay idempotent per request
- authful token name is a bounded constant (not the user label) and
  gateful buckets user:* tenants in usage metrics — no unbounded
  Prometheus series from self-service keys
- gateful caches user:* positive verdicts for 30s (was 300s) so a
  dashboard revoke propagates in seconds; changeset added
- preview deploys receive USER_API_URL via --env (branch-scoped
  upserts don't apply to CLI deployments)

review feedback (pikonha):
- rename DraftsService -> ProposalDraftsService; clearer claim docs
- listProposalDrafts operationId + proposal-drafts description
- runtime db pool gets connection/statement timeouts
- drop the duplicate test:unit script; .env.example keygen hint and
  deduped optional-var blocks; simpler request log lines; skill table

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b1c3c33eb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/user-api/src/services/api-keys/index.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d39173e50f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +47 to +51
daoId: z.string().min(1),
title: z.string().default(""),
discussionUrl: z.string().default(""),
body: z.string().default(""),
actions: z.array(ActionSchema).default([]),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bound persisted draft payload sizes

Any signed-in user can submit arbitrarily large title, discussionUrl, body, and actions values, and these values are persisted directly while the quota only limits the number of rows. Because SIWE and optional email accounts are self-service, a user can consume excessive request memory and database storage despite the 100-draft cap; add field/array limits and enforce an overall request-body limit.

Useful? React with 👍 / 👎.

Comment on lines +48 to +50
if (safeEqual(token, opts.adminApiKey)) {
c.set("authScope", "admin");
return next();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject collisions between admin and provisioning keys

When PROVISIONING_API_KEY is accidentally configured to the same value as ADMIN_API_KEY, this first branch classifies every User API provisioning request as admin, bypassing the controller's user:* restrictions and allowing that credential to mint, list, or revoke first-party tokens. Since the separate credential is intended to be a security boundary, reject equal values during environment validation.

Useful? React with 👍 / 👎.

@@ -0,0 +1,364 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore type safety in integration tests

This file-wide suppression enables numerous as any casts for decoded responses and database setup, allowing API-contract mistakes to compile unnoticed. This directly violates the root AGENTS.md boundary against casting to any/unknown and .claude/skills/testing/SKILL.md, which says test code must never use unsafe casts; replace the blanket suppression with typed response schemas and properly typed test adapters.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd6bfb47e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -0,0 +1,259 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace blanket any suppression in API-key tests

This file-wide suppression permits the response and database as any casts throughout the suite, so API-contract changes can compile without the tests noticing them. This violates both the root AGENTS.md boundary, “Cast types to any/unknown unless explicitly asked,” and .claude/skills/testing/SKILL.md Rule 9, which requires test code to avoid unsafe casts; validate responses with the route schemas and use correctly typed test adapters instead.

Useful? React with 👍 / 👎.

return this.db
.select()
.from(tokens)
.where(eq(tokens.tenant, tenant))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude revoked tokens from usage enrichment

When a user repeatedly creates and revokes keys, every revoked Authful row remains stored and this tenant query returns the entire history, while ApiKeysService.list downloads it on every GET /me/api-keys even though only the at-most-ten locally active keys need usage data. The response and map therefore grow without bound under normal key rotation or deliberate churn; use a bounded active-token query or pagination for this enrichment path.

Useful? React with 👍 / 👎.

Comment thread apps/user-api/src/auth.ts
Comment on lines +201 to +203
const dynamic = createAuth(config, host);
if (instances.size < MAX_DYNAMIC_INSTANCES) instances.set(host, dynamic);
return dynamic;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject new preview hosts after reaching the cache limit

When preview mode is enabled and the instance map reaches MAX_DYNAMIC_INSTANCES, an unseen valid *.vercel.app host still causes createAuth to run, but the result is not cached. Repeated requests for such a host therefore rebuild a complete Better Auth instance on every request, defeating the stated scanner-junk guard and allowing preview traffic to consume excessive CPU; check the limit before construction or evict an existing cached entry.

Useful? React with 👍 / 👎.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants