(
{
email,
sso,
console: useConsole,
managed,
}: {
email?: string
sso?: boolean
console?: boolean
managed?: boolean
},
uiOptions: AuthLoginUiOptions = {},
)
| 177 | } |
| 178 | |
| 179 | export async function performAuthLogin( |
| 180 | { |
| 181 | email, |
| 182 | sso, |
| 183 | console: useConsole, |
| 184 | managed, |
| 185 | }: { |
| 186 | email?: string |
| 187 | sso?: boolean |
| 188 | console?: boolean |
| 189 | managed?: boolean |
| 190 | }, |
| 191 | uiOptions: AuthLoginUiOptions = {}, |
| 192 | ): Promise<void> { |
| 193 | const { |
| 194 | openingMessage = 'Opening browser to sign in…', |
| 195 | urlMessagePrefix = `If the browser didn't open, visit: `, |
| 196 | successMessage = 'Login successful.', |
| 197 | } = uiOptions |
| 198 | |
| 199 | if (useConsole && managed) { |
| 200 | throw new Error('--console and --managed cannot be used together.') |
| 201 | } |
| 202 | |
| 203 | const settings = getInitialSettings() |
| 204 | // forceLoginMethod is a hard constraint (enterprise setting) — matches ConsoleOAuthFlow behavior. |
| 205 | // Without it, --console selects Console; --managed (or no flag) selects |
| 206 | // managed OAuth. |
| 207 | const loginWithClaudeAi = settings.forceLoginMethod |
| 208 | ? settings.forceLoginMethod === 'claudeai' |
| 209 | : !useConsole |
| 210 | const installMode: OAuthInstallMode = loginWithClaudeAi |
| 211 | ? 'managed' |
| 212 | : 'console' |
| 213 | const orgUUID = settings.forceLoginOrgUUID |
| 214 | |
| 215 | // Fast path: if a refresh token is provided via env var, skip the browser |
| 216 | // OAuth flow and exchange it directly for tokens. |
| 217 | const envRefreshToken = process.env.CLAUDE_CODE_OAUTH_REFRESH_TOKEN |
| 218 | if (envRefreshToken) { |
| 219 | const envScopes = process.env.CLAUDE_CODE_OAUTH_SCOPES |
| 220 | if (!envScopes) { |
| 221 | throw new Error( |
| 222 | 'CLAUDE_CODE_OAUTH_SCOPES is required when using CLAUDE_CODE_OAUTH_REFRESH_TOKEN.\n' + |
| 223 | 'Set it to the space-separated scopes the refresh token was issued with\n' + |
| 224 | '(e.g. "user:inference" or "user:profile user:inference user:sessions:claude_code user:mcp_servers").', |
| 225 | ) |
| 226 | } |
| 227 | |
| 228 | const scopes = envScopes.split(/\s+/).filter(Boolean) |
| 229 | |
| 230 | logEvent('ncode_login_from_refresh_token', {}) |
| 231 | |
| 232 | const tokens = await refreshOAuthToken(envRefreshToken, { scopes }) |
| 233 | await installOAuthTokens(tokens, { mode: installMode }) |
| 234 | |
| 235 | const orgResult = await validateForceLoginOrgForCurrentSession() |
| 236 | if (!orgResult.valid) { |
no test coverage detected