-
Notifications
You must be signed in to change notification settings - Fork 469
Convex implementation #913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds Convex integration (examples, docs, CI, tests), centralizes JWT access-token payload construction/decoding to use a validated AccessTokenPayload (prefers is_anonymous with legacy fallback), introduces partial-user APIs and Convex auth helpers in the template SDK, and adds a generic paginated-list utility. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Browser
participant NextApp as Next.js App
participant StackClient as StackClientApp
participant ConvexClient as ConvexReactClient
participant ConvexServer as Convex Server
Browser->>NextApp: load page (client)
NextApp->>StackClient: getConvexClientAuth() → token getter
NextApp->>ConvexClient: new ConvexReactClient({ auth: getter })
ConvexClient->>StackClient: invoke token getter (forceRefresh?)
StackClient->>StackClient: getAccessTokenIfNotExpiredYet(minMs)
alt cached token valid
StackClient-->>ConvexClient: return Bearer <JWT>
else cached token missing/near-expiry
StackClient->>StackClient: refresh/fetch token
StackClient-->>ConvexClient: return Bearer <JWT>
end
ConvexClient->>ConvexServer: Connect / Authenticate(Bearer <JWT>)
ConvexServer-->>ConvexClient: Ack
sequenceDiagram
autonumber
participant Caller
participant InternalSession
participant Cache
participant AuthService
Caller->>InternalSession: getAccessTokenIfNotExpiredYet(minMs)
InternalSession->>Cache: read stored access token
alt token exists and (exp - now) >= minMs (bounded by 60s)
Cache-->>InternalSession: token
InternalSession-->>Caller: AccessToken.payload (validated)
else
InternalSession-->>Caller: null
Caller->>InternalSession: getOrFetchLikelyValidTokens(...)
InternalSession->>AuthService: request new token
AuthService-->>InternalSession: new token
InternalSession->>Cache: store token
InternalSession-->>Caller: token
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this c 8000 omment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Overview
Summary
This PR implements Convex integration for Stack Auth, adding support for using Stack Auth's JWT tokens with Convex's real-time backend platform. The implementation includes:Core Integration Features:
- New
getConvexProvidersConfig()
function that provides JWT configuration for Convex authentication using Stack's issuer and JWKS endpoints - Client-side authentication helpers (
getConvexClientAuth
,getConvexHttpClientAuth
) that enable Convex clients to use Stack Auth tokens - Support for partial user data retrieval (
getPartialUser
,usePartialUser
) with both token-based and Convex context sources - New user types (
TokenPartialUser
,SyncedPartialUser
) for optimized data transfer between Stack and external systems
JWT Token Structure Changes:
- Anonymous users now have role 'authenticated' instead of 'anon' with a new
is_anonymous: boolean
field for cleaner authorization logic - Enhanced JWT payload validation with structured schema validation replacing direct decoding
- Updated access token generation to use typed
AccessTokenPayload
structure
Type System Improvements:
- Renamed
GetUserOptions
toGetCurrentUserOptions
throughout the codebase for better semantic clarity - Added
GetCurrentPartialUserOptions
type supporting both token and Convex context sources - New server/client user type variants for different integration scenarios
Developer Experience:
- Complete example application in
examples/convex/
demonstrating Stack Auth + Convex integration with Next.js - Comprehensive documentation in
docs/templates/others/convex.mdx
(though with some issues) - Pagination utilities in
packages/stack-shared/src/utils/paginated-lists.tsx
for handling large datasets - Updated test suites to validate anonymous user JWT changes and Convex integration functionality
The integration follows Stack's established patterns for third-party integrations while accommodating Convex's unique authentication and real-time data requirements. The changes maintain backward compatibility through deprecated aliases and legacy token handling.
Important Files Changed
Changed Files
Filename | Score | Overview |
---|---|---|
docs/templates/others/convex.mdx | 1/5 | Documentation file with critical errors including function name mismatches, Supabase references instead of Convex, and incorrect GitHub links |
examples/convex/convex/auth.config.ts | 2/5 | Convex authentication config with hardcoded CloudFlare tunnel URLs and mixed local/production endpoints creating deployment issues |
examples/convex/convex/myFunctions.ts | 2/5 | Example Convex functions with questionable client-side Stack app usage in server context and @ts-ignore suppressing errors |
packages/stack-shared/src/utils/paginated-lists.tsx | 3/5 | Complex pagination framework with intricate cursor management logic and potential performance concerns in sorting validation |
examples/convex/eslint.config.mjs | 3/5 | ESLint config using modern flat format but inconsistent with project's shared configurations used by other examples |
packages/template/src/integrations/convex.ts | 5/5 | Clean Convex JWT provider configuration function with proper environment variable handling and type safety |
apps/backend/src/lib/tokens.tsx | 4/5 | JWT token refactoring moving from 'anon' role to 'is_anonymous' field with proper backward compatibility handling |
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts | 4/5 | Comprehensive Convex integration adding partial user methods and auth helpers with good type safety |
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts | 5/5 | Clean renaming of GetUserOptions to GetCurrentUserOptions maintaining full backward compatibility |
packages/stack-shared/src/sessions.ts | 4/5 | JWT payload validation enhancement with schema-based approach improving type safety but adding validation overhead |
examples/convex/package.json | 4/5 | Well-structured package config for Convex example with parallel dev scripts and modern dependency versions |
examples/convex/app/page.tsx | 4/5 | Complete example React component demonstrating Stack Auth + Convex integration with proper loading states |
examples/convex/app/layout.tsx | 4/5 | Proper provider hierarchy setup combining Stack Auth with Convex client integration |
packages/template/src/index.ts | 4/5 | Clean addition of Convex integration export with maintained alphabetical ordering |
examples/convex/components/ConvexClientProvider.tsx | 4/5 | Well-structured provider component integrating Convex client with Stack Auth authentication |
apps/e2e/tests/js/convex.test.ts | 4/5 | Basic integration test validating Convex client auth setup but lacks comprehensive validation scenarios |
examples/convex/README.md | 4/5 | Generic Convex template README not customized for Stack Auth integration despite functional integration |
apps/e2e/tests/backend/endpoints/api/v1/auth/anonymous/anonymous-comprehensive.test.ts | 4/5 | Updated anonymous user tests with proper JWT payload expectations for new token structure |
examples/convex/postcss.config.mjs | 4/5 | Modern Tailwind CSS v4 PostCSS config using unified plugin approach |
examples/convex/app/globals.css | 4/5 | Modern Tailwind v4 global styles with theme support but potential font configuration issues |
packages/template/src/lib/stack-app/users/index.ts | 5/5 | New partial user types for token embedding and external system synchronization with proper type safety |
examples/convex/app/loading.tsx | 5/5 | Standard Next.js loading component following established patterns with helpful Suspense documentation |
examples/convex/stack/server.tsx | 5/5 | Standard Stack server app setup following established example patterns with proper server-only directive |
Confidence score: 3/5
- This PR introduces significant functionality but contains several implementation issues that could cause problems in production
- Score reflects concerns about hardcoded URLs in auth config, documentation errors, and questionable patterns in example code
- Pay close attention to
docs/templates/others/convex.mdx
,examples/convex/convex/auth.config.ts
, andexamples/convex/convex/myFunctions.ts
which need fixes before deployment
Sequence Diagram
sequenceDiagram
participant User
participant "Next.js App" as App
participant "Stack Client" as Stack
participant "Convex Client" as Convex
participant "Stack API" as API
participant "Convex Backend" as Backend
User->>App: "Access protected resource"
App->>Stack: "getUser()"
Stack->>API: "GET /users/me"
API-->>Stack: "User data + JWT tokens"
Stack-->>App: "CurrentUser object"
App->>Convex: "Initialize ConvexReactClient"
App->>Stack: "getConvexClientAuth({ tokenStore: 'nextjs-cookie' })"
Stack-->>App: "Auth callback function"
App->>Convex: "convex.setAuth(authCallback)"
User->>App: "Execute Convex query/mutation"
App->>Convex: "useQuery(api.myFunctions.listNumbers)"
Convex->>Stack: "authCallback({ forceRefreshToken: false })"
Stack->>Stack: "session.getOrFetchLikelyValidTokens(20_000)"
Stack-->>Convex: "JWT access token"
Convex->>Backend: "Query with JWT in Authorization header"
Backend->>Backend: "Verify JWT against Stack JWKS"
Backend->>Backend: "Extract user info from JWT payload"
Backend-->>Convex: "Query results with user context"
Convex-->>App: "Data for authenticated user"
App-->>User: "Render protected content"
Note over Stack,Backend: "JWT contains: userId, email, isAnonymous, etc."
52 files reviewed, 25 comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review by RecurseML
🔍 Review performed on 7a0bf86..4bc9f48
Severity | Location | Issue | Delete |
---|---|---|---|
examples/convex/convex/_generated/server.d.ts:31 | The 'primaryEmail' property in an API response object should use snake_case as 'primary_email' | ||
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts:1673 | Missing optional chaining or explicit error handling for nested nullable property | ||
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts:1675 | Missing optional chaining or explicit error handling for nested nullable property | ||
examples/convex/convex/_generated/server.js:67 | Missing runAsynchronously wrapper for async operations in Node.js environment |
✅ Files analyzed, no issues (2)
• packages/stack-shared/src/utils/paginated-lists.tsx
• examples/convex/app/page.tsx
⏭️ Files skipped (53)
Locations |
---|
.vscode/settings.json |
apps/backend/src/lib/tokens.tsx |
apps/backend/src/lib/types.tsx |
apps/e2e/package.json |
apps/e2e/tests/backend/backend-helpers.ts |
apps/e2e/tests/backend/endpoints/api/v1/auth/anonymous/anonymous-comprehensive.test.ts |
apps/e2e/tests/js/convex.test.ts |
docs/docs-platform.yml |
docs/templates/meta.json |
docs/templates/others/convex.mdx |
examples/convex/.gitignore |
examples/convex/.prettierrc |
examples/convex/README.md |
examples/convex/app/globals.css |
examples/convex/app/handler/[...stack]/page.tsx |
examples/convex/app/layout.tsx |
examples/convex/app/loading.tsx |
examples/convex/app/server/inner.tsx |
examples/convex/app/server/page.tsx |
examples/convex/components/ConvexClientProvider.tsx |
examples/convex/convex/README.md |
examples/convex/convex/_generated/api.d.ts |
examples/convex/convex/_generated/api.js |
examples/convex/convex/_generated/dataModel.d.ts |
examples/convex/convex/auth.config.ts |
examples/convex/convex/myFunctions.ts |
examples/convex/convex/schema.ts |
examples/convex/convex/tsconfig.json |
examples/convex/eslint.config.mjs |
examples/convex/next.config.ts |
examples/convex/package-lock.json |
examples/convex/package.json |
examples/convex/postcss.config.mjs |
examples/convex/public/convex.svg |
examples/convex/stack/client.tsx |
examples/convex/stack/server.tsx |
examples/convex/tsconfig.json |
packages/js/package.json |
packages/react/package.json |
packages/stack-shared/src/sessions.ts |
packages/stack/package.json |
packages/template/package-template.json |
packages/template/package.json |
packages/template/src/index.ts |
packages/template/src/integrations/convex.ts |
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts |
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts |
packages/template/src/lib/stack-app/apps/interfaces/server-app.ts |
packages/template/src/lib/stack-app/common.ts |
packages/template/src/lib/stack-app/index.ts |
packages/template/src/lib/stack-app/users/index.ts |
packages/template/src/providers/stack-provider-client.tsx |
pnpm-lock.yaml |
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
Show resolved
Hide resolved
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
docs/templates/others/convex.mdx (1)
99-100
: Fix example link to point to Convex sample.The link points to the Supabase example.
Apply this diff:
-You can find the full example [here on GitHub](https://github.com/stack-auth/stack-auth/tree/main/examples/supabase). +You can find the full example [here on GitHub](https://github.com/stack-auth/stack-auth/tree/main/examples/convex).apps/backend/src/lib/tokens.tsx (2)
91-98
: Anonymous-audience validation uses legacy role; switch to is_anonymous and fix log messages.With the new payload, role is always "authenticated". The current check will misclassify anonymous tokens.
- const isAnonymous = payload.role === 'anon'; - if (aud.endsWith(":anon") && !isAnonymous) { - console.warn("Unparsable access token. Role is set to anon, but audience is not an anonymous audience.", { accessToken, payload }); + const isAnonymous = payload.is_anonymous === true; + if (aud.endsWith(":anon") && !isAnonymous) { + console.warn("Unparsable access token. Audience is anonymous (:anon) but payload.is_anonymous is false.", { payload }); return Result.error(new KnownErrors.UnparsableAccessToken()); - } else if (!aud.endsWith(":anon") && isAnonymous) { - console.warn("Unparsable access token. Audience is not an anonymous audience, but role is set to anon.", { accessToken, payload }); + } else if (!aud.endsWith(":anon") && isAnonymous) { + console.warn("Unparsable access token. Audience 67E6 is non-anonymous but payload.is_anonymous is true.", { payload }); return Result.error(new KnownErrors.UnparsableAccessToken()); }
85-85
: Do not log raw access tokens.Leaking JWTs in logs is a security risk. Remove token from logs or truncate/hash it.
- console.warn("Unparsable access token. This might be a user error, but if it happens frequently, it's a sign of a misconfiguration.", { accessToken, error }); + console.warn("Unparsable access token. This might be a user error, but if it happens frequently, it's a sign of a misconfiguration.", { error });
🧹 Nitpick comments (32)
examples/convex/.gitignore (2)
7-11
: Yarn Berry: also unignore .yarn/sdks.Aligns with Yarn’s recommended patterns for editor SDKs.
Apply this diff:
.yarn/* !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/versions +!.yarn/sdks
16-19
: Optionally ignore Turborepo cache.If the example uses Turbo (common with Next.js), ignore its cache.
Apply this diff:
# next.js /.next/ /out/ +.turbo
examples/convex/eslint.config.mjs (1)
12-16
: Add ignore patterns for build and generated codeMinimize lint churn and CI time by ignoring common outputs and Convex-generated files.
Apply this diff:
-const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), -]; +const eslintConfig = [ + { + ignores: ["node_modules/**", ".next/**", "out/**", "convex/_generated/**"], + }, + ...compat.extends("next/core-web-vitals", "next/typescript"), +];examples/convex/.prettierrc (1)
1-2
: Empty .prettierrc in examples/convex masks root Prettier config — remove or populate intentionally.examples/convex/.prettierrc is
{}
; Prettier stops at the first config file, preventing inheritance of repo-wide rules (examples/convex/package.json declares "prettier"). Delete the file to inherit the root config or add the intended overrides.examples/convex/convex/tsconfig.json (2)
10-10
: AlignmoduleResolution
casing with TS defaults ("bundler").TS enum values are lowercase in current docs. Use "bundler" for consistency with the other tsconfig.
- "moduleResolution": "Bundler", + "moduleResolution": "bundler",
2-5
: JSON comments are fine for TS, but can break non-TS tools.If any tooling parses this as strict JSON, comments may error. Consider switching to
tsconfig.jsonc
or removing comments if issues arise.examples/convex/package.json (1)
21-33
: Addengines
and atypecheck
script for DX.Helps catch TS errors and ensure correct Node for Next 15.
"devDependencies": { "@eslint/eslintrc": "^3", @@ "typescript": "^5" - } + }, + "engines": { + "node": ">=18.18.0 || >=20" + }And consider:
"scripts": { @@ - "lint": "next lint" + "lint": "next lint", + "typecheck": "tsc --noEmit" },examples/convex/tsconfig.json (2)
10-11
: Keepmodule
/moduleResolution
casing consistent across tsconfigs.You use
module: "esnext"
/moduleResolution: "bundler"
here vsESNext
/Bundler
in the Convex tsconfig. Prefer consistent lowercase forms.- "module": "esnext", - "moduleResolution": "bundler", + "module": "esnext", + "moduleResolution": "bundler",And in
examples/convex/convex/tsconfig.json
, switch to lowercase as noted.
1-27
: Consider sharing a base tsconfig to avoid drift.Both tsconfigs share many options. Create
examples/convex/tsconfig.base.json
andextends
it from each to keep them aligned.Happy to draft a base config if you want.
examples/convex/postcss.config.mjs (2)
1-5
: Add autoprefixer to PostCSS config and devDependenciesVerification: examples/convex/package.json does not include autoprefixer — add it to devDependencies and include "autoprefixer" in examples/convex/postcss.config.mjs plugins (e.g. plugins: ["@tailwindcss/postcss", "autoprefixer"]).
1-3
: Use object-form plugin config for PostCSS (matches Tailwind v4 docs)Replace the array with an object mapping to match Tailwind v4 examples and avoid postcss-load-config resolution issues:
-const config = { - plugins: ["@tailwindcss/postcss"], -}; +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +};packages/template/package.json (1)
64-64
: Duplicate rimraf in dependencies and devDependencies — keep only in devDependencies.This avoids installing it for consumers unnecessarily.
Apply:
--- a/packages/template/package.json +++ b/packages/template/package.json @@ "dependencies": { @@ - "rimraf": "^5.0.5", @@ }, @@ "devDependencies": { @@ "rimraf": "^5.0.5", @@ }Also applies to: 101-101
apps/e2e/tests/backend/endpoints/api/v1/auth/anonymous/anonymous-comprehensive.test.ts (1)
37-38
: Clarify test semantics and assert both tokensTitle says “different kid and role,” but role is now ‘authenticated’ for both; difference is is_anonymous. Consider renaming and asserting regular payload is_anonymous === false.
docs/templates/others/convex.mdx (1)
61-69
: Deduplicate setup sections or clarify flow.This “Setup a new Next.js project” repeats the earlier project creation. Consider consolidating to a single flow (wizard vs manual) to avoid confusion.
examples/convex/convex/README.md (1)
4-4
: Avoid bare URL (MD034).Wrap the URL to satisfy markdownlint.
Apply this diff:
-See https://docs.convex.dev/functions for more. +See <https://docs.convex.dev/functions> for more.packages/template/src/integrations/convex.ts (1)
4-16
: Makeoptions
optional and tighten the return type.This improves DX and avoids accidental shape drift of provider objects.
+export type ConvexCustomJwtProvider = { + type: "customJwt"; + issuer: string; + jwks: string; + algorithm: "ES256"; +}; + -export function getConvexProvidersConfig(options: { - projectId?: string, -}) { +export function getConvexProvidersConfig( + options: { projectId?: string } = {}, +): ConvexCustomJwtProvider[] { const projectId = options.projectId ?? getDefaultProjectId(); return [ { - type: "customJwt", + type: "customJwt", issuer: urlString`https://api.stack-auth.com/api/v1/projects/${projectId}`, jwks: urlString`https://api.stack-auth.com/api/v1/projects/${projectId}/.well-known/jwks.json?include_anonymous=true`, - algorithm: "ES256", + algorithm: "ES256", }, ]; }Also applies to: 3-3
examples/convex/convex/myFunctions.ts (2)
31-33
: Avoid mutating the result array from the DB.Use a non-mutating reverse for clarity and safety.
- numbers: numbers.reverse().map((number) => number.value), + numbers: numbers.slice().reverse().map((n) => n.value),
21-26
: Clampcount
to a sane upper bound to avoid large reads.Prevent accidental large
take()
calls.- .order("desc") - .take(args.count); + .order("desc"); + const limit = Math.max(0, Math.min(args.count, 100)); + const recent = await numbers.take(limit); + // Use `recent` below instead of `numbers`Follow through by replacing subsequent
numbers
references withrecent
.packages/template/src/lib/stack-app/users/index.ts (1)
288-300
: Remove duplicate keys inSyncedPartialUser
intersection.
TokenPartialUser
already includes several fields being re-picked. Avoid duplication for clarity.-export type SyncedPartialUser = TokenPartialUser & Pick< - User, - | "id" - | "displayName" - | "primaryEmail" - | "primaryEmailVerified" - | "profileImageUrl" - | "signedUpAt" - | "clientMetadata" - | "clientReadOnlyMetadata" - | "isAnonymous" - | "hasPassword" ->; +export type SyncedPartialUser = TokenPartialUser & Pick< + User, + | "profileImageUrl" + | "signedUpAt" + | "clientMetadata" + | "clientReadOnlyMetadata" + | "hasPassword" +>;examples/convex/stack/client.tsx (1)
3-6
: Avoid hard-coded baseUrl; prefer env with sensible default.Improves portability across environments.
-export const stackClientApp = new StackClientApp({ - baseUrl: "http://localhost:8102", - tokenStore: "nextjs-cookie", -}); +export const stackClientApp = new StackClientApp({ + baseUrl: process.env.NEXT_PUBLIC_STACK_BASE_URL || "http://localhost:8102", + tokenStore: "nextjs-cookie", +});examples/convex/app/handler/[...stack]/page.tsx (1)
5-5
: Minor JSX style nit: remove spaces around props.Matches common formatting and linters.
- return <StackHandler fullPage app = { stackServerApp } routeProps = { props } />; + return <StackHandler fullPage app={stackServerApp} routeProps={props} />;packages/template/src/lib/stack-app/index.ts (1)
31-34
: LGTM — replace remainingGetUserOptions
import withGetCurrentUserOptions
.Found one usage: packages/template/src/lib/hooks.tsx — change
import { GetUserOptions as AppGetUserOptions, CurrentInternalUser, CurrentUser, StackClientApp } from "./stack-app";
to
import { GetCurrentUserOptions as AppGetUserOptions, CurrentInternalUser, CurrentUser, StackClientApp } from "./stack-app";examples/convex/app/globals.css (1)
22-26
: Use the intended Geist font and declare color-scheme.Body currently forces Arial, ignoring the Geist font variables configured in layout. Also declare color-scheme to assist UA theming.
body { color: var(--foreground); background: var(--background); - font-family: Arial, Helvetica, sans-serif; + color-scheme: light dark; + font-family: var(--font-geist-sans), system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; }examples/convex/components/ConvexClientProvider.tsx (1)
8-10
: Avoid redundant tokenStore override if app already uses "nextjs-cookie".If stackClientApp is constructed with tokenStore "nextjs-cookie", passing it again is redundant. Not a blocker.
-convex.setAuth( - stackClientApp.getConvexClientAuth({ tokenStore: "nextjs-cookie" }) -); +convex.setAuth(stackClientApp.getConvexClientAuth());packages/template/src/lib/stack-app/common.ts (1)
40-57
: Tighten GenericQueryCtx typing (avoid any).Use unknown instead of any to prevent unsoundness while keeping the generic open.
- ctx: GenericQueryCtx<any>, + ctx: GenericQueryCtx<unknown>,packages/template/src/lib/stack-app/apps/interfaces/server-app.ts (1)
3-3
: Use a type-only import for Convex types.Prevents pulling "convex/server" into runtime bundles.
-import { GenericQueryCtx } from "convex/server"; +import type { GenericQueryCtx } from "convex/server";packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts (1)
906-949
: getUser: logic parity with client + anonymous path preserved.Reads well; duplication noted in TODO is accurate.
Consider extracting the common getUser flow (ensure persistent store, fetch session, anonymous handling) into a shared helper to remove duplication with the client impl.
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (3)
1595-1611
: Token-derived partial user: OK; add small guard for anonymous.You can early-return on isAnonymous && options.or !== 'anonymous' before reading other payload fields to avoid unnecessary reads. Current behavior is correct as-is.
1627-1644
: Remove unnecessary any-casts; types already flow from GetCurrentPartialUserOptions.The ctx type matches the helper signature; avoid as unknown as any.
- return await this._getPartialUserFromConvex(options.ctx as unknown as any); + return await this._getPartialUserFromConvex(options.ctx);
1679-1683
: getConvexHttpClientAuth throws on missing token: OK; document behavior.Consider a brief JSDoc noting it throws if no token is available to aid consumers.
packages/stack-shared/src/utils/paginated-lists.tsx (2)
219-219
: Simplify merge cursor type to plain string.The cursor is a JSON string; unions like "first" | "last" |
[${string}]
are misleading and break JSON.parse on non-JSON tokens.- override async _nextOrPrev(type: 'next' | 'prev', { limit, filter, orderBy, cursor }: ImplQueryOptions<'next' | 'prev', "first" | "last" | `[${string}]`, Filter, OrderBy>) { + override async _nextOrPrev(type: 'next' | 'prev', { limit, filter, orderBy, cursor }: ImplQueryOptions<'next' | 'prev', string, Filter, OrderBy>) {
29-31
: Remove duplicate ImplQueryResult type.ImplQueryResult is identical to QueryResult. Alias it to avoid drift.
-type ImplQueryResult<Item, Cursor> = { items: { item: Item, itemCursor: Cursor }[], isFirst: boolean, isLast: boolean, cursor: Cursor } +type ImplQueryResult<Item, Cursor> = QueryResult<Item, Cursor>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (8)
examples/convex/convex/_generated/api.d.ts
is excluded by!**/_generated/**
examples/convex/convex/_generated/api.js
is excluded by!**/_generated/**
examples/convex/convex/_generated/dataModel.d.ts
is excluded by!**/_generated/**
examples/convex/convex/_generated/server.d.ts
is excluded by!**/_generated/**
examples/convex/convex/_generated/server.js
is excluded by!**/_generated/**
examples/convex/package-lock.json
is excluded by!**/package-lock.json
examples/convex/public/convex.svg
is excluded by!**/*.svg
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (50)
.vscode/settings.json
(1 hunks)apps/backend/src/lib/tokens.tsx
(3 hunks)apps/backend/src/lib/types.tsx
(0 hunks)apps/e2e/package.json
(1 hunks)apps/e2e/tests/backend/backend-helpers.ts
(1 hunks)apps/e2e/tests/backend/endpoints/api/v1/auth/anonymous/anonymous-comprehensive.test.ts
(1 hunks)apps/e2e/tests/js/convex.test.ts
(1 hunks)docs/docs-platform.yml
(1 hunks)docs/templates/meta.json
(1 hunks)docs/templates/others/convex.mdx
(1 hunks)examples/convex/.gitignore
(1 hunks)examples/convex/.prettierrc
(1 hunks)examples/convex/README.md
(1 hunks)examples/convex/app/globals.css
(1 hunks)examples/convex/app/handler/[...stack]/page.tsx
(1 hunks)examples/convex/app/layout.tsx
(1 hunks)examples/convex/app/loading.tsx
(1 hunks)examples/convex/app/page.tsx
(1 hunks)examples/convex/app/server/inner.tsx
(1 hunks)examples/convex/app/server/page.tsx
(1 hunks)examples/convex/components/ConvexClientProvider.tsx
(1 hunks)examples/convex/convex/README.md
(1 hunks)examples/convex/convex/auth.config.ts
(1 hunks)examples/convex/convex/myFunctions.ts
(1 hunks)examples/convex/convex/schema.ts
(1 hunks)examples/convex/convex/tsconfig.json
(1 hunks)examples/convex/eslint.config.mjs
(1 hunks)examples/convex/next.config.ts
(1 hunks)examples/convex/package.json
(1 hunks)examples/convex/postcss.config.mjs
(1 hunks)examples/convex/stack/client.tsx
(1 hunks)examples/convex/stack/server.tsx
(1 hunks)examples/convex/tsconfig.json
(1 hunks)packages/js/package.json
(1 hunks)packages/react/package.json
(1 hunks)packages/stack-shared/src/sessions.ts
(3 hunks)packages/stack-shared/src/utils/paginated-lists.tsx
(1 hunks)packages/stack/package.json
(1 hunks)packages/template/package-template.json
(1 hunks)packages/template/package.json
(1 hunks)packages/template/src/index.ts
(2 hunks)packages/template/src/integrations/convex.ts
(1 hunks)packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
(5 hunks)packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
(3 hunks)packages/template/src/lib/stack-app/apps/interfaces/client-app.ts
(4 hunks)packages/template/src/lib/stack-app/apps/interfaces/server-app.ts
(2 hunks)packages/template/src/lib/stack-app/common.ts
(3 hunks)packages/template/src/lib/stack-app/index.ts
(1 hunks)packages/template/src/lib/stack-app/users/index.ts
(2 hunks)packages/template/src/providers/stack-provider-client.tsx
(0 hunks)
💤 Files with no reviewable changes (2)
- apps/backend/src/lib/types.tsx
- packages/template/src/providers/stack-provider-client.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer ES6 Map over Record when representing key–value collections
Files:
examples/convex/convex/schema.ts
apps/e2e/tests/backend/endpoints/api/v1/auth/anonymous/anonymous-comprehensive.test.ts
examples/convex/components/ConvexClientProvider.tsx
examples/convex/app/server/page.tsx
examples/convex/next.config.ts
examples/convex/app/handler/[...stack]/page.tsx
examples/convex/app/server/inner.tsx
examples/convex/convex/myFunctions.ts
examples/convex/app/layout.tsx
examples/convex/convex/auth.config.ts
packages/template/src/lib/stack-app/users/index.ts
packages/template/src/integrations/convex.ts
apps/e2e/tests/js/convex.test.ts
packages/template/src/lib/stack-app/index.ts
examples/convex/stack/client.tsx
packages/template/src/lib/stack-app/common.ts
packages/template/src/index.ts
apps/e2e/tests/backend/backend-helpers.ts
examples/convex/app/page.tsx
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts
examples/convex/app/loading.tsx
packages/template/src/lib/stack-app/apps/interfaces/server-app.ts
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
packages/stack-shared/src/utils/paginated-lists.tsx
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
apps/backend/src/lib/tokens.tsx
examples/convex/stack/server.tsx
packages/stack-shared/src/sessions.ts
**/*.test.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
In tests, prefer .toMatchInlineSnapshot where possible; refer to snapshot-serializer.ts for snapshot formatting and handling of non-deterministic values
Files:
apps/e2e/tests/backend/endpoints/api/v1/auth/anonymous/anonymous-comprehensive.test.ts
apps/e2e/tests/js/convex.test.ts
packages/template/**
📄 CodeRabbit inference engine (AGENTS.md)
When modifying the SDK copies, make changes in packages/template (source of truth)
Files:
packages/template/src/lib/stack-app/users/index.ts
packages/template/src/integrations/convex.ts
packages/template/src/lib/stack-app/index.ts
packages/template/src/lib/stack-app/common.ts
packages/template/src/index.ts
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts
packages/template/src/lib/stack-app/apps/interfaces/server-app.ts
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
packages/template/package-template.json
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
packages/template/package.json
packages/js/**
📄 CodeRabbit inference engine (AGENTS.md)
Never modify packages/js; make changes in packages/template instead as packages/js is a copy
Files:
packages/js/package.json
packages/stack/**
📄 CodeRabbit inference engine (AGENTS.md)
Never modify packages/stack; make changes in packages/template instead as packages/stack is a copy
Files:
packages/stack/package.json
🧬 Code graph analysis (21)
apps/e2e/tests/backend/endpoints/api/v1/auth/anonymous/anonymous-comprehensive.test.ts (1)
packages/stack-shared/src/sessions.ts (1)
payload
(34-37)
examples/convex/components/ConvexClientProvider.tsx (1)
examples/convex/stack/client.tsx (1)
stackClientApp
(3-6)
examples/convex/app/server/page.tsx (2)
examples/convex/convex/_generated/api.js (2)
api
(21-21)api
(21-21)examples/convex/app/server/inner.tsx (1)
Home
(6-31)
examples/convex/app/handler/[...stack]/page.tsx (2)
examples/convex/stack/server.tsx (1)
stackServerApp
(5-7)docs/src/app/handler/[...stack]/page.tsx (1)
Handler
(4-6)
examples/convex/app/server/inner.tsx (2)
examples/convex/convex/_generated/api.js (2)
api
(21-21)api
(21-21)examples/convex/convex/myFunctions.ts (1)
addNumber
(38-56)
examples/convex/convex/myFunctions.ts (3)
examples/convex/convex/_generated/server.js (6)
query
(29-29)query
(29-29)mutation
(49-49)mutation
(49-49)action
(72-72)action
(72-72)examples/convex/stack/client.tsx (1)
stackClientApp
(3-6)examples/convex/convex/_generated/api.js (2)
api
(21-21)api
(21-21)
examples/convex/app/layout.tsx (2)
examples/convex/stack/server.tsx (1)
stackServerApp
(5-7)examples/convex/components/ConvexClientProvider.tsx (1)
ConvexClientProvider
(12-18)
examples/convex/convex/auth.config.ts (1)
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (1)
projectId
(1338-1340)
packages/template/src/lib/stack-app/users/index.ts (1)
packages/template/src/lib/stack-app/index.ts (2)
User
(105-105)ServerUser
(103-103)
packages/template/src/integrations/convex.ts (2)
packages/template/src/lib/stack-app/apps/implementations/common.ts (1)
getDefaultProjectId
(67-69)packages/stack-shared/src/utils/urls.tsx (1)
urlString
(314-316)
apps/e2e/tests/js/convex.test.ts (2)
apps/e2e/tests/helpers.ts (1)
it
(11-11)apps/e2e/tests/js/js-helpers.ts (1)
createApp
(41-86)
packages/template/src/lib/stack-app/common.ts (1)
packages/template/src/lib/stack-app/index.ts (2)
GetCurrentUserOptions
(31-31)GetCurrentUserOptions
(33-33)
examples/convex/app/page.tsx (3)
examples/convex/app/server/inner.tsx (1)
Home
(6-31)packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (1)
useUser
(1550-1592)examples/convex/convex/myFunctions.ts (1)
addNumber
(38-56)
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts (2)
packages/template/src/lib/stack-app/common.ts (3)
TokenStoreInit
(64-73)GetCurrentUserOptions
(31-38)GetCurrentPartialUserOptions
(40-56)packages/template/src/lib/stack-app/users/index.ts (3)
ProjectCurrentUser
(277-277)TokenPartialUser
(279-286)SyncedPartialUser
(288-300)
examples/convex/app/loading.tsx (1)
examples/supabase/app/loading.tsx (1)
Loading
(1-5)
packages/template/src/lib/stack-app/apps/interfaces/server-app.ts (2)
packages/template/src/lib/stack-app/common.ts (2)
GetCurrentUserOptions
(31-38)GetCurrentPartialUserOptions
(40-56)packages/template/src/lib/stack-app/users/index.ts (4)
ProjectCurrentServerUser
(401-401)ServerUser
(395-395)TokenPartialUser
(279-286)SyncedPartialServerUser
(403-406)
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts (3)
packages/template/src/lib/stack-app/common.ts (1)
GetCurrentUserOptions
(31-38)packages/template/src/lib/stack-app/index.ts (3)
GetCurrentUserOptions
(31-31)GetCurrentUserOptions
(33-33)ServerUser
(103-103)packages/template/src/lib/stack-app/users/index.ts (2)
ProjectCurrentServerUser
(401-401)ServerUser
(395-395)
packages/stack-shared/src/utils/paginated-lists.tsx (2)
packages/stack-shared/src/utils/errors.tsx (1)
StackAssertionError
(69-85)packages/stack-shared/src/utils/arrays.tsx (1)
range
(97-109)
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (5)
packages/template/src/lib/stack-app/common.ts (3)
GetCurrentUserOptions
(31-38)GetCurrentPartialUserOptions
(40-56)TokenStoreInit
(64-73)packages/template/src/lib/stack-app/index.ts (2)
GetCurrentUserOptions
(31-31)GetCurrentUserOptions
(33-33)packages/template/src/lib/stack-app/users/index.ts (3)
ProjectCurrentUser
(277-277)TokenPartialUser
(279-286)SyncedPartialUser
(288-300)packages/stack-shared/src/sessions.ts (1)
InternalSession
(72-242)packages/stack-shared/src/utils/errors.tsx (1)
throwErr
(10-19)
apps/backend/src/lib/tokens.tsx (2)
packages/stack-shared/src/sessions.ts (2)
payload
(34-37)AccessTokenPayload
(23-23)packages/stack-shared/src/utils/jwt.tsx (1)
signJWT
(38-54)
packages/stack-shared/src/sessions.ts (1)
packages/stack-shared/src/schema-fields.ts (4)
yupObject
(247-251)yupString
(187-190)yupNumber
(191-194)yupBoolean
(195-198)
🪛 markdownlint-cli2 (0.18.1)
examples/convex/convex/README.md
4-4: Bare URL used
(MD034, no-bare-urls)
examples/convex/README.md
16-16: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
23-23: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: setup-tests
- GitHub Check: lint_and_build (latest)
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: docker
- GitHub Check: restart-dev-and-test
- GitHub Check: build (22.x)
- GitHub Check: Security Check
🔇 Additional comments (42)
examples/convex/eslint.config.mjs (1)
1-16
: LGTM on Flat Config + Next compat setupUsing FlatCompat to bridge Next’s legacy configs in a flat ESLint config looks correct.
packages/template/package-template.json (2)
143-144
: LGTM: adding Convex as a devDependency aligns with the template being the source of truth.Change is localized to the template; version range is reasonable.
143-144
: Confirm convex dependency scope (devDependency vs peerDependency) — manual check required
- If any runtime code shipped to consumers imports convex (convex, convex/react, convex/server) promote it to a peerDependency (optionally optional) and keep it as a devDependency for local builds.
- If Convex is only used for examples/demo/tooling/codegen, devDependency is correct.
- Consider gating it with the template preprocessor to avoid adding it to non-template packages.
Automated search in the sandbox failed; run locally to detect runtime imports:
rg -n -S -uu --glob '!/node_modules/' --glob '!/dist/' '\bconvex(?:/|$)'File: packages/template/package-template.json (around lines 143–144)
Suggested gated variant:
"tailwindcss": "^3.4.4", "tsup": "^8.0.2", - "convex": "^1.27.0" + "//": "NEXT_LINE_PLATFORM template", + "convex": "^1.27.0"examples/convex/next.config.ts (1)
1-7
: LGTM – minimal Next config scaffold is valid.Typed
NextConfig
import and default export look good.examples/convex/package.json (1)
14-20
: Version set looks compatible.Next 15.2.x with React 19 and eslint 9 is consistent; convex ^1.27.0 seems reasonable.
If CI isn’t covering it, please confirm local
next dev
starts without peer warnings.packages/template/package.json (1)
103-103
: tsup addition aligns with tsup-node script — LGTM..vscode/settings.json (1)
116-118
: LGTM: organizeImports runs before eslint fixAllOrder makes sense and avoids import-sort churn.
apps/e2e/tests/backend/backend-helpers.ts (1)
211-211
: LGTM: payload now asserts is_anonymous booleanMatches the new token shape and strengthens validation.
docs/templates/meta.json (1)
36-36
: Docs nav entry verified — page and platform mapping present
docs/templates/others/convex.mdx exists and docs/docs-platform.yml contains- path: others/convex.mdx
with platforms ["next","react","js"].packages/react/package.json (1)
90-92
: No action required — template already updatedpackages/template/package.json already contains "tsup": "^8.0.2" and "convex": "^1.27.0", so the edit in packages/react is generated.
apps/e2e/package.json (1)
17-18
: Add Convex dep — verified single-version alignment across workspaceConvex is declared as ^1.27.0 in all package.json files that reference it (apps/e2e, examples/convex, packages/js, packages/react, packages/stack, packages/template) — no change required.
packages/stack/package.json (1)
98-100
: Do not edit auto-generated packages/stack — template already has "convex"packages/template/package.json and packages/template/package-template.json already list "convex": "^1.27.0"; no change to packages/stack required.
examples/convex/convex/schema.ts (1)
1-12
: LGTM.Schema is minimal and valid for the example.
docs/docs-platform.yml (1)
276-278
: Docs platform entry looks good.Page enabled for next/react/js; consistent with content scope.
packages/template/src/index.ts (1)
3-4
: Export addition LGTM; ensure external docs match symbol name.Publicly exporting
getConvexProvidersConfig
from the template is correct.Verify that consumer docs/snippets import the function with the exact pluralized name and from the correct published package.
examples/convex/app/server/inner.tsx (1)
6-31
: LGTM.Client component correctly consumes a preloaded query and mutation.
docs/templates/others/convex.mdx (1)
84-97
: Complete the client snippet: add missing imports and a real Convex client.
convex/_generated/client
does not export aconvexClient
instance. Provide a constructed client and the missingStackClientApp
import to avoid copy‑paste errors.Apply this diff:
-```typescript title="stack/client.ts" -import { convexClient } from "../convex/_generated/client"; - -export const stackClientApp = new StackClientApp({ +```typescript title="stack/client.ts" +import { StackClientApp } from "@stackframe/stack"; // verify package name +import { ConvexReactClient } from "con 67DE vex/react"; +const convexClient = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!); + +export const stackClientApp = new StackClientApp({ projectId: process.env.NEXT_PUBLIC_STACK_PROJECT_ID, publishableClientKey: process.env.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY, tokenStore: "nextjs-cookie", integrations: { convex: { client: convexClient, }, }, });- Ensure the correct import path for `StackClientApp`. - Document `NEXT_PUBLIC_CONVEX_URL` in the ENV section. </blockquote></details> <details> <summary>examples/convex/app/loading.tsx (1)</summary><blockquote> `1-6`: **LGTM: minimal Suspense loading placeholder.** </blockquote></details> <details> <summary>examples/convex/stack/server.tsx (1)</summary><blockquote> `1-7`: **LGTM: server-only app with cookie token store.** Appropriate for Next.js server routes; complements Convex example. </blockquote></details> <details> <summary>examples/convex/app/server/page.tsx (1)</summary><blockquote> `5-24`: **LGTM: server preloading is correct.** Preload + preloadedQueryResult usage and passing Preloaded to Home are correct. </blockquote></details> <details> <summary>examples/convex/components/ConvexClientProvider.tsx (1)</summary><blockquote> `12-18`: **LGTM: single Convex client instance at module scope.** Good pattern to avoid recreating clients per render. </blockquote></details> <details> <summary>apps/backend/src/lib/tokens.tsx (1)</summary><blockquote> `153-164`: **Centralized payload construction looks good.** Typed payload via AccessTokenPayload (omitting iss/aud) aligns with the new schema and ensures claim consistency. Confirm all producers set role: "authenticated" (including anonymous) and only toggle is_anonymous, and that no older producers still emit role: "anon". </blockquote></details> <details> <summary>packages/stack-shared/src/sessions.ts (2)</summary><blockquote> `140-151`: **Good API addition.** getAccessTokenIfNotExpiredYet with a 60s cap is a sensible guardrail for short-lived tokens. --- `7-21`: **Schema is strict; ensure all token producers meet it.** signJWT is only used in apps/backend/src/lib/tokens.tsx — generateAccessToken builds the payload with project_id, branch_id, refresh_token_id, role: 'authenticated', name, email, email_verified, selected_team_id (nullable) and is_anonymous, and sets iss/aud via getIssuer/getAudience; tests call createAuthTokens → generateAccessToken. No other signJWT producers found. </blockquote></details> <details> <summary>apps/e2e/tests/js/convex.test.ts (1)</summary><blockquote> `1-3`: **Vitest environment and globals verified — OK.** apps/e2e/vitest.config.ts sets test.environment = 'node' and apps/e2e/tests/setup.ts imports/extends Vitest's expect, so the test uses Vitest's global expect and runs in Node. </blockquote></details> <details> <summary>examples/convex/app/layout.tsx (1)</summary><blockquote> `35-37`: **StackProvider serializes server app — no RSC serialization issue.** NextStackProvider calls app[stackAppInternalsSymbol].toClientJson() and passes serialized={true} to the client-side StackProviderClient, which reconstructs via StackClientApp[stackAppInternalsSymbol].fromClientJson(...); passing stackServerApp in the Next.js layout (examples/convex/app/layout.tsx) is safe. </blockquote></details> <details> <summary>packages/template/src/lib/stack-app/common.ts (2)</summary><blockquote> `2-2`: **Good: type-only import avoids bundling Convex server code.** --- `31-38`: **Rename to GetCurrentUserOptions looks sound.** </blockquote></details> <details> <summary>packages/template/src/lib/stack-app/apps/interfaces/server-app.ts (2)</summary><blockquote> `28-36`: **useUser overload updates: OK.** --- `38-45`: **getUser overload updates: OK.** </blockquote></details> <details> <summary>packages/template/src/lib/stack-app/apps/interfaces/client-app.ts (6)</summary><blockquote> `59-61`: **Convex client auth helpers: API shape looks good.** --- `63-67`: **useUser overloads migrated to GetCurrentUserOptions: OK.** --- `69-73`: **getUser overloads migrated to GetCurrentUserOptions: OK.** --- `74-78`: **New getPartialUser overloads: OK.** --- `79-83`: **New usePartialUser overloads: OK (see impl note in client).** Please ensure the React-like implementation does not return a Promise for the convex variant (see comment in client-app-impl.ts). --- `107-108`: **Constructor overload addition: OK.** </blockquote></details> <details> <summary>packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts (2)</summary><blockquote> `24-24`: **Import swap to GetCurrentUserOptions: OK.** --- `961-1018`: **useUser: matches new option type and existing behavior.** </blockquote></details> <details> <summary>packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (4)</summary><blockquote> `586-588`: **Minor: local var before return is fine.** --- `1509-1544`: **getUser overload migration and flow: OK.** --- `1546-1593`: **useUser overload migration and flow: OK.** --- `1667-1683`: **Convex client auth helper: return shape and refresh handling look good.** </blockquote></details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
Outdated
Show resolved
Hide resolved
Documentation Changes Required1. stack-app.mdx (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/backend/src/lib/tokens.tsx (3)
82-87
: Do not log raw access tokens or full payloads.Leaking tokens/PII to logs is a security risk. Remove token and sensitive payload fields from logs.
Apply this diff:
- } else if (error instanceof JOSEError) { - console.warn("Unparsable access token. This might be a user error, but if it happens frequently, it's a sign of a misconfiguration.", { accessToken, error }); + } else if (error instanceof JOSEError) { + console.warn("Unparsable access token. If frequent, check configuration.", { error: { name: error.name, message: error.message } }); return Result.error(new KnownErrors.UnparsableAccessToken()); }
91-98
: Align diagnostics with new is_anonymous flag and avoid sensitive logging.Messages still reference legacy "role is anon" and log full payload. Use is_anonymous terminology and omit payload.
Apply this diff:
- const isAnonymous = payload.is_anonymous ?? /* legacy, now we always set role to authenticated, TODO next-release remove */ payload.role === 'anon'; + const isAnonymous = + payload.is_anonymous ?? + /* legacy, now we always set role to authenticated, TODO next-release remove */ (payload as any).role === 'anon'; if (aud.endsWith(":anon") && !isAnonymous) { - console.warn("Unparsable access token. Role is set to anon, but audience is not an anonymous audience.", { accessToken, payload }); + console.warn("Unparsable access token: audience ends with ':anon' but is_anonymous=false."); return Result.error(new KnownErrors.UnparsableAccessToken()); } else if (!aud.endsWith(":anon") && isAnonymous) { - console.warn("Unparsable access token. Audience is not an anonymous audience, but role is set to anon.", { accessToken, payload }); + console.warn("Unparsable access token: is_anonymous=true but audience is not anonymous."); return Result.error(new KnownErrors.UnparsableAccessToken()); }
70-79
: Critical: untrusted 'aud' is being used to select JWKS/issuers — fix requiredpackages/stack-shared/src/utils/jwt.tsx decodes the token and calls getPrivateJwks({ audience }) to build the JWK set (lines ~60–67), and apps/backend/src/lib/tokens.tsx (lines ~70–79) also derives allowedIssuers from the decoded aud before verification. An attacker-controlled aud can therefore influence which keys/issuer are used to verify the token. Derive issuer/audience mapping from trusted configuration (tenant/project lookup) or use a preconfigured issuer→JWKS allowlist; do not use unverified token claims to select keys or trusted issuers.
🧹 Nitpick comments (9)
examples/convex/components/ConvexClientProvider.tsx (1)
4-7
: Optional: add Convex API typing to the client.If the generated API types are available, typing the client improves end-to-end safety.
import { ConvexProvider, ConvexReactClient } from "convex/react"; +// If available: +// import { api } from "@/convex/_generated/api"; @@ -const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL ?? "http://localhost:1234"); +// If you imported `api`, add the generic: +// const convex = new ConvexReactClient<typeof api>(process.env.NEXT_PUBLIC_CONVEX_URL ?? "http://localhost:1234");docs/templates/others/convex.mdx (1)
89-101
: Use StackServerApp for Convex server examples to avoid client-only dependencies.Importing a client instance (configured with "nextjs-cookie") into Convex server functions can pull in client/Next-only code. Prefer a server app instance for server-side ctx flows.
Example replacement snippet:
// convex/myFunctions.ts import { v } from "convex/values"; import { query } from "./_generated/server"; import { StackServerApp } from "@stackframe/stack"; const stackServerApp = new StackServerApp({ projectId: process.env.NEXT_PUBLIC_STACK_PROJECT_ID!, publishableClientKey: process.env.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY!, secretServerKey: process.env.STACK_SECRET_SERVER_KEY!, tokenStore: "memory", }); export const whoAmI = query({ args: {}, handler: async (ctx) => { const user = await stackServerApp.getPartialUser({ from: "convex", ctx }); return user; // null when not authenticated }, });vitest.workspace.ts (1)
7-8
: Gate examples workspace behind an env flag instead of disabling outright.Keeps CI stable while enabling local runs when needed.
Apply this diff:
-import { defineWorkspace } from 'vitest/config'; +import { defineWorkspace } from 'vitest/config'; -export default defineWorkspace([ - 'packages/*', - 'apps/*', - 'docs', - // 'examples/*', there is an issue with examples/convex causing vitest to stall -]); +const workspaces = [ + 'packages/*', + 'apps/*', + 'docs', + ...(process.env.INCLUDE_EXAMPLES ? ['examples/*'] : []), +]; + +// TODO: Track the stalling issue (examples/convex) and re-enable by default once fixed. +export default defineWorkspace(workspaces);apps/backend/src/lib/tokens.tsx (1)
106-113
: Reuse computed isAnonymous instead of recomputing.Avoid duplication; use the local isAnonymous variable.
Apply this diff:
- const result = await accessTokenSchema.validate({ + const result = await accessTokenSchema.validate({ projectId: aud.split(":")[0], userId: payload.sub, branchId: branchId, refreshTokenId: payload.refresh_token_id ?? payload.refreshTokenId, exp: payload.exp, - isAnonymous: payload.is_anonymous ?? /* legacy, now we always set role to authenticated, TODO next-release remove */ payload.role === 'anon', + isAnonymous, });apps/e2e/tests/js/convex.test.ts (3)
57-59
: Avoid hardcoded Convex URL to reduce flakiness.Use env vars with a fallback.
Apply this diff:
- const convex = new ConvexReactClient("http://localhost:1234", { webSocketConstructor: MockWebSocket as any, expectAuth: true }); + const url = process.env.CONVEX_URL ?? process.env.NEXT_PUBLIC_CONVEX_URL ?? "http://localhost:1234"; + const convex = new ConvexReactClient(url, { webSocketConstructor: MockWebSocket as any, expectAuth: true });
87-89
: Avoid hardcoded Convex URL for HTTP client as well.Apply this diff:
- const convex = new ConvexHttpClient("http://localhost:1234"); + const url = process.env.CONVEX_URL ?? process.env.NEXT_PUBLIC_CONVEX_URL ?? "http://localhost:1234"; + const convex = new ConvexHttpClient(url);
124-131
: Prefer inline snapshots for structured objects (repo test guideline).Switch to toMatchInlineSnapshot for the partial user.
Apply this diff:
- expect(partialUser).toEqual({ - id: user.id, - displayName: user.displayName, - primaryEmail: user.primaryEmail, - primaryEmailVerified: user.primaryEmailVerified, - isAnonymous: user.isAnonymous, - }); + expect(partialUser).toMatchInlineSnapshot(` + { + "displayName": ${user.displayName ? `"${user.displayName}"` : "undefined"}, + "id": "${user.id}", + "isAnonymous": ${user.isAnonymous}, + "primaryEmail": "${user.primaryEmail}", + "primaryEmailVerified": ${user.primaryEmailVerified}, + } + `);Note: Adjust serializer placeholders if your snapshot-serializer.ts formats undefined differently.
packages/template/src/lib/stack-app/apps/interfaces/server-app.ts (1)
3-4
: Unify Convex context type to ConvexCtx across the public APIUse the shared ConvexCtx type (from ../../common) instead of importing GenericQueryCtx directly here. This keeps the interface aligned with GetCurrentPartialUserOptions and the server/client implementations.
-import type { GenericQueryCtx } from "convex/server"; -import { AsyncStoreProperty, GetCurrentPartialUserOptions, GetCurrentUserOptions } from "../../common"; +import { AsyncStoreProperty, GetCurrentPartialUserOptions, GetCurrentUserOptions, ConvexCtx } from "../../common"; @@ - useUser(options: { from: "convex", ctx: GenericQueryCtx<any>, or?: "return-null" | "anonymous" }): ServerUser | null, + useUser(options: { from: "convex", ctx: ConvexCtx, or?: "return-null" | "anonymous" }): ServerUser | null, @@ - getUser(options: { from: "convex", ctx: GenericQueryCtx<any>, or?: "return-null" | "anonymous" }): Promise<ServerUser | null>, + getUser(options: { from: "convex", ctx: ConvexCtx, or?: "return-null" | "anonymous" }): Promise<ServerUser | null>,Also applies to: 35-35, 44-44
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (1)
1599-1615
: Minor: unify anonymous gating for token-derived partial user_currently _getTokenPartialUserFromSession performs anonymous gating. If you adopt the cache refactor above, move the gating to the caller (like shown) to keep the cache pure and reusable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
apps/backend/src/lib/tokens.tsx
(4 hunks)apps/e2e/tests/backend/backend-helpers.ts
(1 hunks)apps/e2e/tests/js/convex.test.ts
(1 hunks)docs/templates/others/convex.mdx
(1 hunks)examples/convex/.env.development
(1 hunks)examples/convex/.env.test.local
(1 hunks)examples/convex/components/ConvexClientProvider.tsx
(1 hunks)examples/convex/convex/auth.config.ts
(1 hunks)examples/convex/convex/myFunctions.ts
(1 hunks)examples/convex/package.json
(1 hunks)packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
(6 hunks)packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
(5 hunks)packages/template/src/lib/stack-app/apps/interfaces/client-app.ts
(4 hunks)packages/template/src/lib/stack-app/apps/interfaces/server-app.ts
(2 hunks)packages/template/src/lib/stack-app/common.ts
(3 hunks)vitest.workspace.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- examples/convex/.env.test.local
- examples/convex/.env.development
🚧 Files skipped from review as they are similar to previous changes (4)
- examples/convex/package.json
- apps/e2e/tests/backend/backend-helpers.ts
- examples/convex/convex/auth.config.ts
- examples/convex/convex/myFunctions.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer ES6 Map over Record when representing key–value collections
Files:
vitest.workspace.ts
examples/convex/components/ConvexClientProvider.tsx
apps/e2e/tests/js/convex.test.ts
packages/template/src/lib/stack-app/common.ts
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts
apps/backend/src/lib/tokens.tsx
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
packages/template/src/lib/stack-app/apps/interfaces/server-app.ts
**/*.test.{ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
In tests, prefer .toMatchInlineSnapshot where possible; refer to snapshot-serializer.ts for snapshot formatting and handling of non-deterministic values
Files:
apps/e2e/tests/js/convex.test.ts
packages/template/**
📄 CodeRabbit inference engine (AGENTS.md)
When modifying the SDK copies, make changes in packages/template (source of truth)
Files:
packages/template/src/lib/stack-app/common.ts
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
packages/template/src/lib/stack-app/apps/interfaces/server-app.ts
🧬 Code graph analysis (7)
apps/e2e/tests/js/convex.test.ts (2)
apps/e2e/tests/helpers.ts (1)
it
(11-11)apps/e2e/tests/js/js-helpers.ts (1)
createApp
(41-86)
packages/template/src/lib/stack-app/common.ts (1)
packages/template/src/lib/stack-app/index.ts (2)
GetCurrentUserOptions
(31-31)GetCurrentUserOptions
(33-33)
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (5)
packages/template/src/lib/stack-app/apps/implementations/common.ts (2)
createCache
(22-27)useAsyncCache
(146-191)packages/template/src/lib/stack-app/users/index.ts (3)
TokenPartialUser
(279-286)ProjectCurrentUser
(277-277)SyncedPartialUser
(288-300)packages/template/src/lib/stack-app/common.ts (4)
GetCurrentUserOptions
(31-38)GetCurrentPartialUserOptions
(44-60)ConvexCtx
(40-42)TokenStoreInit
(68-77)packages/stack-shared/src/sessions.ts (1)
InternalSession
(72-242)packages/stack-shared/src/utils/errors.tsx (1)
throwErr
(10-19)
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts (3)
packages/template/src/lib/stack-app/common.ts (3)
TokenStoreInit
(68-77)GetCurrentUserOptions
(31-38)GetCurrentPartialUserOptions
(44-60)packages/template/src/lib/stack-app/index.ts (2)
GetCurrentUserOptions
(31-31)GetCurrentUserOptions
(33-33)packages/template/src/lib/stack-app/users/index.ts (3)
ProjectCurrentUser
(277-277)TokenPartialUser
(279-286)SyncedPartialUser
(288-300)
apps/backend/src/lib/tokens.tsx (3)
packages/stack-shared/src/sessions.ts (2)
payload
(34-37)AccessTokenPayload
(23-23)packages/stack-shared/src/utils/jwt.tsx (1)
signJWT
(38-54)packages/stack-shared/src/utils/env.tsx (1)
getEnvVariable
(16-58)
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts (2)
packages/template/src/lib/stack-app/apps/implementations/common.ts (2)
createCache
(22-27)useAsyncCache
(146-191)packages/template/src/lib/stack-app/common.ts (2)
ConvexCtx
(40-42)GetCurrentUserOptions
(31-38)
packages/template/src/lib/stack-app/apps/interfaces/server-app.ts (3)
packages/template/src/lib/stack-app/common.ts (2)
GetCurrentUserOptions
(31-38)GetCurrentPartialUserOptions
(44-60)packages/template/src/lib/stack-app/index.ts (3)
GetCurrentUserOptions
(31-31)GetCurrentUserOptions
(33-33)ServerUser
(103-103)packages/template/src/lib/stack-app/users/index.ts (4)
ProjectCurrentServerUser
(401-401)ServerUser
(395-395)TokenPartialUser
(279-286)SyncedPartialServerUser
(403-406)
🪛 Biome (2.1.2)
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
[error] 1660-1660: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
[error] 898-898: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 1007-1007: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: docker
- GitHub Check: setup-tests
- GitHub Check: docker
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: restart-dev-and-test
- GitHub Check: build (22.x)
- GitHub Check: Security Check
🔇 Additional comments (13)
examples/convex/components/ConvexClientProvider.tsx (3)
7-7
: Fail fast on missing NEXT_PUBLIC_CONVEX_URL (avoid silent localhost in prod).Defaulting to localhost can mask misconfigurations and break production. Throw in prod; dev can warn and fall back.
-const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL ?? "http://localhost:1234"); +const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL; +if (!convexUrl) { + if (process.env.NODE_ENV === "production") { + throw new Error("NEXT_PUBLIC_CONVEX_URL is not set; configure your Convex deployment URL."); + } else { + console.warn("NEXT_PUBLIC_CONVEX_URL is not set; defaulting to http://localhost:1234 for development."); + } +} +const convex = new ConvexReactClient(convexUrl ?? "http://localhost:1234");
12-18
: Provider setup LGTM — stable singleton client.Module-scoped client avoids re-instantiation; wrapping children with ConvexProvider is correct.
8-10
: Confirm cookie tokenStore security semantics (HttpOnly, SameSite, XSS exposure).If tokens are client-readable cookies, they’re XSS-exposed. Ensure the "nextjs-cookie" store issues HttpOnly cookies (set server-side) with SameSite=Lax/Strict and short-lived access tokens.
Run to locate and inspect the implementation and usages:
packages/template/src/lib/stack-app/common.ts (1)
44-61
: Keep GetCurrentPartialUserOptions intact; only update ConvexCtx reference.No behavior change; just ensure the union references the new ConvexCtx.
Please verify downstream files don’t import "convex/server" due to this type change. If any do, we should migrate them to structural types or guard with peer optional types.
packages/template/src/lib/stack-app/apps/interfaces/client-app.ts (3)
59-61
: Convex client auth helpers: LGTMExposing getConvexClientAuth/getConvexHttpClientAuth on the client interface is clear and consistent with the implementation.
74-82
: Partial-user overloads on client interface: LGTMThe token/convex variants and the general overload map cleanly to the new types (TokenPartialUser, SyncedPartialUser).
105-106
: Constructor overload tidy-up: LGTMThe concrete constructor overload aligns with the rest of the codebase.
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts (3)
155-160
: Solid: cached Convex identity subjectUsing a cache for ctx.auth.getUserIdentity().subject is a good call to avoid repeated provider calls.
881-904
: Convex-based user lookup: LGTM_getUserByConvex/_useUserByConvex mirror token/apiKey paths and properly honor includeAnonymous.
24-24
: Missing server getPartialUser/usePartialUser implementations (interface drift)Interfaces declare getPartialUser/usePartialUser, but this implementation file does not define them. This will break assignment to StackServerApp unless another class fills the gap.
Would you like me to port the client-side partial-user logic here (token-derived via access token payload; convex-derived via ctx.auth.getUserIdentity), plus React-like usePartialUser with a cache, mirroring the client’s _convexPartialUserCache?
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts (3)
590-592
: Minor: LGTM_getSession refactor (assign + return) is fine; no behavioral change.
1670-1686
: Null-safe access to accessToken.tokentokens?.accessToken.token can throw if tokens exists but accessToken is undefined. Use optional chaining on accessToken too.
return async (args: { forceRefreshToken: boolean }) => { const session = await this._getSession(options.tokenStore); if (!args.forceRefreshToken) { const tokens = await session.getOrFetchLikelyValidTokens(20_000); - return tokens?.accessToken.token ?? null; + return tokens?.accessToken?.token ?? null; } const tokens = await session.fetchNewTokens(); - return tokens?.accessToken.token ?? null; + return tokens?.accessToken?.token ?? null; }; @@ const session = await this._getSession(options.tokenStore); const tokens = await session.getOrFetchLikelyValidTokens(20_000); - return tokens?.accessToken.token ?? throwErr("No access token available"); + return tokens?.accessToken?.token ?? throwErr("No access token available");
1617-1629
: Convex identity mapping: avoid provider-specific field namesemail_verified and is_anonymous are not standard across providers; Convex commonly exposes emailVerified. Coerce safely with fallbacks.
return { id: auth.subject, - displayName: auth.name ?? null, - primaryEmail: auth.email ?? null, - primaryEmailVerified: auth.email_verified as boolean, - isAnonymous: auth.is_anonymous as boolean, + displayName: (auth as any).name ?? null, + primaryEmail: (auth as any).email ?? null, + primaryEmailVerified: Boolean((auth as any).emailVerified ?? (auth as any).email_verified ?? false), + isAnonymous: Boolean((auth as any).isAnonymous ?? (auth as any).is_anonymous ?? false), };
packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts
Show resolved
Hide resolved
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
.github/workflows/lint-and-build.yaml (1)
71-73
: Add Convex production env copy — good placement and parity with other examples.To avoid drift across workflows, consider DRYing these repeated env-copy steps via:
- A small composite action (e.g., .github/actions/copy-env) parameterized for test/production.
- Or a single bash step looping over a whitelist of example dirs to copy .env.development -> .env.{test,production}.local.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.github/workflows/e2e-api-tests.yaml
(1 hunks).github/workflows/e2e-source-of-truth-api-tests.yaml
(1 hunks).github/workflows/lint-and-build.yaml
(1 hunks)apps/backend/src/lib/tokens.tsx
(4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer ES6 Map over Record when representing key–value collections
Files:
apps/backend/src/lib/tokens.tsx
🧬 Code graph analysis (1)
apps/backend/src/lib/tokens.tsx (2)
packages/stack-shared/src/sessions.ts (2)
payload
(34-37)AccessTokenPayload
(23-23)packages/stack-shared/src/utils/jwt.tsx (1)
signJWT
(38-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: setup-tests
- GitHub Check: restart-dev-and-test
- GitHub Check: all-good
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: build (22.x)
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: Security Check
🔇 Additional comments (8)
.github/workflows/e2e-api-tests.yaml (1)
87-89
: Approve: Convex test env copy added — source verified.
examples/convex/.env.development exists; the workflow copy step is valid..github/workflows/e2e-source-of-truth-api-tests.yaml (1)
89-90
: Approve — Convex env setup verified.
examples/convex/.env.development exists and workflows (.github/workflows/lint-and-build.yaml, e2e-api-tests.yaml, e2e-source-of-truth-api-tests.yaml) copy it to the expected .env.production.local / .env.test.local files.apps/backend/src/lib/tokens.tsx (6)
16-16
: LGTM! Improved type safety with centralized schema.The import of
AccessTokenPayload
from the shared package centralizes the type definition and ensures consistency across the codebase.
91-91
: Good migration approach with legacy fallback.The implementation correctly prioritizes the new
is_anonymous
field while maintaining backward compatibility with the legacyrole === 'anon'
check. The TODO comment appropriately marks this as a future cleanup task.
112-112
: Consistent with decoding logic.The validation logic correctly mirrors the decoding approach on Line 91, using the same precedence and fallback pattern for the
is_anonymous
field.
153-164
: Excellent refactor: centralized payload construction.This change improves maintainability by:
- Creating a typed payload object that excludes standard JWT fields (
iss
,aud
)- Consolidating all payload fields in one location
- Making the payload structure more explicit and easier to modify
The role is now consistently set to
'authenticated'
with anonymity indicated by theis_anonymous
field, which aligns with the migration strategy.
170-170
: Clean payload usage.The
signJWT
call now uses the centralized payload object, making the code more readable and maintainable.
91-98
: Verify consistency of anonymous user handling across the codebase. The role→is_anonymous migration in apps/backend/src/lib/tokens.tsx looks implemented; automated search here couldn't scan files — run a repo-wide search for "role === 'anon'", "payload.role", "is_anonymous|isAnonymous", "AccessTokenPayload", "aud.endsWith(':anon')" and update all token consumers, JWT verification code, types and tests to use is_anonymous (remove legacy role-based checks).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/stack-shared/src/sessions.ts (2)
146-156
: Bug: 0ms expiry not caught due to truthiness checkIf a freshly fetched token expires immediately (0ms), the current check won’t throw. Compare directly.
- const expiresInMillis = newTokens?.accessToken.expiresInMillis; - if (expiresInMillis && expiresInMillis < minMillisUntilExpiration) { + if (newTokens && newTokens.accessToken.expiresInMillis < minMillisUntilExpiration) { throw new StackAssertionError(`Required access token expiry ${minMillisUntilExpiration}ms is too long; access tokens are too short when they're generated (${expiresInMillis}ms)`); }Note: Adjust the error message to compute expiresInMillis inline, or compute it unconditionally and compare without a truthiness guard.
189-197
: Critical: sessions with only an access token can never return the cached tokenEarly-returning null when there’s no refresh token breaks valid sessions that only carry an access token (documented as supported earlier in this file). getOrFetchLikelyValidTokens will then attempt refresh (impossible) and return null.
Fix:
private _getPotentiallyInvalidAccessTokenIfAvailable(): AccessToken | null { - if (!this._refreshToken) return null; if (this.isKnownToBeInvalid()) return null; const accessToken = this._accessToken.get(); if (accessToken && !accessToken.isExpired()) return accessToken; return null; }
🧹 Nitpick comments (3)
packages/stack-shared/src/schema-fields.ts (1)
660-674
: selected_team_id: consider validating as UUIDIf this is always a team ID, using uuid() makes bugs surface earlier.
Proposed change:
- selected_team_id: yupString().defined().nullable(), + selected_team_id: yupString().uuid().defined().nullable(),packages/stack-shared/src/sessions.ts (2)
25-28
: Missing exp currently treated as “never expires”Combined with optional exp in the schema, tokens lacking exp appear valid indefinitely. Either make exp required (preferred) or treat missing exp as expired here.
If keeping schema as-is, apply:
get expiresAt(): Date { const { exp } = this.payload; - if (exp === undefined) return new Date(8640000000000000); // max date value + if (exp === undefined) return new Date(0); // treat as already expired return new Date(exp * 1000); }
125-132
: Use consistent error type for too-long expiry requirementElsewhere you throw StackAssertionError for the same condition; align here for consistency.
getAccessTokenIfNotExpiredYet(minMillisUntilExpiration: number): AccessToken | null { if (minMillisUntilExpiration > 60_000) { - throw new Error(`Required access token expiry ${minMillisUntilExpiration}ms is too long; access tokens are too short to be used for more than 60s`); + throw new StackAssertionError(`Required access token expiry ${minMillisUntilExpiration}ms is too long; access tokens are too short to be used for more than 60s`); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
examples/convex/.env.development
(1 hunks)examples/convex/.eslintrc.js
(1 hunks)examples/convex/components/ConvexClientProvider.tsx
(1 hunks)examples/convex/package.json
(1 hunks)packages/stack-shared/src/schema-fields.ts
(2 hunks)packages/stack-shared/src/sessions.ts
(3 hunks)vitest.workspace.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- examples/convex/.eslintrc.js
🚧 Files skipped from review as they are similar to previous changes (4)
- vitest.workspace.ts
- examples/convex/package.json
- examples/convex/.env.development
- examples/convex/components/ConvexClientProvider.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer ES6 Map over Record when representing key–value collections
Files:
packages/stack-shared/src/sessions.ts
packages/stack-shared/src/schema-fields.ts
🧬 Code graph analysis (1)
packages/stack-shared/src/sessions.ts (1)
packages/stack-shared/src/schema-fields.ts (1)
accessTokenPayloadSchema
(660-674)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: docker
- GitHub Check: all-good
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: setup-tests
- GitHub Check: restart-dev-and-test
- GitHub Check: Security Check
🔇 Additional comments (5)
packages/stack-shared/src/schema-fields.ts (3)
2-2
: Direct import path for KnownErrors looks goodReduces ambiguity and potential cycles. No further action.
5-5
: Type-only import optimization is correctRemoves runtime import weight and clarifies intent.
660-674
: Verified no “anon” role usage remains Ripgrep found no instances ofrole: "anon"
in TS/JS code.packages/stack-shared/src/sessions.ts (2)
2-4
: New AccessTokenPayload CDAC type from schema is goodTightens typing and centralizes validation.
19-22
: Decoding JWT without signature verification: confirm intended scopedecodeJwt() doesn’t verify signatures. Fine for non-security display/expiry checks, but ensure no security decisions depend on payload here.
If any downstream logic authorizes based on this payload, switch to verified decoding or guard with server-side checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
examples/convex/convex/auth.config.ts (2)
3-7
: Mark the exported config as constPrevents accidental mutation and improves TS inference for Convex config objects.
Apply this diff:
export default { providers: getConvexProvidersConfig({ projectId: process.env.NEXT_PUBLIC_STACK_PROJECT_ID }), -} +} as const;
5-6
: Guard against undefined env var to avoid passing an undefined projectIdIf the helper treats undefined vs omission differently, prefer omitting the field when unset.
Apply this diff:
- providers: getConvexProvidersConfig({ - projectId: process.env.NEXT_PUBLIC_STACK_PROJECT_ID - }), + providers: getConvexProvidersConfig( + process.env.NEXT_PUBLIC_STACK_PROJECT_ID + ? { projectId: process.env.NEXT_PUBLIC_STACK_PROJECT_ID } + : {} + ),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
docs/templates/others/convex.mdx
(1 hunks)examples/convex/convex/auth.config.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/templates/others/convex.mdx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Prefer ES6 Map over Record when representing key–value collections
Files:
examples/convex/convex/auth.config.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: docker
- GitHub Check: setup-tests
- GitHub Check: build (22.x)
- GitHub Check: build (22.x)
- GitHub Check: all-good
- GitHub Check: lint_and_build (latest)
- GitHub Check: docker
- GitHub Check: restart-dev-and-test
- GitHub Check: Security Check
🔇 Additional comments (2)
examples/convex/convex/auth.config.ts (2)
3-7
: Good move: using the shared helper removes hard-coded URLs and mixed originsThis addresses earlier feedback and keeps the example aligned with the SDK.
1-1
: No action required: export path verified getConvexProvidersConfig is re-exported by@stackframe/stack
(in packages/template/src/index.ts); the example import is correct.
Important
Add Convex integration with new auth helpers, update access token handling, and include documentation, examples, and tests for the new features.
client-app-impl.ts
andserver-app-impl.ts
.is_anonymous
for better anonymous handling intokens.tsx
.docs/templates/others/convex.mdx
.docs/docs-platform.yml
anddocs/templates/meta.json
.examples/convex
with auth wiring, functions, schema, and UI.convex.test.ts
.backend-helpers.ts
andanonymous-comprehensive.test.ts
.package.json
files.e2e-api-tests.yaml
ande2e-source-of-truth-api-tests.yaml
.This description was created by
for aa0983a. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit
New Features
Documentation
Examples
Tests
Chores