From 1d8842e3afb50d17061abf259160ec40d0472ed4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 09:57:57 +0000 Subject: [PATCH] fix(login): validate the token on the runtime's API port, not always 3333 With a local Console URL, `apify login` always validated the token against http://localhost:3333. A runtime started on another port (`apify runtime start --api-port 4333`, `ACTOR_RUNTIME_API_PORT` in the container) was therefore never reachable from login. Login now takes the API from `APIFY_CLIENT_BASE_URL` or the connected runtime first, then the port the runtime was last started on, and only falls back to 3333 when no port is known (hand-started runtime on the defaults, local platform development). Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015JzfLYa5CHCXxfzC1No3gV --- src/commands/auth/login.ts | 21 ++++++--- .../local/commands/login-api-base-url.test.ts | 45 +++++++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 test/local/commands/login-api-base-url.test.ts diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index cc5da7399..343a25fe7 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -20,18 +20,27 @@ import { useMaskedInput } from '../../lib/hooks/user-confirmations/useMaskedInpu import { useSelectFromList } from '../../lib/hooks/user-confirmations/useSelectFromList.js'; import { createLocalApiServer } from '../../lib/local-api-server.js'; import { error, info, success, warning } from '../../lib/outputs.js'; +import { runtimeApiUrl } from '../../lib/runtime/docker.js'; +import { configuredRuntimePorts, resolveApiBaseUrl } from '../../lib/runtime/target.js'; import { tildify } from '../../lib/utils.js'; -// When logging in against a local Console instance (local platform development), validate the token -// against the local API rather than production. -const LOCAL_API_BASE_URL = 'http://localhost:3333'; - // Not really checked right now, but it might come useful if we ever need to do some breaking changes const API_VERSION = 'v1'; +/** + * Where the token is validated. `APIFY_CLIENT_BASE_URL` and a connected Actor runtime win. Otherwise a local + * Console (a hand-started runtime, or local platform development) means the local API: the port the + * runtime was last started on, or 3333 when none is known. + */ +export function resolveLoginApiBaseUrl(): string | undefined { + const explicit = resolveApiBaseUrl(); + if (explicit) return explicit; + + return getConsoleUrl().includes('localhost') ? runtimeApiUrl(configuredRuntimePorts().api) : undefined; +} + const tryToLogin = async (token: string) => { - const apiBaseUrl = getConsoleUrl().includes('localhost') ? LOCAL_API_BASE_URL : undefined; - const result = await loginWithToken(token, apiBaseUrl); + const result = await loginWithToken(token, resolveLoginApiBaseUrl()); if (result) { const { userInfo } = result; diff --git a/test/local/commands/login-api-base-url.test.ts b/test/local/commands/login-api-base-url.test.ts new file mode 100644 index 000000000..e1302b00e --- /dev/null +++ b/test/local/commands/login-api-base-url.test.ts @@ -0,0 +1,45 @@ +import { resolveLoginApiBaseUrl } from '../../../src/commands/auth/login.js'; +import { updateActorRuntimeConfig } from '../../../src/lib/runtime/config.js'; +import { setConnectedToActorRuntime } from '../../../src/lib/runtime/target.js'; +import { useAuthSetup } from '../../__setup__/hooks/useAuthSetup.js'; + +useAuthSetup(); + +describe('apify login API base URL', () => { + beforeEach(() => { + vitest.stubEnv('APIFY_CLIENT_BASE_URL', ''); + vitest.stubEnv('APIFY_CONSOLE_URL', ''); + }); + + it('validates against the Apify platform by default', () => { + expect(resolveLoginApiBaseUrl()).toBeUndefined(); + }); + + it('falls back to port 3333 for a local Console when no runtime port is known', () => { + vitest.stubEnv('APIFY_CONSOLE_URL', 'http://localhost:3000'); + + expect(resolveLoginApiBaseUrl()).toBe('http://localhost:3333'); + }); + + it('uses the port the runtime was last started on for a local Console', () => { + vitest.stubEnv('APIFY_CONSOLE_URL', 'http://localhost:4000'); + updateActorRuntimeConfig({ apiPort: 4333, consolePort: 4000 }); + + expect(resolveLoginApiBaseUrl()).toBe('http://localhost:4333'); + }); + + it('targets the connected runtime on its configured port', () => { + updateActorRuntimeConfig({ apiPort: 4333, consolePort: 4000 }); + setConnectedToActorRuntime(true); + + expect(resolveLoginApiBaseUrl()).toBe('http://localhost:4333'); + }); + + it('lets APIFY_CLIENT_BASE_URL win over a local Console and a connected runtime', () => { + vitest.stubEnv('APIFY_CLIENT_BASE_URL', 'https://api.example.com'); + vitest.stubEnv('APIFY_CONSOLE_URL', 'http://localhost:3000'); + setConnectedToActorRuntime(true); + + expect(resolveLoginApiBaseUrl()).toBe('https://api.example.com'); + }); +});