getActiveFirebaseAccount method
Email of the user the Firebase CLI is logged in as
(firebase login:list --json), or null when no login is stored.
Why this matters: the Firebase CLI prefers a stored login token
at ~/.config/configstore/firebase-tools.json over GOOGLE_APPLICATION_ CREDENTIALS. So a user who did firebase login previously as a
personal account that does NOT have firebase.admin on the
configured project will hit firebase apps:list PERMISSION_DENIED
even though Oracular set GOOGLE_APPLICATION_CREDENTIALS to a
service-account JSON that does have the role.
Used by _logCredentialDiagnostics to surface "you're authenticated
as <user>, but the SA is <other> — these don't match" so the user
can firebase logout and let Oracular's SA auth take over.
Implementation
Future<String?> getActiveFirebaseAccount() async {
final ProcessResult r = await _runner.run('firebase', <String>[
'login:list',
'--json',
]);
if (!r.success) return null;
try {
final dynamic decoded = jsonDecode(_stripAnsi(r.stdout));
if (decoded is Map<String, dynamic>) {
final dynamic result = decoded['result'];
if (result is List && result.isNotEmpty) {
final dynamic first = result.first;
if (first is Map<String, dynamic>) {
final dynamic user = first['user'];
if (user is Map<String, dynamic>) {
final dynamic email = user['email'];
if (email is String && email.trim().isNotEmpty) {
return email.trim();
}
}
// Some firebase CLI versions return the email at the top level.
final dynamic email = first['email'];
if (email is String && email.trim().isNotEmpty) {
return email.trim();
}
} else if (first is String && first.trim().isNotEmpty) {
return first.trim();
}
}
} else if (decoded is List && decoded.isNotEmpty) {
final dynamic first = decoded.first;
if (first is String && first.trim().isNotEmpty) return first.trim();
}
} catch (_) {
// Older firebase CLI doesn't support --json on login:list; parse
// the text output as a fallback ("Logged in as <email>").
final RegExp emailRe = RegExp(r'([\w.+-]+@[\w-]+\.[\w.-]+)');
final Match? match = emailRe.firstMatch(_stripAnsi(r.stdout));
if (match != null) return match.group(1);
}
return null;
}