mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(openrouter): use the loopback's bound port in the OAuth callback_url (#3904)
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
This commit is contained in:
@@ -31,8 +31,10 @@ describe('connectOpenRouterViaOAuth', () => {
|
||||
expect(openExternalUrl).toHaveBeenCalledTimes(1);
|
||||
const authUrl = new URL(openExternalUrl.mock.calls[0][0]);
|
||||
expect(authUrl.origin + authUrl.pathname).toBe('https://openrouter.ai/auth');
|
||||
// callback_url must use the port the listener actually bound to (carried in
|
||||
// redirectUri), not the requested OPENROUTER_LOOPBACK_PORT constant.
|
||||
expect(authUrl.searchParams.get('callback_url')).toBe(
|
||||
'http://localhost:3000/auth?state=expected-state'
|
||||
'http://localhost:53824/auth?state=expected-state'
|
||||
);
|
||||
expect(authUrl.searchParams.get('code_challenge_method')).toBe('S256');
|
||||
expect(authUrl.searchParams.get('code_challenge')).toBeTruthy();
|
||||
@@ -43,6 +45,37 @@ describe('connectOpenRouterViaOAuth', () => {
|
||||
expect(cancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('uses the listener bound port for callback_url when the requested port was busy', async () => {
|
||||
// Port 3000 busy -> the Tauri listener falls back to an OS-assigned port and
|
||||
// returns it in redirectUri. callback_url must point at that bound port, or
|
||||
// OpenRouter redirects to a port nothing is listening on and OAuth fails.
|
||||
const openExternalUrl = vi.fn().mockResolvedValue(undefined);
|
||||
const startLoopbackListener = vi
|
||||
.fn()
|
||||
.mockResolvedValue({
|
||||
redirectUri: 'http://127.0.0.1:54321/auth?state=expected-state',
|
||||
state: 'expected-state',
|
||||
awaitCallback: vi
|
||||
.fn()
|
||||
.mockResolvedValue('http://127.0.0.1:54321/auth?state=expected-state&code=abc123'),
|
||||
cancel: vi.fn().mockResolvedValue(undefined),
|
||||
});
|
||||
const fetchImpl = vi
|
||||
.fn()
|
||||
.mockResolvedValue({ ok: true, json: async () => ({ key: 'sk-or-via-oauth' }) });
|
||||
|
||||
await connectOpenRouterViaOAuth({
|
||||
startLoopbackListener,
|
||||
openExternalUrl,
|
||||
fetchImpl: fetchImpl as unknown as typeof fetch,
|
||||
});
|
||||
|
||||
const authUrl = new URL(openExternalUrl.mock.calls[0][0]);
|
||||
expect(authUrl.searchParams.get('callback_url')).toBe(
|
||||
'http://localhost:54321/auth?state=expected-state'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects when the loopback listener is unavailable', async () => {
|
||||
await expect(
|
||||
connectOpenRouterViaOAuth({ startLoopbackListener: vi.fn().mockResolvedValue(null) })
|
||||
|
||||
@@ -105,8 +105,13 @@ function toOpenRouterCallbackUrl(redirectUri: string): string {
|
||||
throw new Error('OpenRouter OAuth listener returned an invalid redirect URL.');
|
||||
}
|
||||
|
||||
// Preserve the port the loopback listener actually bound to (carried in
|
||||
// redirectUri): when the requested port is busy, the Tauri command falls back
|
||||
// to an OS-assigned ephemeral port, so hardcoding OPENROUTER_LOOPBACK_PORT here
|
||||
// sent OpenRouter a callback_url pointing at the wrong port. The PKCE
|
||||
// callback_url is per-request, so the dynamic port is valid (this matches the
|
||||
// sibling OAuthProviderButton flow, which trusts the bound port).
|
||||
parsed.hostname = 'localhost';
|
||||
parsed.port = String(OPENROUTER_LOOPBACK_PORT);
|
||||
return parsed.toString();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user