Skip to content

Add polished OAuth success page#37

Open
salmonumbrella wants to merge 1 commit intoRoamJS:mainfrom
salmonumbrella:oauth-success-page
Open

Add polished OAuth success page#37
salmonumbrella wants to merge 1 commit intoRoamJS:mainfrom
salmonumbrella:oauth-success-page

Conversation

@salmonumbrella
Copy link

@salmonumbrella salmonumbrella commented Mar 3, 2026

Summary

  • Adds a proposed replacement for the roamjs.com/oauth?auth=true redirect page that users see after authorizing their Google account
  • Currently the page is bare — this adds a polished success screen with the RoamJS logo, a "Google Account Connected to Roam Research" heading, and a subtle blinking cursor animation on the "Roam" badge
  • Includes the OAuth relay JavaScript (postMessage for browser popup flow, session POST for desktop app flow) so it's a functional drop-in replacement

Details

The HTML file at static/oauth-success.html is self-contained and ready to be hosted at roamjs.com/oauth. It handles:

  1. Browser flow: Relays the authorization code and state back to the Roam window via postMessage
  2. Desktop flow: Decodes the session from state and POSTs to /oauth/session for the extension to poll
  3. Error display: Shows a message if Google returns an error
pr-for-the-droid.mp4

Preview

The page features:

  • Centered RoamJS logo with a checkmark badge
  • Fade-in animation on load
  • Monospace "Roam" badge with a blinking cursor and hand-drawn underlines

Test plan

  • Open static/oauth-success.html locally to verify the visual design
  • Verify the postMessage relay works by testing the full OAuth flow with this page hosted at the redirect URI
  • Test desktop app flow (session polling path)

Open with Devin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

Warning

Rate limit exceeded

@mdroidian has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 25 minutes and 26 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4b9fe8c3-60b0-49b0-93b1-61a98fe3b519

📥 Commits

Reviewing files that changed from the base of the PR and between 5d19243 and b0cad41.

📒 Files selected for processing (1)
  • static/oauth-success.html

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.

@salmonumbrella
Copy link
Author

Hey @mdroidian — this is a proposed upgrade for the roamjs.com/oauth?auth=true redirect page. Right now it's pretty bare after Google auth completes. This adds a polished success screen with the RoamJS logo, a blinking cursor animation, and clear messaging. The HTML is self-contained with the OAuth relay JS included, so it should be a drop-in replacement. Let me know what you think!

@mdroidian
Copy link
Contributor

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

✅ Actions performed

Full review triggered.

@mdroidian mdroidian self-requested a review March 15, 2026 19:00
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

// Browser popup flow: relay via postMessage to opener
if (window.opener) {
try {
window.opener.postMessage(payload, "*");

Choose a reason for hiding this comment

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

🔴 postMessage sends OAuth authorization code with wildcard target origin, enabling code theft

The postMessage on line 185 uses "*" as the target origin, which sends the sensitive OAuth authorization code to any window that happens to be window.opener, without verifying its origin.

Attack scenario: A malicious page calls window.open("https://accounts.google.com/o/oauth2/v2/auth?client_id=<ROAMJS_CLIENT_ID>&redirect_uri=<ROAMJS_REDIRECT>&..."). The user sees Google's legitimate consent screen for the RoamJS app and authenticates. Google redirects to oauth-success.html with the authorization code. Since window.opener is the attacker's page, postMessage(payload, "*") delivers the code to the attacker. The attacker can then exchange this code for tokens via the anonymous google-auth endpoint (src/components/GoogleOauthPanel.tsx:123-131 uses anonymous: true), gaining full access to the user's Google account with the granted scopes.

Recommended fix

The state parameter already contains the initiator's origin (encoded at GoogleOauthPanel.tsx:53). The decoded stateObj.origin should be used as the postMessage target origin instead of "*". While an attacker could encode their own origin in the state, this still provides defense-in-depth because the receiver at GoogleOauthPanel.tsx:228 validates the full state value matches what it originally generated (including the nonce), so a tampered state would be rejected by the legitimate receiver anyway. The key improvement is that the code is no longer broadcast to arbitrary origins.

Prompt for agents
In static/oauth-success.html, line 185, change the postMessage target origin from "*" to a specific origin extracted from the decoded state parameter. The state object (decoded earlier in the try/catch block at lines 170-178) contains an `origin` field set by the initiating page (see src/components/GoogleOauthPanel.tsx:53). Store `stateObj.origin` in a variable (e.g., `var targetOrigin`) alongside the existing `session` variable at line 175, defaulting to "*" if decoding fails. Then on line 185, use that variable instead of "*": `window.opener.postMessage(payload, targetOrigin)`. This prevents the authorization code from being delivered to an unexpected opener origin.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor

@mdroidian mdroidian left a comment

Choose a reason for hiding this comment

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

Thank you for the contribution!

Unfortunately, this PR is targeting the wrong repo for the change it describes.

The Google extension sends users to https://roamjs.com/oauth?auth=true from GoogleOauthPanel.tsx, but the actual callback page for /oauth is implemented in the website repo at oauth/page.tsx. Adding static/oauth-success.htmlhere inroamjs/google` does not replace the live page unless that file is separately ported into the website app and deployed from there.

There are also a couple of behavior differences from the current live implementation that should be preserved if this gets moved:

  • The current page only posts messages to trusted origins, while this version uses postMessage(..., "*").
  • The current page forwards the full auth params and handles desktop-session error propagation more completely; this version only relays { code, state } in popup flow.

Requesting changes so this can be moved to the correct repo (website) and reconciled with the existing callback/security behavior before merge and a loom video testing the full login flow on both the browser and app (if any logic is changed).

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.

2 participants