Files
openhuman/app/src/utils/oauthAppVersionGate.ts
T
Ghost ScripterandCursor 6019ea2f22 fix(oauth): wait for runtime readiness before first sign-in (#1689)
First-launch Google/GitHub sign-in often failed because the auth deep link
ran before BootCheckGate finished and the embedded core answered RPC. Gate
OAuth on core mode + core.ping + bootstrap completion, start processing
earlier from the Welcome buttons, and surface actionable errors instead of a
generic failure message.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 02:25:02 +05:30

80 lines
2.6 KiB
TypeScript

import { getVersion } from '@tauri-apps/api/app';
import {
type OAuthAuthReadinessFailure,
type OAuthAuthReadinessResult,
oauthAuthReadinessUserMessage,
prepareOAuthLoginLaunch,
waitForOAuthAuthReadiness,
} from '../components/oauth/oauthAuthReadiness';
import { LATEST_APP_DOWNLOAD_URL, MINIMUM_SUPPORTED_APP_VERSION } from './config';
import { isVersionAtLeast, parseSemverParts } from './semver';
import { isTauri } from './tauriCommands/common';
export {
oauthAuthReadinessUserMessage,
prepareOAuthLoginLaunch,
waitForOAuthAuthReadiness,
type OAuthAuthReadinessFailure,
type OAuthAuthReadinessResult,
};
export type OAuthAppVersionGateResult =
| { ok: true }
| { ok: false; current: string; minimum: string; downloadUrl: string };
function block(minimum: string, current: string): OAuthAppVersionGateResult {
return { ok: false, current, minimum, downloadUrl: LATEST_APP_DOWNLOAD_URL };
}
/**
* When `VITE_MINIMUM_SUPPORTED_APP_VERSION` is set (CI/production), block OAuth
* `openhuman://oauth/success` handling if the running desktop build is older.
* Prevents completing Gmail (and other) OAuth on deprecated app binaries.
*
* When a minimum is configured, fails **closed** if the app version cannot be
* determined or parsed (never silently allows OAuth on unknown versions).
*/
export async function evaluateOAuthAppVersionGate(): Promise<OAuthAppVersionGateResult> {
const minimum = MINIMUM_SUPPORTED_APP_VERSION.trim();
try {
if (!minimum) {
return { ok: true };
}
if (!parseSemverParts(minimum)) {
console.warn('[oauth-app-version] invalid MINIMUM_SUPPORTED_APP_VERSION; gate disabled');
return { ok: true };
}
if (!isTauri()) {
return { ok: true };
}
let current: string;
try {
current = await getVersion();
} catch (e) {
console.warn('[oauth-app-version] getVersion failed; blocking OAuth', e);
return block(minimum, 'unknown');
}
if (!parseSemverParts(current)) {
console.warn('[oauth-app-version] unparseable app version; blocking OAuth', current);
return block(minimum, current);
}
if (isVersionAtLeast(current, minimum)) {
return { ok: true };
}
console.warn('[oauth-app-version] blocked OAuth success deep link', { current, minimum });
return block(minimum, current);
} catch (e) {
// Never throw: outer deep-link handler must not receive errors that could log the raw URL.
console.warn('[oauth-app-version] unexpected error', e);
if (!minimum) {
return { ok: true };
}
return block(minimum, 'unknown');
}
}