login method
Ensures the user is logged into Firebase and prompts for account selection if multiple accounts are available.
This method provides:
- Interactive account selection.
- Support for adding new accounts (
--reauthflow). - Account switching with automatic session cleanup.
- Logout functionality.
Implementation
Future<void> login() async {
print('๐ Checking Firebase account...\n');
try {
final result = await runWithResult('firebase', ['login:list']);
final output = result.stdout.toString() + result.stderr.toString();
final emailRegex =
RegExp(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}');
final lines = output.split('\n');
final allAccounts = <String>[];
String? currentAccount;
for (var line in lines) {
final match = emailRegex.firstMatch(line);
if (match != null) {
final email = match.group(0)!;
if (!allAccounts.contains(email)) {
allAccounts.add(email);
}
// Firebase CLI marks active account with * or (current)
// Or if it just says "Logged in as email"
if (line.contains('*') ||
line.contains('(current)') ||
line.contains('Logged in as $email')) {
currentAccount = email;
}
}
}
// Fallback: If only one account exists, it must be the current one
if (allAccounts.length == 1 && currentAccount == null) {
currentAccount = allAccounts.first;
}
// If no accounts, need to log in
if (allAccounts.isEmpty) {
print('โ No Firebase accounts found');
print('๐ฆ Triggering interactive login...\n');
await runInteractive('firebase', ['login']);
return;
}
// If we have accounts, ask which one to use
final prompt = PromptService();
final options = [
...allAccounts,
'+ Add a new account',
'โ Logout from active account'
];
print('๐ค Currently logged in accounts:');
for (int i = 0; i < allAccounts.length; i++) {
final isCurrent = allAccounts[i] == currentAccount;
print(' ${i + 1}. ${allAccounts[i]} ${isCurrent ? "โญ๏ธ (active)" : ""}');
}
print('');
final index =
prompt.select('Select Google account to use for this project', options);
if (index == allAccounts.length) {
// Add new account
print('๐ฆ Adding new account...\n');
// ๐ฅ CRITICAL: Logout from current to prevent the "login_hint" in URL
if (currentAccount != null) {
print('๐งน Clearing current session hint ($currentAccount)...');
await run('firebase', ['logout', currentAccount]);
}
print('๐ Opening browser for fresh login...\n');
await runInteractive('firebase', ['login']);
// Refresh the check to show the new account list
print('\n๐ Refreshing account list...');
await login();
return;
} else if (index == allAccounts.length + 1) {
// Logout
if (currentAccount != null) {
print('๐ซ Logging out from $currentAccount...\n');
await run('firebase', ['logout', currentAccount]);
print('โ
Logged out successfully');
await login(); // Refresh
return;
} else {
print('๐ซ Logging out from all accounts...\n');
await run('firebase', ['logout']);
await login();
return;
}
} else {
final selectedEmail = allAccounts[index];
if (selectedEmail != currentAccount) {
print('๐ Switching to account: $selectedEmail...\n');
print('โ ๏ธ Switching requires logging out of $currentAccount first.');
if (currentAccount != null) {
await run('firebase', ['logout', currentAccount]);
}
print('๐ฆ Now please log in to $selectedEmail in the browser...\n');
await runInteractive('firebase', ['login']);
await login(); // Re-verify to confirm switch worked
return;
} else {
print('โ
Using active account: $selectedEmail');
}
}
} catch (e) {
print('โ Firebase account check failed');
rethrow;
}
}