custom callback_host for arcade login (#481)

Say you are @shubcodes and running arcade on replit or vs codespaces.
Today, `arcade login` assumes that the browser you've opened for the
auth flow's "localhost" is the same that is running the `arcade login`
command. If you are running on one of these remote code execution
environments, that won't be true.

Usage:
```
arcade login --callback-host "https://replit.com:9999/path/to/my/codespace"
```
This commit is contained in:
Evan Tahler 2025-07-14 10:53:26 -07:00 committed by GitHub
parent b1e3c334f0
commit e715ae912f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 33 additions and 4 deletions

View file

@ -99,6 +99,11 @@ def login(
"--port",
help="The port of the Arcade Cloud host (if running locally).",
),
callback_host: str = typer.Option(
"localhost",
"--callback-host",
help="The host to use to complete the auth flow - this should be the same as the host that the CLI is running on. Include the port if needed.",
),
debug: bool = typer.Option(False, "--debug", "-d", help="Show debug information"),
) -> None:
"""
@ -119,7 +124,7 @@ def login(
try:
# Open the browser for user login
login_url = compute_login_url(host, state, port)
login_url = compute_login_url(host, state, port, callback_host)
console.print("Opening a browser to log you in...")
if not webbrowser.open(login_url):

View file

@ -176,11 +176,22 @@ def compute_base_url(
return f"{protocol}://{encoded_host}"
def compute_login_url(host: str, state: str, port: int | None) -> str:
def compute_login_url(
host: str, state: str, port: int | None, callback_host: str | None = None
) -> str:
"""
Compute the full URL for the CLI login endpoint.
"""
callback_uri = f"http://{LOCALHOST}:9905/callback"
if callback_host:
if not (callback_host.startswith("http://") or callback_host.startswith("https://")):
callback_uri = f"http://{callback_host}"
else:
callback_uri = callback_host
if not callback_uri.rstrip("/").endswith("/callback"):
callback_uri = callback_uri.rstrip("/") + "/callback"
else:
callback_uri = f"http://{LOCALHOST}:9905/callback"
params = urlencode({"callback_uri": callback_uri, "state": state})
port = port if port else 8000

View file

@ -218,9 +218,22 @@ def test_compute_base_url(inputs: dict, expected_output: str):
"https://cloud.arcade.dev/api/v1/auth/cli_login?callback_uri=http%3A%2F%2Flocalhost%3A9905%2Fcallback&state=123",
id="cloud host with an ignored custom port",
),
pytest.param(
{
"host_input": DEFAULT_CLOUD_HOST,
"port_input": DEFAULT_PORT,
"state": "123",
"callback_host": "other-host.com/123",
},
"https://cloud.arcade.dev/api/v1/auth/cli_login?callback_uri=http%3A%2F%2Fother-host.com%2F123%2Fcallback&state=123",
id="cloud host with a custom callback host",
),
],
)
def test_compute_login_url(inputs: dict, expected_output: str):
login_url = compute_login_url(inputs["host_input"], inputs["state"], inputs["port_input"])
callback_host = inputs.get("callback_host")
login_url = compute_login_url(
inputs["host_input"], inputs["state"], inputs["port_input"], callback_host
)
assert login_url == expected_output