Skip to content

fix(plugin-auth): route invitation accept link through the Console base path#2847

Merged
os-zhuang merged 2 commits into
mainfrom
claude/invitation-link-https-prefix-wc05nn
Jul 13, 2026
Merged

fix(plugin-auth): route invitation accept link through the Console base path#2847
os-zhuang merged 2 commits into
mainfrom
claude/invitation-link-https-prefix-wc05nn

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Problem

The "accept invitation" link in invitation emails did not open. On cloud.objectos.ai the link was missing the https:// prefix (relative-looking, unclickable), and it also pointed at the wrong path.

Root cause

sendInvitationEmail built the accept URL straight from config.baseUrl with no scheme guarantee and no UI mount prefix:

const baseUrl = (this.config.baseUrl ?? '').replace(/\/$/, '');
const acceptUrl = `${baseUrl}/accept-invitation/${invitation.id}`;

Two defects:

  1. Missing scheme — when baseUrl is a bare host (e.g. cloud.objectos.ai), the result is relative-looking; email clients won't linkify it.
  2. Missing Console base path — verified against the objectui Console frontend: the accept-invitation page is a React Router route /accept-invitation/:invitationId mounted under the SPA basename (import.meta.env.BASE_URL = /_console/), the same router that serves /login, /register, /oauth/consent. The Console's own "copy invitation link" action builds ${origin}${BASE_URL}accept-invitation/<id>. The emailed root-path link omitted that prefix and 404'd at the host root.

Fix

The link is now built to match the frontend's canonical form:

const uiBase = (this.config.uiBasePath ?? '/_console').replace(/\/$/, '');
const acceptUrl = `${this.getCanonicalOrigin()}${uiBase}/accept-invitation/${invitation.id}`;
  • getCanonicalOrigin() is hardened to always return an absolute origin — it prepends https:// when baseUrl has no scheme, and trims a trailing slash. (This also hardens the OAuth issuer / consent / device-flow URLs that share the helper.)
  • The uiBasePath prefix (default /_console, configurable for Console mounted elsewhere) matches loginPage/consentPage construction already in this file and the frontend router.

Result on cloud.objectos.ai: https://cloud.objectos.ai/_console/accept-invitation/<token>.

Verification

Cross-checked against objectstack-ai/objectui:

  • apps/console/src/App.tsx<Route path="/accept-invitation/:invitationId"> inside <BrowserRouter basename={BASENAME}>, alongside /login and /register.
  • packages/app-shell/.../InvitationsPage.tsx + InviteMemberDialog.tsx — the "copy link" helper builds ${origin}${BASE_URL}accept-invitation/<id>.

Tests

Added three cases (bare host → https://…/_console/…; explicit scheme preserved; custom uiBasePath honoured). Full @objectstack/plugin-auth suite passes.

Changeset

@objectstack/plugin-auth: patch.

🤖 Generated with Claude Code

The invitation email built the accept URL directly from `config.baseUrl`
with no scheme guarantee. When `baseUrl` is a bare host (e.g.
`cloud.objectos.ai`), the emailed link was relative-looking and email
clients wouldn't linkify it — clicking led nowhere.

Route the accept URL through `getCanonicalOrigin()` and have that helper
guarantee an absolute origin: prepend `https://` when the configured
baseUrl has no scheme. This also hardens the OAuth issuer / consent /
device-flow URLs that share the same helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NT4mx1fHXtkxery6MtLTsp
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 12, 2026 2:17pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-auth.

9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-auth)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-auth)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/plugin-auth)
  • content/docs/permissions/authentication.mdx (via @objectstack/plugin-auth)
  • content/docs/permissions/sso.mdx (via @objectstack/plugin-auth)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-auth)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-auth)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-auth)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…se path

Verified against the objectui Console frontend: the accept-invitation page is
a React Router route (`/accept-invitation/:invitationId`) mounted under the
SPA basename (`import.meta.env.BASE_URL` = `/_console/`), the same router that
serves `/login` and `/register`. The Console's own "copy invitation link"
action builds `${origin}${BASE_URL}accept-invitation/<id>`.

The emailed link now matches that canonical form:
`${origin}${uiBasePath}/accept-invitation/<id>`, where the origin is forced
absolute (https:// prepended when baseUrl lacks a scheme) and uiBasePath
defaults to `/_console`. Previously the link was `${baseUrl}/accept-invitation/
<id>` — missing both the scheme (relative-looking, unclickable in email) and
the Console prefix (404 at the host root).

Added tests for the bare-host, explicit-scheme, and custom-uiBasePath cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NT4mx1fHXtkxery6MtLTsp
@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Jul 12, 2026
@os-zhuang os-zhuang changed the title fix(plugin-auth): make invitation accept URL an absolute https link fix(plugin-auth): route invitation accept link through the Console base path Jul 12, 2026
@os-zhuang os-zhuang marked this pull request as ready for review July 13, 2026 00:27
@os-zhuang os-zhuang merged commit 3fd87b2 into main Jul 13, 2026
16 checks passed
@os-zhuang os-zhuang deleted the claude/invitation-link-https-prefix-wc05nn branch July 13, 2026 00:27
os-zhuang pushed a commit that referenced this pull request Jul 13, 2026
…ps origin

Follow-up to the invitation-link fix (#2847). Audited every backend site that
builds a user-facing link and found the same latent flaw: they read the raw
`config.baseUrl`, so a bare-host value (no scheme) yielded relative-looking,
unclickable links. Routed them all through the hardened getCanonicalOrigin():

- better-auth `baseURL` (583) — source of the reset-password / verify-email /
  magic-link email links.
- OAuth `loginPage` / `consentPage` (1723-1724).
- Device-authorization `verificationUri` (1811).
- Phone-invite SMS `{{baseUrl}}` (sendPhoneInviteSms, 2201).

Added tests: bare host promoted to https, explicit scheme/trailing slash
preserved (getAuthIssuer), and better-auth baseURL carries the scheme.
Full @objectstack/plugin-auth suite passes (406 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NT4mx1fHXtkxery6MtLTsp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants