fix(pairing): allow emulator ws setup urls

This commit is contained in:
Ayaan Zaidi 2026-04-03 13:07:17 +05:30
parent acd5734aa9
commit 84add47525
3 changed files with 35 additions and 3 deletions

View File

@ -229,6 +229,19 @@ describe("registerQrCli", () => {
expect(output).toContain("gateway.tailscale.mode=serve");
});
it("allows android emulator cleartext override urls", async () => {
loadConfig.mockReturnValue({
gateway: {
bind: "loopback",
auth: { mode: "token", token: "tok" },
},
});
await runQr(["--setup-code-only", "--url", "ws://10.0.2.2:18789"]);
expectLoggedSetupCode("ws://10.0.2.2:18789");
});
it("accepts --token override when config has no auth", async () => {
loadConfig.mockReturnValue({
gateway: {

View File

@ -399,6 +399,21 @@ describe("pairing setup code", () => {
urlSource: "gateway.bind=custom",
},
},
{
name: "allows android emulator cleartext setup urls",
config: {
gateway: {
bind: "custom",
customBindHost: "10.0.2.2",
auth: { mode: "token", token: "tok_123" },
},
} satisfies ResolveSetupConfig,
expected: {
authLabel: "token",
url: "ws://10.0.2.2:18789",
urlSource: "gateway.bind=custom",
},
},
] as const)("$name", async ({ config, options, expected }) => {
await expectResolvedSetupSuccessCase({
config,
@ -443,7 +458,7 @@ describe("pairing setup code", () => {
options: {
networkInterfaces: () => createIpv4NetworkInterfaces("192.168.1.20"),
} satisfies ResolveSetupOptions,
expectedError: "ws:// is only valid for localhost",
expectedError: "ws:// is only valid for localhost or the Android emulator",
},
] as const)("$name", async ({ config, options, expectedError }) => {
await expectResolvedSetupFailureCase({

View File

@ -69,10 +69,14 @@ function describeSecureMobilePairingFix(source?: string): string {
"Mobile pairing requires a secure remote gateway URL (wss://) or Tailscale Serve/Funnel." +
sourceNote +
" Fix: prefer gateway.tailscale.mode=serve, or set gateway.remote.url / " +
"plugins.entries.device-pair.config.publicUrl to a wss:// URL. ws:// is only valid for localhost."
"plugins.entries.device-pair.config.publicUrl to a wss:// URL. ws:// is only valid for localhost or the Android emulator."
);
}
function isMobilePairingCleartextAllowedHost(host: string): boolean {
return isLoopbackHost(host) || host === "10.0.2.2";
}
function validateMobilePairingUrl(url: string, source?: string): string | null {
if (isSecureWebSocketUrl(url)) {
return null;
@ -85,7 +89,7 @@ function validateMobilePairingUrl(url: string, source?: string): string | null {
}
const protocol =
parsed.protocol === "https:" ? "wss:" : parsed.protocol === "http:" ? "ws:" : parsed.protocol;
if (protocol !== "ws:" || isLoopbackHost(parsed.hostname)) {
if (protocol !== "ws:" || isMobilePairingCleartextAllowedHost(parsed.hostname)) {
return null;
}
return describeSecureMobilePairingFix(source);