Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions web/app/src/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,43 @@ const Login = lazy(() => import('./pages/login'));
const Dashboard = lazy(() => import('./pages/dashboard'));
const SignUp = lazy(() => import('./pages/signup'));

/**
* Validates a redirect URI to prevent open redirect attacks.
* Allows same-origin redirects and cross-origin redirects only for
* http/https protocols that match configured redirect URLs.
*/
function isValidRedirectUri(
uri: string,
configuredRedirectURL?: string,
): boolean {
try {
const url = new URL(uri, window.location.origin);
// Only allow http and https protocols (block javascript:, data:, etc.)
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
return false;
}
// Same-origin redirects are always allowed
if (url.origin === window.location.origin) {
return true;
}
// Cross-origin: only allow if it matches the configured redirect URL origin
if (configuredRedirectURL) {
try {
const configuredUrl = new URL(configuredRedirectURL);
if (url.origin === configuredUrl.origin) {
return true;
}
} catch {
// Invalid configured URL, reject cross-origin
}
}
return false;
} catch {
// If URI can't be parsed, reject it
return false;
}
}

export default function Root({
globalState,
}: {
Expand All @@ -45,10 +82,10 @@ export default function Root({

const rawRedirectURL =
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
if (rawRedirectURL && isValidRedirectUri(rawRedirectURL)) {
if (rawRedirectURL && isValidRedirectUri(rawRedirectURL, config?.redirectURL)) {
urlProps.redirectURL = rawRedirectURL;
} else {
urlProps.redirectURL = hasWindow() ? window.location.origin : '/app';
urlProps.redirectURL = hasWindow() ? window.location.origin : '/';
}

urlProps.redirect_uri = urlProps.redirectURL;
Expand Down