Export all Firebase Authentication users to CSV with Identifier, Created, and Signed In columns
Size
8.5 KB
Version
1.0.1
Created
Apr 1, 2026
Updated
15 days ago
1// ==UserScript==
2// @name Firebase Users CSV Exporter
3// @description Export all Firebase Authentication users to CSV with Identifier, Created, and Signed In columns
4// @version 1.0.1
5// @match https://*.console.firebase.google.com/*
6// @icon https://www.gstatic.com/mobilesdk/240501_mobilesdk/firebase_16dp.png
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Firebase Users CSV Exporter: Extension loaded');
12
13 // Debounce function to prevent multiple rapid calls
14 function debounce(func, wait) {
15 let timeout;
16 return function executedFunction(...args) {
17 const later = () => {
18 clearTimeout(timeout);
19 func(...args);
20 };
21 clearTimeout(timeout);
22 timeout = setTimeout(later, wait);
23 };
24 }
25
26 // Function to convert data to CSV format
27 function convertToCSV(data) {
28 if (!data || data.length === 0) {
29 return 'Identifier,Created,Signed In\n';
30 }
31
32 const headers = ['Identifier', 'Created', 'Signed In'];
33 const csvRows = [headers.join(',')];
34
35 data.forEach(row => {
36 const values = [
37 `"${(row.identifier || '').replace(/"/g, '""')}"`,
38 `"${(row.created || '').replace(/"/g, '""')}"`,
39 `"${(row.signedIn || '').replace(/"/g, '""')}"`
40 ];
41 csvRows.push(values.join(','));
42 });
43
44 return csvRows.join('\n');
45 }
46
47 // Function to download CSV file
48 function downloadCSV(csvContent, filename) {
49 const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
50 const link = document.createElement('a');
51 const url = URL.createObjectURL(blob);
52
53 link.setAttribute('href', url);
54 link.setAttribute('download', filename);
55 link.style.visibility = 'hidden';
56 document.body.appendChild(link);
57 link.click();
58 document.body.removeChild(link);
59
60 console.log('Firebase Users CSV Exporter: CSV file downloaded successfully');
61 }
62
63 // Function to extract user data from the table
64 function extractUserData() {
65 console.log('Firebase Users CSV Exporter: Extracting user data from table');
66
67 const userData = [];
68 const tableRows = document.querySelectorAll('table[role="grid"][aria-label*="Usuario"] tbody.mdc-data-table__content tr.mat-mdc-row');
69
70 console.log(`Firebase Users CSV Exporter: Found ${tableRows.length} user rows`);
71
72 tableRows.forEach((row, index) => {
73 try {
74 // Extract identifier (email or phone)
75 const identifierCell = row.querySelector('td.cdk-column-identifier .identifier-text');
76 const identifier = identifierCell ? identifierCell.textContent.trim() : '';
77
78 // Extract created date
79 const createdCell = row.querySelector('td.cdk-column-created-at');
80 const created = createdCell ? createdCell.textContent.trim() : '';
81
82 // Extract last login date
83 const lastLoginCell = row.querySelector('td.cdk-column-last-login');
84 const signedIn = lastLoginCell ? lastLoginCell.textContent.trim() : '';
85
86 if (identifier) {
87 userData.push({
88 identifier: identifier,
89 created: created,
90 signedIn: signedIn
91 });
92 console.log(`Firebase Users CSV Exporter: Extracted user ${index + 1}: ${identifier}`);
93 }
94 } catch (error) {
95 console.error(`Firebase Users CSV Exporter: Error extracting row ${index}:`, error);
96 }
97 });
98
99 return userData;
100 }
101
102 // Function to handle export button click
103 async function handleExportClick() {
104 console.log('Firebase Users CSV Exporter: Export button clicked');
105
106 const button = document.querySelector('button[data-export-users-csv]');
107 if (!button) return;
108
109 // Disable button and show loading state
110 button.disabled = true;
111 const originalText = button.querySelector('.mdc-button__label').textContent;
112 button.querySelector('.mdc-button__label').textContent = 'Exportando...';
113
114 try {
115 // Extract user data
116 const userData = extractUserData();
117
118 if (userData.length === 0) {
119 alert('No se encontraron usuarios para exportar. Asegúrate de que la tabla de usuarios esté visible.');
120 console.log('Firebase Users CSV Exporter: No users found to export');
121 return;
122 }
123
124 // Convert to CSV
125 const csvContent = convertToCSV(userData);
126
127 // Generate filename with timestamp
128 const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
129 const filename = `firebase-users-${timestamp}.csv`;
130
131 // Download CSV
132 downloadCSV(csvContent, filename);
133
134 console.log(`Firebase Users CSV Exporter: Successfully exported ${userData.length} users`);
135
136 } catch (error) {
137 console.error('Firebase Users CSV Exporter: Error during export:', error);
138 alert('Error al exportar usuarios. Por favor, intenta de nuevo.');
139 } finally {
140 // Re-enable button
141 button.disabled = false;
142 button.querySelector('.mdc-button__label').textContent = originalText;
143 }
144 }
145
146 // Function to create and add the export button
147 function addExportButton() {
148 // Check if button already exists
149 if (document.querySelector('button[data-export-users-csv]')) {
150 console.log('Firebase Users CSV Exporter: Export button already exists');
151 return;
152 }
153
154 // Find the "Agregar usuario" button
155 const addUserButton = document.querySelector('button[data-test-id="add-user-button"]');
156
157 if (!addUserButton) {
158 console.log('Firebase Users CSV Exporter: Add user button not found, will retry');
159 return;
160 }
161
162 console.log('Firebase Users CSV Exporter: Adding export button');
163
164 // Create the export button with matching Firebase style
165 const exportButton = document.createElement('button');
166 exportButton.setAttribute('data-export-users-csv', 'true');
167 exportButton.className = 'mdc-button mat-mdc-button-base mdc-button--raised mat-mdc-raised-button mat-primary';
168 exportButton.style.marginLeft = '8px';
169
170 exportButton.innerHTML = `
171 <span class="mat-mdc-button-persistent-ripple mdc-button__ripple"></span>
172 <span class="mdc-button__label">Exportar CSV</span>
173 <span class="mat-focus-indicator"></span>
174 <span class="mat-mdc-button-touch-target"></span>
175 `;
176
177 // Add click event listener
178 exportButton.addEventListener('click', handleExportClick);
179
180 // Insert the button next to "Agregar usuario" button
181 const parentContainer = addUserButton.parentElement;
182 if (parentContainer) {
183 parentContainer.insertBefore(exportButton, addUserButton.nextSibling);
184 console.log('Firebase Users CSV Exporter: Export button added successfully');
185 }
186 }
187
188 // Function to initialize the extension
189 function init() {
190 console.log('Firebase Users CSV Exporter: Initializing extension');
191
192 // Check if we're on the users page
193 if (!window.location.href.includes('/authentication/users')) {
194 console.log('Firebase Users CSV Exporter: Not on users page, waiting for navigation');
195 }
196
197 // Add the export button
198 addExportButton();
199
200 // Observe DOM changes to handle dynamic content loading
201 const observer = new MutationObserver(debounce(() => {
202 if (window.location.href.includes('/authentication/users')) {
203 addExportButton();
204 }
205 }, 500));
206
207 // Start observing
208 observer.observe(document.body, {
209 childList: true,
210 subtree: true
211 });
212
213 console.log('Firebase Users CSV Exporter: Extension initialized successfully');
214 }
215
216 // Wait for the page to be fully loaded
217 if (document.readyState === 'loading') {
218 document.addEventListener('DOMContentLoaded', init);
219 } else {
220 // DOM is already ready
221 setTimeout(init, 1000);
222 }
223
224})();