mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 18:15:23 +00:00
fix: add CLIProxy account reauthentication
This commit is contained in:
1 parent
d212c72ad8
commit
5f1976f69c
16 files changed
+332
-34
No files matched your search
@@ -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). */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user