fix(welcome): include bearer token in core RPC test-connection probe (#1301)

This commit is contained in:
sanil-23
2026-05-06 13:16:04 -07:00
committed by GitHub
parent a35ba10bb2
commit b7198063de
3 changed files with 98 additions and 6 deletions
+2 -6
View File
@@ -3,7 +3,7 @@ import { useState } from 'react';
import OAuthProviderButton from '../components/oauth/OAuthProviderButton';
import { oauthProviderConfigs } from '../components/oauth/providerConfigs';
import RotatingTetrahedronCanvas from '../components/RotatingTetrahedronCanvas';
import { clearCoreRpcUrlCache } from '../services/coreRpcClient';
import { clearCoreRpcUrlCache, testCoreRpcConnection } from '../services/coreRpcClient';
import { useDeepLinkAuthState } from '../store/deepLinkAuthState';
import {
clearStoredRpcUrl,
@@ -65,11 +65,7 @@ const Welcome = () => {
setRpcUrlError(null);
try {
const response = await fetch(normalized, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'openhuman.ping', params: {} }),
});
const response = await testCoreRpcConnection(normalized);
if (response.ok || response.status === 405) {
setSaveSuccess(true);
@@ -421,4 +421,76 @@ describe('coreRpcClient', () => {
expect(tokenCalls).toBe(1);
expect(fetch).not.toHaveBeenCalled();
});
describe('testCoreRpcConnection', () => {
test('POSTs an openhuman.ping JSON-RPC envelope to the supplied URL', async () => {
vi.resetModules();
vi.mocked(isTauri).mockReturnValue(false);
const { testCoreRpcConnection } = await import('../coreRpcClient');
const fetchMock = vi.mocked(fetch);
fetchMock.mockResolvedValueOnce({ ok: true, status: 200 } as Response);
await testCoreRpcConnection('http://example.test:7788/rpc');
expect(fetchMock).toHaveBeenCalledTimes(1);
const [url, init] = fetchMock.mock.calls[0];
expect(url).toBe('http://example.test:7788/rpc');
const requestInit = init as RequestInit;
expect(requestInit.method).toBe('POST');
expect(JSON.parse(requestInit.body as string)).toMatchObject({
jsonrpc: '2.0',
id: 1,
method: 'openhuman.ping',
params: {},
});
});
test('omits Authorization header when no bearer token is available (non-Tauri)', async () => {
vi.resetModules();
vi.mocked(isTauri).mockReturnValue(false);
const { testCoreRpcConnection } = await import('../coreRpcClient');
const fetchMock = vi.mocked(fetch);
fetchMock.mockResolvedValueOnce({ ok: true, status: 200 } as Response);
await testCoreRpcConnection('http://example.test:7788/rpc');
const requestInit = fetchMock.mock.calls[0][1] as RequestInit;
const headers = requestInit.headers as Record<string, string>;
expect(headers).toMatchObject({ 'Content-Type': 'application/json' });
expect(headers).not.toHaveProperty('Authorization');
});
test('attaches Authorization: Bearer when the Tauri bearer token resolves', async () => {
vi.resetModules();
vi.mocked(isTauri).mockReturnValue(true);
vi.mocked(invoke).mockImplementation(async (cmd: string) => {
if (cmd === 'core_rpc_token') return 'deadbeef';
throw new Error(`unexpected command: ${cmd}`);
});
const { testCoreRpcConnection } = await import('../coreRpcClient');
const fetchMock = vi.mocked(fetch);
fetchMock.mockResolvedValueOnce({ ok: true, status: 200 } as Response);
await testCoreRpcConnection('http://example.test:7788/rpc');
const requestInit = fetchMock.mock.calls[0][1] as RequestInit;
const headers = requestInit.headers as Record<string, string>;
expect(headers.Authorization).toBe('Bearer deadbeef');
expect(headers['Content-Type']).toBe('application/json');
});
test('returns the raw fetch Response so callers can inspect status/ok', async () => {
vi.resetModules();
vi.mocked(isTauri).mockReturnValue(false);
const { testCoreRpcConnection } = await import('../coreRpcClient');
const fetchMock = vi.mocked(fetch);
const probe = { ok: false, status: 405, statusText: 'Method Not Allowed' } as Response;
fetchMock.mockResolvedValueOnce(probe);
const response = await testCoreRpcConnection('http://example.test:7788/rpc');
expect(response).toBe(probe);
expect(response.status).toBe(405);
});
});
});
+24
View File
@@ -167,6 +167,30 @@ async function getCoreRpcToken(): Promise<string | null> {
return resolvingCoreRpcToken;
}
/**
* Probe an arbitrary core RPC URL with `openhuman.ping`. Used by the
* Welcome page's "Test Connection" affordance to validate a user-entered
* RPC URL without going through the cached `getCoreRpcUrl` resolution.
*
* Encapsulates the bearer-token + JSON-RPC envelope assembly that would
* otherwise sit in the calling component, keeping all RPC client behavior
* inside the service per the project guideline ("Keep Tauri IPC and RPC
* client calls localized to services … do not scatter `invoke()` or
* direct RPC calls throughout components").
*/
export async function testCoreRpcConnection(url: string): Promise<Response> {
const token = await getCoreRpcToken();
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
if (token) {
headers.Authorization = `Bearer ${token}`;
}
return fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'openhuman.ping', params: {} }),
});
}
export async function getCoreHttpBaseUrl(): Promise<string> {
const rpcUrl = await getCoreRpcUrl();
const url = new URL(rpcUrl);