Skip to content

feat: add Descope#7756

Open
mariamantsurova wants to merge 2 commits into
TanStack:mainfrom
mariamantsurova:docs/add-start-descope-example
Open

feat: add Descope#7756
mariamantsurova wants to merge 2 commits into
TanStack:mainfrom
mariamantsurova:docs/add-start-descope-example

Conversation

@mariamantsurova

@mariamantsurova mariamantsurova commented Jul 8, 2026

Copy link
Copy Markdown

This PR introduces a TanStack Start sample app showcasing Descope authentication and adds it to the documentation.

Cap.2026-07-08.at.14.47.43.mp4

Summary by CodeRabbit

  • New Features

    • Added a new React authentication example with sign-in, protected profile, and logout flows.
    • Included session-aware navigation, error, and not-found screens for a smoother user experience.
    • Added setup support for running the example locally in a dev container or standard development environment.
  • Documentation

    • Updated the getting-started docs and example listings to include the new React example.
    • Added setup instructions, environment variable guidance, and run/build steps for the example.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​descope/​react-sdk@​2.30.3981009599100
Added@​descope/​node-sdk@​2.12.0981009999100

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Medium
Low adoption: npm @descope/outbound-applications-widget

Location: Package overview

From: pnpm-lock.yamlnpm/@descope/react-sdk@2.30.3npm/@descope/outbound-applications-widget@0.5.2

ℹ Read more on: This package | This alert | What are unpopular packages?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Unpopular packages may have less maintenance and contain other problems.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@descope/outbound-applications-widget@0.5.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a new TanStack Start React example (start-descope) demonstrating Descope authentication integration, including server-side session resolution via cookies, request middleware, protected routes, login/logout flows, a profile page with the Descope UserProfile widget, and supporting docs, config, and workspace registration.

Changes

Descope Example App

