Skip to content

fix: redirect logins to APP_ORIGIN - #84

Open
uniqueg wants to merge 2 commits into
devfrom
fix/callback-origin
Open

uniqueg wants to merge 2 commits into
devfrom
fix/callback-origin

Conversation

@uniqueg

@uniqueg uniqueg commented Nov 14, 2026 •

Copy link
Copy Markdown
Member

Closes #83

Summary

The login callback built its redirects from request.url. In the image, the standalone server sees its listening address as that origin, so after a login the browser went to http://0.0.0.0:3000/dashboard, without the session.

  • Redirects: the callback now redirects to APP_ORIGIN. It falls back to the origin of the request, with a log line, if APP_ORIGIN is unset or isn't a URL.
  • Tests: unit tests cover all 5 redirects of the callback with a request at the listening address, plus both fallbacks.
  • Docs: the configuration reference now says what APP_ORIGIN is used for.

Found by the end-to-end tests against the image (#79, PR #82, which carries these two commits until this PR is merged).

Verification

  • Unit tests: npm run test:coverage → 124 passed, 100% coverage.
  • Lint and types: bun run lint and bunx tsc --noEmit → clean.
  • Image, before the fix: the e2e journey against an image built from dev → 5 of 11 Chromium tests failed at the redirect after the login.
  • Image, after the fix: with the fix → 22/22 in Chromium and Firefox.
  • CI on PR test: run the end-to-end tests on the image #82: green with these commits, including End-to-end tests (image).
  • Reviews: adversarial-reviewer and quality-reviewer, two rounds; all findings are fixed.

Declined review findings

None.

Open questions

None.

🤖 Generated with Claude Code

Summary by Sourcery

Ensure OIDC callback redirects use the configured application origin while retaining a safe request-origin fallback.

Bug Fixes:

  • Redirect OIDC login callback responses to the configured APP_ORIGIN so standalone deployments return users to the browser-facing application instead of the server listening address.
  • Fall back to the request origin, with an error log for invalid configuration, when APP_ORIGIN is unset or invalid.

Documentation:

  • Document that APP_ORIGIN is the browser-facing origin used for login redirects and logout validation.

Tests:

  • Add coverage for all callback redirect paths using the configured origin and for missing or invalid APP_ORIGIN fallbacks.

uniqueg and others added 2 commits November 14, 2026 23:31
The login callback built its redirects from request.url. The image's
standalone server sees its listening address as the origin of requests
(http://0.0.0.0:3000), so after a login it sent the browser to
http://0.0.0.0:3000/dashboard, where the session cookies (set for the
real origin) don't apply. The redirects now go to APP_ORIGIN, and to the
origin of the request only when it isn't set. Found by running the
end-to-end tests against the image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An APP_ORIGIN without a scheme made the callback throw (a 500, or a
misleading log line in the token error path). It now logs the problem
and redirects to the origin of the request, as without the setting.
The tests cover every redirect of the callback with a request at the
listening address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 14, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
krini Error Error Nov 14, 2026 10:58pm UTC

@sourcery-ai

sourcery-ai Bot commented Nov 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

API

Build every callback redirect from APP_ORIGIN, falling back to the request origin when the setting is missing or invalid.

  • src/app/auth/api/callback/route.ts L6-28 (+5 small edits)

Documentation

Explain that APP_ORIGIN is the browser-facing origin used for login redirects and logout requests.

  • docs/operator-guide/configuration.md L49-51

Tests

Exercise redirects for each callback outcome and verify fallback behavior when APP_ORIGIN is absent or invalid.

  • src/app/auth/api/callback/route.test.ts L38-48, L60-63, L106-147 (+1 small edits)

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Nov 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (6d208e1) to head (7899f7e).

Additional details and impacted files
@@            Coverage Diff            @@
##               dev       #84   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           12        12           
  Lines          273       279    +6     
  Branches        77        78    +1     
=========================================
+ Hits           273       279    +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@uniqueg
uniqueg marked this pull request as ready for review November 14, 2026 23:00
@uniqueg
uniqueg requested a review from AnonymousXC as a code owner November 14, 2026 23:00
@uniqueg

uniqueg commented Nov 14, 2026

Copy link
Copy Markdown
Member Author

@AnonymousXC could you review this one? It changes app code (the login callback), so it's yours to decide and merge. The new end-to-end tests against the Docker image (#79, PR #82) found that after a login the browser was sent to the server's listening address (http://0.0.0.0:3000/dashboard) instead of the app's origin. This PR redirects to APP_ORIGIN. #82 carries the same two commits until this one is merged; after that, #82 only adds the image tests.

@sourcery-ai

sourcery-ai Bot commented Nov 14, 2026

Copy link
Copy Markdown

Linked issues

Checked at commit 7899f7e

#83 fix: login redirects to the server's listening address · 3 of 5 met · 1 to check by hand

# Requirement Status Notes
1 Redirect after login and on callback errors to APP_ORIGIN, even when the server sees another origin. ✅ Met All five callback redirect paths use appUrl, which builds the URL from APP_ORIGIN when it is valid.
2 Fall back to the request origin when APP_ORIGIN is unset or isn't a URL, and log the fallback. 🟡 Partly met The callback falls back for both cases, but only logs when APP_ORIGIN is invalid; it does not log when the setting is unset.
3 Add unit tests with a request at http://0.0.0.0:3000. ✅ Met The tests exercise the callback with the listening address as the request origin.
4 Document in the configuration reference that login sends users back to APP_ORIGIN. ✅ Met The configuration reference describes APP_ORIGIN as the destination for login redirects.
5 The end-to-end journey passes against the image. 👀 Check by hand (runtime) The PR reports that the image journey passed; confirm the end-to-end journey against the built image.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/app/auth/api/callback/route.ts" line_range="89" />
<code_context>

         const responseRedirect = NextResponse.redirect(
-            new URL("/dashboard", request.url),
+            appUrl("/dashboard", request),
         );

</code_context>
<issue_to_address>
**Cross-origin redirect drops session**

When the browser-facing origin in `OIDC_REDIRECT_URI` differs from `APP_ORIGIN`, `appUrl` sends the browser to `APP_ORIGIN`, but the callback response sets host-only session cookies for the origin at which the browser reached the callback. When those origins differ, the browser does not send the cookies to the dashboard, so the user is not logged in there.

Validate that the public callback and application origins match, or configure cookie scope so the session is available on both origins.
</issue_to_address>

### Comment 2
<location path="src/app/auth/api/callback/route.ts" line_range="19" />
<code_context>
+    const appOrigin = process.env.APP_ORIGIN;
+    if (appOrigin) {
+        try {
+            return new URL(path, new URL(appOrigin).origin);
+        } catch {
+            console.error(
</code_context>
<issue_to_address>
**Dashboard redirects leave the web app**

When APP_ORIGIN is syntactically valid but uses a non-HTTP(S) scheme, `appUrl` accepts the URL and returns a redirect using that scheme, so the browser cannot reach the application dashboard and login fails.

Require `APP_ORIGIN` to use HTTP or HTTPS before returning the redirect, and otherwise use the fallback.
</issue_to_address>

### Comment 3
<location path="src/app/auth/api/callback/route.ts" line_range="26" />
<code_context>
+            );
+        }
+    }
+    return new URL(path, request.url);
+}
+
</code_context>
<issue_to_address>
**Unset origin fallback goes unreported**

When APP_ORIGIN is unset or empty, `appUrl` returns a URL based on `request.url` without logging that it used the fallback, so operators cannot diagnose the configuration behind failed logins.

Log the fallback in the unset-or-empty branch, as the invalid-URL branch already does.

Also at `src/app/auth/api/callback/route.ts:17`.
</issue_to_address>

Sourcery assessment

Approval pending. 2 findings to address first.

Blocking findings: src/app/auth/api/callback/route.ts:89, src/app/auth/api/callback/route.ts:19


Sourcery is free for open source - if you like our reviews please consider sharing them ✨


const responseRedirect = NextResponse.redirect(
new URL("/dashboard", request.url),
appUrl("/dashboard", request),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Cross-origin redirect drops session

When the browser-facing origin in OIDC_REDIRECT_URI differs from APP_ORIGIN, appUrl sends the browser to APP_ORIGIN, but the callback response sets host-only session cookies for the origin at which the browser reached the callback. When those origins differ, the browser does not send the cookies to the dashboard, so the user is not logged in there.

Validate that the public callback and application origins match, or configure cookie scope so the session is available on both origins.

Prompt for AI agents
In `src/app/auth/api/callback/route.ts` at line 89:

**Cross-origin redirect drops session**

When the browser-facing origin in `OIDC_REDIRECT_URI` differs from `APP_ORIGIN`, `appUrl` sends the browser to `APP_ORIGIN`, but the callback response sets host-only session cookies for the origin at which the browser reached the callback. When those origins differ, the browser does not send the cookies to the dashboard, so the user is not logged in there.

Validate that the public callback and application origins match, or configure cookie scope so the session is available on both origins.

const appOrigin = process.env.APP_ORIGIN;
if (appOrigin) {
try {
return new URL(path, new URL(appOrigin).origin);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Dashboard redirects leave the web app

When APP_ORIGIN is syntactically valid but uses a non-HTTP(S) scheme, appUrl accepts the URL and returns a redirect using that scheme, so the browser cannot reach the application dashboard and login fails.

Require APP_ORIGIN to use HTTP or HTTPS before returning the redirect, and otherwise use the fallback.

Prompt for AI agents
In `src/app/auth/api/callback/route.ts` at line 19:

**Dashboard redirects leave the web app**

When APP_ORIGIN is syntactically valid but uses a non-HTTP(S) scheme, `appUrl` accepts the URL and returns a redirect using that scheme, so the browser cannot reach the application dashboard and login fails.

Require `APP_ORIGIN` to use HTTP or HTTPS before returning the redirect, and otherwise use the fallback.

);
}
}
return new URL(path, request.url);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚪ Low · Unset origin fallback goes unreported

When APP_ORIGIN is unset or empty, appUrl returns a URL based on request.url without logging that it used the fallback, so operators cannot diagnose the configuration behind failed logins.

Log the fallback in the unset-or-empty branch, as the invalid-URL branch already does.

Also at src/app/auth/api/callback/route.ts:17.

Prompt for AI agents
In `src/app/auth/api/callback/route.ts` at line 26:

**Unset origin fallback goes unreported**

When APP_ORIGIN is unset or empty, `appUrl` returns a URL based on `request.url` without logging that it used the fallback, so operators cannot diagnose the configuration behind failed logins.

Log the fallback in the unset-or-empty branch, as the invalid-URL branch already does.

Also at `src/app/auth/api/callback/route.ts:17`.

From #83:

Fall back to the request origin when APP_ORIGIN is unset or isn't a URL, and log the fallback.

This branch had an error being deployed

1 failed deployment
Preview – krini — 7899f7e3 Deployed Nov 14, 2026 by vercel[bot]
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.

fix: login redirects to the server's listening address

1 participant