Skip to content

fix(account-settings): decode URL-encoded city in active sessions#1503

Open
aadesh18 wants to merge 2 commits into
devfrom
fix/active-sessions-location-url-encoding
Open

fix(account-settings): decode URL-encoded city in active sessions#1503
aadesh18 wants to merge 2 commits into
devfrom
fix/active-sessions-location-url-encoding

Conversation

@aadesh18
Copy link
Copy Markdown
Collaborator

@aadesh18 aadesh18 commented May 27, 2026

Summary

The Active Sessions table in account settings showed locations like San%20Francisco instead of San Francisco.

Vercel percent-encodes its geolocation headers (e.g. x-vercel-ip-city), so a multi-word city arrives URL-encoded. The city name was being stored verbatim, so the raw %20 leaked into the UI.

The fix decodes the city name where the Vercel geo header is read, so recorded sessions store the human-readable name. This also benefits any other consumer of the location data. It falls back to the raw value if it isn't valid percent-encoding, so a stray % can't break things.

Test plan

  • Unit tests (in-source, apps/backend/src/lib/end-users.tsx): simulating Vercel headers with x-vercel-ip-city: San%20Francisco now yields cityName: "San Francisco"; an invalid-encoding value (100% Real City) passes through unchanged instead of throwing. All 8 tests in the file pass.
  • In a Vercel-deployed environment, sign in and open Account Settings → Active Sessions; confirm the Location column shows a plain city name (e.g. San Francisco) with no %20.

Note: this can't be reproduced on localhost because there's no Vercel proxy supplying geo headers (the location shows Unknown). The behavior is covered by the unit tests, which feed the exact headers Vercel sends.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected handling of city name data from hosting-provided location headers so multi-word city names display correctly and invalid percent-encoding no longer causes errors.
  • Tests

    • Added tests to verify URL-decoded city names from location headers and to ensure malformed encodings are safely preserved.

Review Change Stack

Copilot AI review requested due to automatic review settings May 27, 2026 20:56
@vercel
Copy link
Copy Markdown

vercel Bot commented May 27, 2026

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

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-auth-internal-tool Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-auth-mcp Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-auth-skills Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-backend Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-dashboard Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-demo Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-docs Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-preview-backend Ready Ready Preview, Comment May 27, 2026 9:47pm
stack-preview-dashboard Ready Ready Preview, Comment May 27, 2026 9:47pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 07462753-6d31-401e-8f84-cb4516f69ca3

📥 Commits

Reviewing files that changed from the base of the PR and between 9e26ab2 and bec6279.

📒 Files selected for processing (1)
  • apps/backend/src/lib/end-users.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/backend/src/lib/end-users.tsx

📝 Walkthrough

Walkthrough

Backend introduces decodeVercelGeoHeader to safely URL-decode Vercel geo header values and applies it to cityName extraction in both trusted-proxy and fallback geo paths within getBrowserEndUserInfo. Tests verify decoded percent-encoded city names and graceful fallback when decoding fails.

Changes

Vercel Geo Header Decoding

Layer / File(s) Summary
Vercel geo header decoder and integration
apps/backend/src/lib/end-users.tsx
decodeVercelGeoHeader helper URL-decodes Vercel city header values with try/catch fallback to the raw string. Applied to cityName extraction in both the trusted-proxy geo path and the spoofable-geo path (when no trusted proxy is configured). Added Vitest tests that assert percent-encoded multi-word city names are decoded and that invalid percent-encoding preserves the original raw value without throwing.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 I nibble bytes and lightly tap the key,
Decoding cities where spaces used to be,
If percent-encoding trips my little paws,
I hand back the raw with no loud cause,
Hop—geo names tidy, and errors flee.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: decoding URL-encoded city names in the active sessions feature, which directly matches the core fix in the changeset.
Description check ✅ Passed The description is comprehensive, covering the problem, solution, fallback behavior, test coverage, and manual verification steps, though it follows a custom format rather than the minimal template.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/active-sessions-location-url-encoding

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 and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes display of multi-word city names in the Active Sessions table by decoding URL-encoded Vercel geo headers both at ingestion and at render time.

Changes:

  • Decode x-vercel-ip-city (and similar) at the source in end-users.tsx via a new decodeVercelGeoHeader helper, with fallback to the raw value on invalid encoding.
  • Defensively decode the city name at display time in the Active Sessions page with a local decodeCityName helper.
  • Adds in-source unit tests covering both the successful decoding path and the invalid-encoding fallback.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
apps/backend/src/lib/end-users.tsx Adds decodeVercelGeoHeader, applies it to both trusted and spoofed city names, and adds two unit tests.
packages/template/src/components-page/account-settings/active-sessions/active-sessions-page.tsx Adds decodeCityName helper and uses it when rendering session.geoInfo.cityName.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 27, 2026

Greptile Summary

This PR fixes URL-encoded city names (e.g. San%20Francisco) appearing verbatim in the Active Sessions table by decoding Vercel geo headers at two layers: at the ingestion point in the backend so new sessions store human-readable values, and defensively in the UI so existing records already in the database are also rendered correctly.

  • decodeVercelGeoHeader() is added in end-users.tsx and applied when reading x-vercel-ip-city, with a try/catch that passes through invalid percent-encoding unchanged; two unit tests cover both the happy path and the fallback.
  • decodeCityName() — logically identical code — is added in active-sessions-page.tsx and called when the cityName field is truthy, guarding the Unknown fallback path.

Confidence Score: 5/5

Safe to merge — both decode paths fall back to the raw value on error, the unit tests validate both the successful decode and the invalid-encoding fallback, and no existing behavior is broken.

The change is narrow: two small helper functions applied at well-understood call sites. Edge cases (invalid percent-encoding, empty/null values, already-decoded stored data) are all handled. Unit test coverage is present and meaningful.

No files require special attention; the only observation is that the two decode helpers are copies of each other across packages.

Important Files Changed

Filename Overview
apps/backend/src/lib/end-users.tsx Adds decodeVercelGeoHeader() to URL-decode city names at the geo-header ingestion layer; decoding is applied in both the trusted-Vercel path and the spoofable/no-proxy path, with a safe try/catch fallback. Two matching unit tests are included.
packages/template/src/components-page/account-settings/active-sessions/active-sessions-page.tsx Adds a local decodeCityName() helper identical in logic to the backend helper and applies it defensively when rendering the cityName column, so already-stored encoded values are also displayed correctly.

Sequence Diagram

sequenceDiagram
    participant Vercel as Vercel Proxy
    participant Backend as end-users.tsx
    participant DB as Database
    participant UI as ActiveSessionsPage

    Vercel->>Backend: x-vercel-ip-city: San%20Francisco
    Note over Backend: decodeVercelGeoHeader()<br/>San%20Francisco → San Francisco
    Backend->>DB: store cityName: "San Francisco"

    UI->>DB: fetch sessions
    DB-->>UI: "cityName: "San Francisco" (new)<br/>or cityName: "San%20Francisco" (old)"
    Note over UI: decodeCityName()<br/>"San Francisco" → "San Francisco" (no-op)<br/>"San%20Francisco" → "San Francisco" (legacy fix)
    UI-->>UI: display "San Francisco"
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
packages/template/src/components-page/account-settings/active-sessions/active-sessions-page.tsx:11-19
`decodeCityName` is byte-for-byte identical to `decodeVercelGeoHeader` in the backend. Since this template already imports from `@stackframe/stack-shared`, a shared utility would keep the fallback logic in one place. If that's out of scope here, at least a comment noting the duplication would help future maintainers find both sites if the logic ever needs to change.

```suggestion
// Some geo providers (e.g. Vercel) URL-encode city names, so "San Francisco" arrives as "San%20Francisco".
// Decode defensively for display, falling back to the raw value if it isn't valid percent-encoding.
// NOTE: This mirrors `decodeVercelGeoHeader` in apps/backend/src/lib/end-users.tsx; keep them in sync.
function decodeCityName(cityName: string): string {
  try {
    return decodeURIComponent(cityName);
  } catch {
    return cityName;
  }
}
```

Reviews (1): Last reviewed commit: "fix(account-settings): decode URL-encode..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@apps/backend/src/lib/end-users.tsx`:
- Around line 95-96: In decodeVercelGeoHeader, replace the falsy check `if
(!raw)` with an explicit nullish check (e.g., `if (raw == null)`) so that empty
strings are not treated the same as null/undefined; update the early-return
condition inside the function to use that nullish check.

In
`@packages/template/src/components-page/account-settings/active-sessions/active-sessions-page.tsx`:
- Line 211: Replace the truthy check on session.geoInfo?.cityName in the
ActiveSessionsPage JSX so it uses an explicit nullish check: instead of using
the boolean check currently around decodeCityName(session.geoInfo.cityName), use
session.geoInfo?.cityName == null to decide when to render t('Unknown') vs
decodeCityName(session.geoInfo.cityName); keep the call to decodeCityName and
the surrounding Typography element unchanged except for the conditional
expression.
🪄 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: d7580198-dd62-4f43-a950-9a0277b6beee

📥 Commits

Reviewing files that changed from the base of the PR and between c753ab2 and 6a203c4.

📒 Files selected for processing (2)
  • apps/backend/src/lib/end-users.tsx
  • packages/template/src/components-page/account-settings/active-sessions/active-sessions-page.tsx

Comment thread apps/backend/src/lib/end-users.tsx Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Tip: cubic could auto-approve low-risk PRs like this, if it thinks it's safe to merge. Learn more

Re-trigger cubic

The Active Sessions table showed locations like "San%20Francisco" instead
of "San Francisco". Vercel percent-encodes its geolocation headers, and the
city name was stored verbatim without decoding.

Decode the city name where the Vercel geo header is read, so recorded sessions
store the human-readable name. Falls back to the raw value if it isn't valid
percent-encoding, so a stray "%" can't break things.
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.

3 participants