Layer / File(s) Summary
Docs registration
docs/start/config.json, docs/start/framework/react/getting-started.md
Adds a Descope example entry to the docs config and getting-started examples list.
Project scaffolding
examples/react/start-descope/.devcontainer/..., .env.example, .gitignore, .prettierignore, .vscode/settings.json, README.md, package.json, tsconfig.json, vite.config.ts, pnpm-workspace.yaml
Creates the example project's dev container, env template, ignore/config files, README, package manifest, TS config, Vite config, and adds reselect to the pnpm trust policy exclude list.
Server session & middleware
src/integrations/descope/session.server.ts, server.ts, middleware.ts, start.ts
Implements cookie-based session resolution/refresh via DescopeClient, context storage/retrieval, getSession/clearServerSession server functions, request middleware, and wiring into the Start instance alongside CSRF middleware.
Client provider
src/integrations/descope/provider.tsx
Adds DescopeProvider wrapping AuthProvider with cookie-based session tokens.
Router setup & route tree
src/router.tsx, src/routeTree.gen.ts
Adds router creation helper and the generated route tree connecting all file routes.
Root route & shared UI
src/routes/__root.tsx, src/components/*, src/utils/seo.ts, src/styles/app.css
Adds root route with session loading, HTML shell, header nav, error/not-found components, logo, SEO helper, and global Tailwind styles.
Protected routes & auth flows
src/routes/_authed*.tsx, src/routes/index.tsx, src/routes/login.tsx, src/routes/logout.tsx
Adds _authed guard, profile page with Descope widget, home page with auth-aware CTAs, and login/logout routes handling sign-in/sign-out.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant descopeMiddleware
  participant resolveSessionFromRequest
  participant DescopeClient
  Browser->>descopeMiddleware: Request with session cookie
  descopeMiddleware->>resolveSessionFromRequest: resolve session
  resolveSessionFromRequest->>DescopeClient: validateAndRefreshSession(token)
  DescopeClient-->>resolveSessionFromRequest: authInfo/claims
  resolveSessionFromRequest-->>descopeMiddleware: SessionUser or null
  descopeMiddleware-->>Browser: request continues with user context
Loading
sequenceDiagram
  participant User
  participant LoginRoute
  participant DescopeWebComponent
  participant Router
  participant LogoutRoute
  User->>LoginRoute: Visit /login
  LoginRoute->>DescopeWebComponent: render sign-up-or-in flow
  DescopeWebComponent-->>LoginRoute: onSuccess
  LoginRoute->>Router: invalidate() and navigate to /
  User->>LogoutRoute: Visit /logout
  LogoutRoute->>DescopeWebComponent: sdk.logout()
  LogoutRoute->>LogoutRoute: clearServerSession()
  LogoutRoute->>Router: invalidate() and navigate to /
Loading

Possibly related PRs

  • TanStack/router#7425: Both PRs modify pnpm-workspace.yaml trust-policy settings for pnpm dependency handling.

Suggested labels: documentation

Suggested reviewers: SeanCassiere

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a new Descope example app and docs entry.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/react/start-descope/.gitignore`:
- Around line 15-16: The .gitignore entry is malformed because the Sentry
comment is attached to the /public/build ignore pattern, so the pattern is not
applied correctly. Update the ignore rules in the .gitignore file for the react
start-descope example so /public/build stands alone and the Sentry Config File
note is placed on its own separate comment line before .env.sentry-build-plugin.

In `@examples/react/start-descope/package.json`:
- Around line 18-19: The dependency versions for `@tanstack/react-router` and
`@tanstack/react-router-devtools` are out of sync, which can lead to
incompatibilities. Update the `@tanstack/react-router-devtools` entry in
package.json to match the same minor version as `@tanstack/react-router`, and keep
the versions aligned so both packages resolve together consistently.

In `@examples/react/start-descope/src/integrations/descope/session.server.ts`:
- Around line 49-55: The rotated session cookie set in session.server.ts via the
SESSION_COOKIE write should be marked secure because it stores a JWT in a
browser cookie. Update the cookie options in the authInfo.jwt rotation path to
include the secure flag alongside the existing httpOnly, sameSite, and path
settings so it is only sent over HTTPS.

In `@examples/react/start-descope/src/routes/logout.tsx`:
- Around line 15-30: The logout flow in doLogout only catches errors from
sdk.logout(), so failures in clearServerSession(), router.invalidate(), or
router.navigate() can reject unhandled and leave the user stuck on the
signing-out screen. Wrap the post-logout cleanup/navigation steps in the same
try/catch (or a second one around the remaining awaits) and ensure errors are
logged while still allowing the UI to recover. Keep the existing
useEffect/doLogout structure, but make the full logout sequence resilient to any
thrown promise from clearServerSession, router.invalidate, and router.navigate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0352abff-733f-405d-86cc-fe8b4b73ce7a

📥 Commits

Reviewing files that changed from the base of the PR and between a3e24c3 and 1156615.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • docs/start/config.json
  • docs/start/framework/react/getting-started.md
  • examples/react/start-descope/.devcontainer/devcontainer.json
  • examples/react/start-descope/.env.example
  • examples/react/start-descope/.gitignore
  • examples/react/start-descope/.prettierignore
  • examples/react/start-descope/.vscode/settings.json
  • examples/react/start-descope/README.md
  • examples/react/start-descope/package.json
  • examples/react/start-descope/src/components/DefaultCatchBoundary.tsx
  • examples/react/start-descope/src/components/DescopeLogo.tsx
  • examples/react/start-descope/src/components/NotFound.tsx
  • examples/react/start-descope/src/integrations/descope/middleware.ts
  • examples/react/start-descope/src/integrations/descope/provider.tsx
  • examples/react/start-descope/src/integrations/descope/server.ts
  • examples/react/start-descope/src/integrations/descope/session.server.ts
  • examples/react/start-descope/src/routeTree.gen.ts
  • examples/react/start-descope/src/router.tsx
  • examples/react/start-descope/src/routes/__root.tsx
  • examples/react/start-descope/src/routes/_authed.tsx
  • examples/react/start-descope/src/routes/_authed/profile.tsx
  • examples/react/start-descope/src/routes/index.tsx
  • examples/react/start-descope/src/routes/login.tsx
  • examples/react/start-descope/src/routes/logout.tsx
  • examples/react/start-descope/src/start.ts
  • examples/react/start-descope/src/styles/app.css
  • examples/react/start-descope/src/utils/seo.ts
  • examples/react/start-descope/tsconfig.json
  • examples/react/start-descope/vite.config.ts
  • pnpm-workspace.yaml

Comment on lines +15 to +16
/public/build# Sentry Config File
.env.sentry-build-plugin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the malformed ignore entry.

/public/build# Sentry Config File is parsed as a literal pattern, so /public/build is never ignored. Put the comment on its own line.

Proposed fix
-/public/build# Sentry Config File
+/public/build
+# Sentry Config File
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/public/build# Sentry Config File
.env.sentry-build-plugin
/public/build
# Sentry Config File
.env.sentry-build-plugin
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/start-descope/.gitignore` around lines 15 - 16, The .gitignore
entry is malformed because the Sentry comment is attached to the /public/build
ignore pattern, so the pattern is not applied correctly. Update the ignore rules
in the .gitignore file for the react start-descope example so /public/build
stands alone and the Sentry Config File note is placed on its own separate
comment line before .env.sentry-build-plugin.

Comment on lines +18 to +19
"@tanstack/react-router": "^1.170.17",
"@tanstack/react-router-devtools": "^1.167.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Version mismatch between @tanstack/react-router and @tanstack/react-router-devtools.

@tanstack/react-router is at ^1.170.17 while @tanstack/react-router-devtools is at ^1.167.0. The devtools package should track the same minor version as the router to avoid API incompatibilities. With ^ ranges, the devtools could resolve to a version several minors behind the router.

🔧 Proposed fix
-    "`@tanstack/react-router-devtools`": "^1.167.0",
+    "`@tanstack/react-router-devtools`": "^1.170.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@tanstack/react-router": "^1.170.17",
"@tanstack/react-router-devtools": "^1.167.0",
"`@tanstack/react-router`": "^1.170.17",
"`@tanstack/react-router-devtools`": "^1.170.0",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/start-descope/package.json` around lines 18 - 19, The
dependency versions for `@tanstack/react-router` and
`@tanstack/react-router-devtools` are out of sync, which can lead to
incompatibilities. Update the `@tanstack/react-router-devtools` entry in
package.json to match the same minor version as `@tanstack/react-router`, and keep
the versions aligned so both packages resolve together consistently.

Comment on lines +49 to +55
if (authInfo.jwt && authInfo.jwt !== sessionToken) {
setCookie(SESSION_COOKIE, authInfo.jwt, {
httpOnly: false,
sameSite: 'lax',
path: '/',
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Target file ==\n'
git ls-files 'examples/react/start-descope/src/integrations/descope/session.server.ts'
printf '\n== File outline ==\n'
ast-grep outline examples/react/start-descope/src/integrations/descope/session.server.ts --view expanded || true
printf '\n== Relevant lines ==\n'
nl -ba examples/react/start-descope/src/integrations/descope/session.server.ts | sed -n '1,140p'
printf '\n== Search for SESSION_COOKIE and setCookie usage ==\n'
rg -n "SESSION_COOKIE|setCookie\\(" examples/react/start-descope/src/integrations/descope -S
printf '\n== Search for cookie options / secure / maxAge nearby ==\n'
rg -n "secure:|maxAge:|expires:|sameSite:|httpOnly:" examples/react/start-descope/src/integrations/descope -S

Repository: TanStack/router

Length of output: 741


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== session.server.ts (numbered) ==\n'
sed -n '1,140p' examples/react/start-descope/src/integrations/descope/session.server.ts | awk '{printf "%4d  %s\n", NR, $0}'

printf '\n== setCookie definition / cookie helper usage ==\n'
rg -n "export function setCookie|function setCookie|const setCookie|setCookie\\s*=|SESSION_COOKIE" examples/react/start-descope/src -S

printf '\n== Descope cookie lifetime hints ==\n'
rg -n "SessionTokenCookieName|RefreshTokenCookieName|maxAge|expires|secure" examples/react/start-descope/src -S

Repository: TanStack/router

Length of output: 4197


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Search for React Start cookie helper docs/usages in repo ==\n'
rg -n "`@tanstack/react-start/server`|setCookie\\(" . -S

printf '\n== Package versions ==\n'
rg -n '"`@tanstack/react-start`"|`@descope/node-sdk`' package.json examples -S

printf '\n== Search for secure cookie assumptions ==\n'
rg -n "sameSite: 'lax'|sameSite: 'none'|secure: true|httpOnly: false|maxAge:" examples packages .github -S

Repository: TanStack/router

Length of output: 29301


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== setCookie implementation ==\n'
sed -n '300,380p' packages/start-server-core/src/request-response.ts | awk '{printf "%4d  %s\n", NR+299, $0}'

printf '\n== cookie helper docs in repo ==\n'
rg -n "setCookie\\(.*secure|cookie options|httpOnly|sameSite|maxAge|expires" packages/start-server-core docs examples -S

printf '\n== Descope example README lines around session handling ==\n'
sed -n '50,95p' examples/react/start-descope/README.md | awk '{printf "%4d  %s\n", NR+49, $0}'

Repository: TanStack/router

Length of output: 10725


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== provider.tsx ==\n'
sed -n '1,120p' examples/react/start-descope/src/integrations/descope/provider.tsx | awk '{printf "%4d  %s\n", NR, $0}'

printf '\n== Descope sessionTokenViaCookie occurrences ==\n'
rg -n "sessionTokenViaCookie|sameSite: 'Lax'|sameSite: 'lax'" examples/react/start-descope docs -S

printf '\n== CookieSerializeOptions definition ==\n'
rg -n "interface CookieSerializeOptions|type CookieSerializeOptions" packages/start-server-core packages -S

Repository: TanStack/router

Length of output: 2145


🌐 Web query:

h3 setCookie default secure maxAge CookieSerializeOptions sameSite Descope sessionTokenViaCookie cookie lifetime

💡 Result:

In the H3 framework, the setCookie utility uses CookieSerializeOptions to define cookie behavior [1][2][3]. By default, H3 sets the cookie path to '/' [2][4], but it does not impose global defaults for attributes like secure, maxAge, or sameSite unless specified in the options object passed to setCookie [3]. Developers should explicitly define these in the options parameter when calling setCookie [3]. For Descope session management, the sessionTokenViaCookie configuration controls whether session tokens are stored in a cookie [5]. When enabled, Descope automatically sets the session token in a cookie (default name: DS) with Secure and HttpOnly flags [6][7][8]. Regarding Descope's specific cookie defaults: - Secure: Defaults to true (the cookie will only be sent over HTTPS connections) [6][7]. - SameSite: Defaults to Strict in the standard web JS SDK [6][7], but defaults to Lax in the @descope/nextjs-sdk to ensure compatibility with middleware [9]. - Cookie Lifetime: The session lifetime is generally managed by the Descope platform's session settings rather than an explicit maxAge set on the client side during creation [8]. However, customization of these attributes (sameSite, secure, cookieName) is supported by passing an object to sessionTokenViaCookie [6][7][9]. If you are customizing these, note that if you provide a configuration object and only specify sameSite, the secure flag will typically default to true [6][7][9].

Citations:


Mark the rotated session cookie as secure. This JWT is stored in a browser cookie, so it should not be sent over plain HTTP.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/start-descope/src/integrations/descope/session.server.ts`
around lines 49 - 55, The rotated session cookie set in session.server.ts via
the SESSION_COOKIE write should be marked secure because it stores a JWT in a
browser cookie. Update the cookie options in the authInfo.jwt rotation path to
include the secure flag alongside the existing httpOnly, sameSite, and path
settings so it is only sent over HTTPS.

Comment on lines +15 to +30
React.useEffect(() => {
async function doLogout() {
try {
// Revoke the session with Descope and clear the client-side tokens.
await sdk.logout()
} catch (err) {
console.error('Descope logout error', err)
}
// Clear the server-readable cookies, then refresh and go home.
await clearServerSession()
await router.invalidate()
await router.navigate({ to: '/' })
}
doLogout()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Unhandled rejection leaves user stuck on "Signing out…" if clearServerSession() or router methods throw.

sdk.logout() is wrapped in try/catch, but clearServerSession(), router.invalidate(), and router.navigate() are not. If the server function fails, the promise rejects unhandled and the user has no way to recover — they're left on a perpetual loading screen.

🛡️ Proposed fix: wrap post-logout operations in try/catch
   React.useEffect(() => {
     async function doLogout() {
       try {
         // Revoke the session with Descope and clear the client-side tokens.
         await sdk.logout()
       } catch (err) {
         console.error('Descope logout error', err)
       }
-      // Clear the server-readable cookies, then refresh and go home.
-      await clearServerSession()
-      await router.invalidate()
-      await router.navigate({ to: '/' })
+      try {
+        // Clear the server-readable cookies, then refresh and go home.
+        await clearServerSession()
+        await router.invalidate()
+        await router.navigate({ to: '/' })
+      } catch (err) {
+        console.error('Logout cleanup error', err)
+        await router.navigate({ to: '/' })
+      }
     }
     doLogout()
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
React.useEffect(() => {
async function doLogout() {
try {
// Revoke the session with Descope and clear the client-side tokens.
await sdk.logout()
} catch (err) {
console.error('Descope logout error', err)
}
// Clear the server-readable cookies, then refresh and go home.
await clearServerSession()
await router.invalidate()
await router.navigate({ to: '/' })
}
doLogout()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
React.useEffect(() => {
async function doLogout() {
try {
// Revoke the session with Descope and clear the client-side tokens.
await sdk.logout()
} catch (err) {
console.error('Descope logout error', err)
}
try {
// Clear the server-readable cookies, then refresh and go home.
await clearServerSession()
await router.invalidate()
await router.navigate({ to: '/' })
} catch (err) {
console.error('Logout cleanup error', err)
await router.navigate({ to: '/' })
}
}
doLogout()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/react/start-descope/src/routes/logout.tsx` around lines 15 - 30, The
logout flow in doLogout only catches errors from sdk.logout(), so failures in
clearServerSession(), router.invalidate(), or router.navigate() can reject
unhandled and leave the user stuck on the signing-out screen. Wrap the
post-logout cleanup/navigation steps in the same try/catch (or a second one
around the remaining awaits) and ensure errors are logged while still allowing
the UI to recover. Keep the existing useEffect/doLogout structure, but make the
full logout sequence resilient to any thrown promise from clearServerSession,
router.invalidate, and router.navigate.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant