mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-30 21:01:54 +08:00
## Summary - Fixes Sentry [STACK-BACKEND-146](https://stackframe-pw.sentry.io/issues/7377768662/): `PrismaClientKnownRequestError` P2025 on `projectUserRefreshToken.update()` during token refresh. - Root cause: `generateAccessTokenFromRefreshTokenIfValid` (`apps/backend/src/lib/tokens.tsx`) reads the refresh-token row upstream, then issues `.update(...)` on it (and on `projectUser`) inside a `Promise.all`. If a concurrent sign-out (`DELETE /auth/sessions/current`), session revoke, password change, or user deletion removes the row between the read and the update, Prisma throws P2025 and the refresh endpoint 500s. ## Changes - `apps/backend/src/lib/tokens.tsx` — swap the two `.update(...)`s for `.updateMany(...)` so a missing row is a no-op, then re-check the refresh token still exists; return `null` if it doesn't. The refresh route already maps `null` -> `KnownErrors.RefreshTokenNotFoundOrExpired` (401), which is the correct user-facing behavior for a just-revoked session. - `apps/backend/src/oauth/model.tsx` — in `generateAccessToken`, replace the "ultra-rare race condition" `throwErr` fallback with `throw new KnownErrors.RefreshTokenNotFoundOrExpired()` so concurrent sign-out during an OAuth `refresh_token` grant returns a clean 401 instead of 500. - `apps/e2e/tests/backend/endpoints/api/v1/auth/sessions/current/refresh-race.test.ts` — new regression test that fires `POST /auth/sessions/current/refresh` and `DELETE /auth/sessions/current` concurrently with the same refresh token. Before the fix it 500s on the first iteration; after, it passes in ~12s. ## Test plan - [x] New regression test passes locally. - [x] Existing `auth/sessions/**` + `auth/oauth/token.test.ts` still pass (27 tests, 3 todo, 0 failed). - [ ] CI green. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Refresh flows now detect a revoked or removed refresh token during concurrent operations and stop cleanly, preventing issuance of an access token from stale data. * A specific refresh-token-not-found/expired error is returned instead of a generic failure when refresh cannot proceed. * **Tests** * Added E2E tests exercising concurrent refresh vs sign-out to prevent race-condition crashes and validate safe handling of competing requests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|---|---|---|
| .. | ||
| ai | ||
| bulldozer | ||
| payments | ||
| cache.tsx | ||
| cel-evaluator.ts | ||
| clickhouse-errors.ts | ||
| clickhouse.tsx | ||
| config.tsx | ||
| contact-channel.tsx | ||
| dev-perf-stats.tsx | ||
| dev-request-stats.tsx | ||
| email-delivery-stats.tsx | ||
| email-drafts.tsx | ||
| email-queue-step.test.tsx | ||
| email-queue-step.tsx | ||
| email-rendering.test.tsx | ||
| email-rendering.tsx | ||
| email-template-rewrite.ts | ||
| emailable.tsx | ||
| emails-low-level.tsx | ||
| emails.tsx | ||
| end-users.tsx | ||
| events.tsx | ||
| external-db-sync-metadata.ts | ||
| external-db-sync-queue.ts | ||
| external-db-sync.ts | ||
| featurebase.tsx | ||
| images.tsx | ||
| internal-api-keys.tsx | ||
| internal-feedback-emails.tsx | ||
| js-execution.tsx | ||
| local-emulator.test.ts | ||
| local-emulator.ts | ||
| managed-email-domains.tsx | ||
| managed-email-onboarding.tsx | ||
| metrics-activity-split.ts | ||
| notification-categories.ts | ||
| oauth.tsx | ||
| openapi.tsx | ||
| payments.test.tsx | ||
| payments.tsx | ||
| permissions.tsx | ||
| preview-mode.ts | ||
| product-versions.tsx | ||
| projects.tsx | ||
| redirect-urls.test.tsx | ||
| redirect-urls.tsx | ||
| request-checks.tsx | ||
| risk-scores.tsx | ||
| seed-dummy-data.ts | ||
| session-replays.tsx | ||
| sign-up-context.ts | ||
| sign-up-heuristics.tsx | ||
| sign-up-rules.ts | ||
| stripe-proxy.tsx | ||
| stripe.tsx | ||
| telegram.tsx | ||
| tenancies.tsx | ||
| tokens.tsx | ||
| turnstile.tsx | ||
| types.tsx | ||
| upstash.tsx | ||
| users.tsx | ||
| webhooks.tsx | ||