Skip to content

Feature/ivy studio cloud fixes#2899

Open
rorychatt wants to merge 6 commits intomainfrom
feature/ivy-studio-cloud-fixes
Open

Feature/ivy studio cloud fixes#2899
rorychatt wants to merge 6 commits intomainfrom
feature/ivy-studio-cloud-fixes

Conversation

@rorychatt
Copy link
Copy Markdown
Collaborator

No description provided.

swaner and others added 4 commits March 31, 2026 14:34
When using --path-base="/test/studio", the OAuth login URL was
incorrectly generated with a double slash:
  http://localhost:5006/test/studio//ivy/auth/oauth-login

This was caused by BaseUrl already providing a trailing slash when
BasePath is set, then concatenating with a leading slash in the path.

Remove the leading slash from the hardcoded path since BaseUrl
guarantees a trailing slash when BasePath is set.

Fixes: DefaultAuthApp.cs line 148

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
When using ivy-studio behind a reverse proxy with --path-base (e.g., /test/studio),
OAuth callback URLs and post-login redirects were missing the base-path prefix.
This caused redirects to go to http://localhost:5006/ instead of
http://localhost:5006/test/studio/.

Changes:
- Updated WebhookEndpoint.BuildAuthCallbackBaseUrl and BuildWebhookBaseUrl to accept
  and include the optional basePath parameter
- Modified AuthController.OAuthLogin to inject ServerArgs and pass basePath when
  constructing OAuth callback URLs
- Updated AuthController.OAuthCallback to redirect to the correct URL including
  the base-path
- Updated IAuthTokenHandler.InitializeAsync interface to accept basePath parameter
- Updated all IAuthTokenHandler implementations (MicrosoftEntraAuthTokenHandler,
  ClerkAuthTokenHandler) to handle basePath in callback URL construction
- Modified UseWebhook hook to pass appContext.BasePath when creating webhook endpoints
- Updated AppHub to pass basePath to authProvider.InitializeAsync calls

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

return Redirect("/?oauthLogin=1");
var redirectUrl = serverArgs.BasePath != null ? $"{serverArgs.BasePath}/?oauthLogin=1" : "/?oauthLogin=1";
return Redirect(redirectUrl);

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection due to
user-provided value
.

Copilot Autofix

AI about 5 hours ago

In general, to fix untrusted URL redirection, ensure that any untrusted component (here, serverArgs.BasePath) cannot cause the redirect to point to an external host. The redirect target should either be a purely relative path or be validated against a whitelist or host check before passing it to Redirect(...).

For this specific method, the safest approach without changing intended functionality is to normalize serverArgs.BasePath into a strictly relative application path and then construct the final URL from that normalized path. We should prevent BasePath from being interpreted as an absolute URI or protocol-relative URL and ensure it always produces something like "/", "/some-base/", etc. A straightforward way is:

  1. Start from a default "/" if BasePath is null or empty.
  2. Ensure the base path starts with a single /.
  3. Remove any scheme/host information and ignore inputs that look like absolute URLs or protocol-relative URLs.
  4. Build the redirect as normalizedBasePath + (normalizedBasePath.EndsWith("/") ? "" : "/") + "?oauthLogin=1".

This can be implemented directly in OAuthCallback around lines 145–146, replacing the simple string interpolation with a small normalization block, without touching other methods or adding dependencies. No new imports are needed; string operations are sufficient.

Suggested changeset 1
src/Ivy/Core/Auth/AuthController.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/Ivy/Core/Auth/AuthController.cs b/src/Ivy/Core/Auth/AuthController.cs
--- a/src/Ivy/Core/Auth/AuthController.cs
+++ b/src/Ivy/Core/Auth/AuthController.cs
@@ -142,7 +142,34 @@
             cookies.AddCookiesForBrokeredSessions(tempSession.BrokeredSessions);
             cookies.WriteToResponse(Response);
 
-            var redirectUrl = serverArgs.BasePath != null ? $"{serverArgs.BasePath}/?oauthLogin=1" : "/?oauthLogin=1";
+            // Ensure redirect URL remains within this application by normalizing BasePath to a relative path
+            var basePath = serverArgs.BasePath;
+            if (string.IsNullOrWhiteSpace(basePath))
+            {
+                basePath = "/";
+            }
+
+            // Disallow absolute or protocol-relative URLs in BasePath
+            if (basePath.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
+                basePath.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
+                basePath.StartsWith("//", StringComparison.Ordinal))
+            {
+                basePath = "/";
+            }
+
+            // Normalize to a rooted, application-local path
+            if (!basePath.StartsWith("/"))
+            {
+                basePath = "/" + basePath;
+            }
+
+            // Avoid double slashes before query
+            if (!basePath.EndsWith("/"))
+            {
+                basePath += "/";
+            }
+
+            var redirectUrl = $"{basePath}?oauthLogin=1";
             return Redirect(redirectUrl);
         }
         catch (Exception ex)
EOF
@@ -142,7 +142,34 @@
cookies.AddCookiesForBrokeredSessions(tempSession.BrokeredSessions);
cookies.WriteToResponse(Response);

var redirectUrl = serverArgs.BasePath != null ? $"{serverArgs.BasePath}/?oauthLogin=1" : "/?oauthLogin=1";
// Ensure redirect URL remains within this application by normalizing BasePath to a relative path
var basePath = serverArgs.BasePath;
if (string.IsNullOrWhiteSpace(basePath))
{
basePath = "/";
}

// Disallow absolute or protocol-relative URLs in BasePath
if (basePath.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
basePath.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
basePath.StartsWith("//", StringComparison.Ordinal))
{
basePath = "/";
}

// Normalize to a rooted, application-local path
if (!basePath.StartsWith("/"))
{
basePath = "/" + basePath;
}

// Avoid double slashes before query
if (!basePath.EndsWith("/"))
{
basePath += "/";
}

var redirectUrl = $"{basePath}?oauthLogin=1";
return Redirect(redirectUrl);
}
catch (Exception ex)
Copilot is powered by AI and may make mistakes. Always verify output.
@artem-ivy-ai
Copy link
Copy Markdown
Collaborator

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.

4 participants