fix(auth): strip internal port from OAuth redirects on BYOD domains (#728)
* fix(auth): strip internal port from OAuth redirects on BYOD domains When BYOD custom domains proxy auth requests through the dashboard to PRIVATE_SERVER_URL (e.g. http://cio-api-vymh:10000), Node's URL host setter preserves the upstream port. Better Auth then redirects browsers to https://customer.example:10000/... which fails. - Add oauth-proxy-redirect helpers to clear backend ports explicitly - Rewrite inbound auth request URLs and outbound oauth-proxy-callback Location headers - Send hostname-only X-Forwarded-Host from dashboard proxy when port matches backend Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix(auth): address Greptile review on oauth port stripping - Only treat PRIVATE_SERVER_URL as an internal backend port - Add test for rewriteAuthRequestUrl when forwarded host still has :10000 Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
This commit is contained in:
co-authored by
Rotimi Best
Cursor Agent
parent
3eb5e1268d
commit
2fb6b1c477
+29
-11
@@ -6,6 +6,12 @@ import { isPublicCorsPath, sessionCors } from '@api/middlewares/cors';
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
import { API_SERVER_URL } from '@api/constants';
|
||||
import {
|
||||
resolveOAuthBrowserOrigin,
|
||||
rewriteAuthRequestUrl,
|
||||
rewriteOAuthProxyCallbackLocation,
|
||||
shouldRewriteOAuthProxyCallbackLocation
|
||||
} from '@api/utils/oauth-proxy-redirect';
|
||||
import { Hono } from '@api/utils/hono';
|
||||
import { ErrorCodes } from '@api/utils/errors';
|
||||
import { accountRouter } from '@api/routes/account';
|
||||
@@ -114,13 +120,16 @@ export const app = new Hono()
|
||||
let request = c.req.raw;
|
||||
const fwdHost = c.req.header('x-forwarded-host');
|
||||
const fwdProto = c.req.header('x-forwarded-proto');
|
||||
const browserOrigin = resolveOAuthBrowserOrigin({
|
||||
forwardedHost: fwdHost,
|
||||
forwardedProto: fwdProto
|
||||
});
|
||||
|
||||
if (fwdHost) {
|
||||
const url = new URL(request.url);
|
||||
url.host = fwdHost;
|
||||
if (fwdProto === 'https' || fwdProto === 'http') {
|
||||
url.protocol = `${fwdProto}:`;
|
||||
}
|
||||
request = new Request(url, request);
|
||||
request = rewriteAuthRequestUrl(request, {
|
||||
forwardedHost: fwdHost,
|
||||
forwardedProto: fwdProto
|
||||
});
|
||||
}
|
||||
|
||||
// Inbound: if the browser is hitting `/oauth-proxy-callback?token=…`
|
||||
@@ -168,8 +177,9 @@ export const app = new Hono()
|
||||
// pushes the Location header past Render's response size ceiling and
|
||||
// the redirect silently never reaches the browser.
|
||||
if (response.status >= 300 && response.status < 400) {
|
||||
const location = response.headers.get('location');
|
||||
if (location && location.includes('/oauth-proxy-callback') && location.includes('cookies=')) {
|
||||
let location = response.headers.get('location');
|
||||
|
||||
if (location?.includes('/oauth-proxy-callback') && location.includes('cookies=')) {
|
||||
try {
|
||||
const outUrl = new URL(location);
|
||||
const cookies = outUrl.searchParams.get('cookies');
|
||||
@@ -178,14 +188,22 @@ export const app = new Hono()
|
||||
await storeHandoffPayload(token, cookies);
|
||||
outUrl.searchParams.delete('cookies');
|
||||
outUrl.searchParams.set('token', token);
|
||||
const headers = new Headers(response.headers);
|
||||
headers.set('location', outUrl.toString());
|
||||
return new Response(response.body, { status: response.status, headers });
|
||||
location = outUrl.toString();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[auth-handler] failed to swap cookies → token, falling back to inline cookies:', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (location && browserOrigin && shouldRewriteOAuthProxyCallbackLocation(location, browserOrigin)) {
|
||||
location = rewriteOAuthProxyCallbackLocation(location, browserOrigin);
|
||||
}
|
||||
|
||||
if (location && location !== response.headers.get('location')) {
|
||||
const headers = new Headers(response.headers);
|
||||
headers.set('location', location);
|
||||
return new Response(response.body, { status: response.status, headers });
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status >= 300 && response.status < 400) {
|
||||
|
||||
Reference in New Issue
Block a user