fix: add CLIProxy account reauthentication

This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-05-27 10:13:03 -04:00
1 parent d212c72ad8
commit 5f1976f69c
16 files changed
+332 -34

No files matched your search

+2
View File
@@ -389,6 +389,8 @@ export interface OAuthOptions {
account?: string;
add?: boolean;
nickname?: string;
/** Existing account id to update during reauthentication. */
expectedAccountId?: string;
/** If true, caller explicitly accepts Antigravity OAuth risk for this command/session. */
acceptAgyRisk?: boolean;
/** Kiro auth method override (CLI + Dashboard parity). */
+6 -5
View File
@@ -1131,8 +1131,9 @@ export async function triggerOAuth(
// Check for existing accounts
const existingAccounts = getProviderAccounts(provider);
const existingNameMatch = nickname ? findAccountNameMatch(existingAccounts, nickname) : null;
const targetAccountId = options.expectedAccountId || existingNameMatch?.id;
const nicknameError = !fromUI
? getCliAuthNicknameError(provider, nickname, existingAccounts, existingNameMatch?.id)
? getCliAuthNicknameError(provider, nickname, existingAccounts, targetAccountId)
: null;
if (nicknameError) {
console.log(fail(nicknameError));
@@ -1144,7 +1145,7 @@ export async function triggerOAuth(
const tokenDir = getProviderTokenDir(provider);
const success = await importKiroToken(verbose);
if (success) {
return registerAccountFromToken(provider, tokenDir, nickname, verbose, existingNameMatch?.id);
return registerAccountFromToken(provider, tokenDir, nickname, verbose, targetAccountId);
}
return null;
}
@@ -1235,7 +1236,7 @@ export async function triggerOAuth(
verbose,
tokenDir,
nickname,
existingNameMatch?.id,
targetAccountId,
{
gitlabBaseUrl: resolvedGitLabBaseUrl,
gitlabPersonalAccessToken: options.gitlabPersonalAccessToken,
@@ -1251,7 +1252,7 @@ export async function triggerOAuth(
verbose,
tokenDir,
nickname,
existingNameMatch?.id,
targetAccountId,
{
kiroMethod: provider === 'kiro' ? resolvedKiroMethod : undefined,
gitlabBaseUrl: provider === 'gitlab' ? resolvedGitLabBaseUrl : undefined,
@@ -1336,7 +1337,7 @@ export async function triggerOAuth(
verbose,
isCLI,
nickname,
expectedAccountId: existingNameMatch?.id,
expectedAccountId: targetAccountId,
authFlowType: isDeviceCodeFlow ? 'device_code' : 'authorization_code',
kiroMethod: provider === 'kiro' ? resolvedKiroMethod : undefined,
manualCallback: useSelectedKiroLocalPasteCallback,
+60 -17
View File
@@ -348,6 +348,24 @@ export function getStartAuthNicknameError(
return null;
}
export function getReauthAccountTarget(
accountId: string | undefined,
existingAccounts: Array<{ id: string; nickname?: string }>
): { account?: { id: string; nickname?: string }; error?: string } {
if (!accountId) {
return {};
}
const account = existingAccounts.find((candidate) => candidate.id === accountId);
if (!account) {
return {
error: `Account '${accountId}' not found for this provider`,
};
}
return { account };
}
/**
* GET /api/cliproxy/auth - Get auth status for built-in CLIProxy profiles
* Also fetches CLIProxyAPI stats to update lastUsedAt for active providers
@@ -623,6 +641,8 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
const requestBody =
req.body && typeof req.body === 'object' ? (req.body as Record<string, unknown>) : {};
const nicknameRaw = typeof requestBody.nickname === 'string' ? requestBody.nickname : undefined;
const accountId =
typeof requestBody.accountId === 'string' ? requestBody.accountId.trim() : undefined;
const noIncognitoBody =
typeof requestBody.noIncognito === 'boolean' ? requestBody.noIncognito : undefined;
const kiroMethodRaw = requestBody.kiroMethod;
@@ -659,6 +679,16 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
return;
}
const localProvider = provider as CLIProxyProvider;
const existingAccounts = getProviderAccounts(localProvider);
const reauthTarget = getReauthAccountTarget(accountId, existingAccounts);
if (reauthTarget.error) {
res.status(404).json({ error: reauthTarget.error });
return;
}
const targetAccountId = reauthTarget.account?.id;
const effectiveNickname = nickname || reauthTarget.account?.nickname;
if (provider === 'kiro' && invalidKiroMethod) {
res.status(400).json({
error: 'Invalid kiroMethod. Supported: aws, aws-authcode, google, github, idc',
@@ -708,7 +738,6 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
}
try {
const localProvider = provider as CLIProxyProvider;
const knownTokenFiles = listProviderTokenSnapshots(localProvider);
const response = await fetch(buildProxyUrl(target, '/v0/management/gitlab-auth-url'), {
method: 'POST',
@@ -738,7 +767,8 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
const tokenSnapshot = findNewTokenSnapshot(
listProviderTokenSnapshots(localProvider),
knownTokenFiles
knownTokenFiles,
targetAccountId
);
if (!tokenSnapshot) {
res.status(409).json({
@@ -750,9 +780,9 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
const account = registerAccountFromToken(
localProvider,
getProviderTokenDir(localProvider),
nickname,
effectiveNickname,
false,
tokenSnapshot.file
targetAccountId || tokenSnapshot.file
);
if (!account) {
res.status(409).json({
@@ -784,11 +814,11 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
}
}
const existingAccounts = getProviderAccounts(provider as CLIProxyProvider);
const nicknameError = getStartAuthNicknameError(
provider as CLIProxyProvider,
nickname,
existingAccounts
localProvider,
effectiveNickname,
existingAccounts,
targetAccountId
);
if (nicknameError) {
res.status(400).json(nicknameError);
@@ -808,7 +838,8 @@ router.post('/:provider/start', async (req: Request, res: Response): Promise<voi
const account = await triggerOAuth(provider as CLIProxyProvider, {
add: true, // Always add mode from UI
headless: false, // Force interactive mode
nickname: nickname || undefined,
nickname: effectiveNickname || undefined,
expectedAccountId: targetAccountId,
acceptAgyRisk: provider === 'agy',
kiroMethod: provider === 'kiro' ? kiroMethod : undefined,
kiroIDCStartUrl: provider === 'kiro' ? kiroIDCStartUrl : undefined,
@@ -971,6 +1002,8 @@ router.post('/:provider/start-url', async (req: Request, res: Response): Promise
const requestBody =
req.body && typeof req.body === 'object' ? (req.body as Record<string, unknown>) : {};
const nicknameRaw = typeof requestBody.nickname === 'string' ? requestBody.nickname : undefined;
const accountId =
typeof requestBody.accountId === 'string' ? requestBody.accountId.trim() : undefined;
const kiroMethodRaw = requestBody.kiroMethod;
const gitlabAuthModeRaw = requestBody.gitlabAuthMode;
const gitlabBaseUrl =
@@ -1037,11 +1070,20 @@ router.post('/:provider/start-url', async (req: Request, res: Response): Promise
return;
}
const existingAccounts = getProviderAccounts(provider as CLIProxyProvider);
const localProvider = provider as CLIProxyProvider;
const existingAccounts = getProviderAccounts(localProvider);
const reauthTarget = getReauthAccountTarget(accountId, existingAccounts);
if (reauthTarget.error) {
res.status(404).json({ error: reauthTarget.error });
return;
}
const targetAccountId = reauthTarget.account?.id;
const effectiveNickname = nickname || reauthTarget.account?.nickname;
const nicknameError = getStartAuthNicknameError(
provider as CLIProxyProvider,
nickname,
existingAccounts
localProvider,
effectiveNickname,
existingAccounts,
targetAccountId
);
if (nicknameError) {
res.status(400).json(nicknameError);
@@ -1136,8 +1178,9 @@ router.post('/:provider/start-url', async (req: Request, res: Response): Promise
if (oauthState) {
rememberManualAuthState(oauthState, {
nickname: nickname || undefined,
knownTokenFiles: listProviderTokenSnapshots(provider as CLIProxyProvider),
nickname: effectiveNickname || undefined,
expectedAccountId: targetAccountId,
knownTokenFiles: listProviderTokenSnapshots(localProvider),
});
}
@@ -1237,7 +1280,7 @@ router.get('/:provider/status', async (req: Request, res: Response): Promise<voi
getProviderTokenDir(localProvider),
pendingAuth.nickname,
false,
tokenSnapshot.file
pendingAuth.expectedAccountId || tokenSnapshot.file
);
if (!account) {
@@ -1386,7 +1429,7 @@ router.post('/:provider/submit-callback', async (req: Request, res: Response): P
getProviderTokenDir(localProvider),
pendingAuth.nickname,
false,
tokenSnapshot.file
pendingAuth.expectedAccountId || tokenSnapshot.file
);
if (!account) {