Export Firebase Authentication users to CSV file
Size
7.4 KB
Version
1.0.1
Created
Mar 1, 2026
Updated
about 2 months ago
1// ==UserScript==
2// @name Firebase Users CSV Exporter
3// @description Export Firebase Authentication users to CSV file
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// @grant GM.xmlhttpRequest
8// ==/UserScript==
9(function() {
10 'use strict';
11
12 // Check if the extension should still work (until January 16, 2026)
13 const expirationDate = new Date('2026-01-16T23:59:59');
14 const currentDate = new Date();
15
16 if (currentDate > expirationDate) {
17 console.log('Firebase CSV Exporter: Extension expired on January 16, 2026');
18 return;
19 }
20
21 console.log('Firebase CSV Exporter: Extension loaded');
22
23 // Function to wait for element to appear
24 function waitForElement(selector, timeout = 10000) {
25 return new Promise((resolve, reject) => {
26 if (document.querySelector(selector)) {
27 return resolve(document.querySelector(selector));
28 }
29
30 const observer = new MutationObserver(() => {
31 if (document.querySelector(selector)) {
32 observer.disconnect();
33 resolve(document.querySelector(selector));
34 }
35 });
36
37 observer.observe(document.body, {
38 childList: true,
39 subtree: true
40 });
41
42 setTimeout(() => {
43 observer.disconnect();
44 reject(new Error('Timeout waiting for element: ' + selector));
45 }, timeout);
46 });
47 }
48
49 // Function to extract user data from the table
50 function extractUserData() {
51 const users = [];
52 const rows = document.querySelectorAll('table[role="grid"] tbody tr[role="row"]');
53
54 console.log(`Firebase CSV Exporter: Found ${rows.length} user rows`);
55
56 rows.forEach((row, index) => {
57 try {
58 const cells = row.querySelectorAll('td[role="gridcell"]');
59
60 if (cells.length >= 4) {
61 const identifier = cells[0].textContent.trim();
62 const created = cells[2].textContent.trim();
63 const signedIn = cells[3].textContent.trim();
64
65 users.push({
66 identifier: identifier,
67 created: created,
68 signedIn: signedIn
69 });
70
71 console.log(`Firebase CSV Exporter: Extracted user ${index + 1}:`, { identifier, created, signedIn });
72 }
73 } catch (error) {
74 console.error('Firebase CSV Exporter: Error extracting user data from row:', error);
75 }
76 });
77
78 return users;
79 }
80
81 // Function to convert data to CSV
82 function convertToCSV(users) {
83 const headers = ['Identifier', 'Created', 'Signed In'];
84 const csvRows = [headers.join(',')];
85
86 users.forEach(user => {
87 const row = [
88 `"${user.identifier.replace(/"/g, '""')}"`,
89 `"${user.created.replace(/"/g, '""')}"`,
90 `"${user.signedIn.replace(/"/g, '""')}"`
91 ];
92 csvRows.push(row.join(','));
93 });
94
95 return csvRows.join('\n');
96 }
97
98 // Function to download CSV
99 function downloadCSV(csvContent) {
100 const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
101 const link = document.createElement('a');
102 const url = URL.createObjectURL(blob);
103
104 const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
105 link.setAttribute('href', url);
106 link.setAttribute('download', `firebase-users-${timestamp}.csv`);
107 link.style.visibility = 'hidden';
108
109 document.body.appendChild(link);
110 link.click();
111 document.body.removeChild(link);
112
113 console.log('Firebase CSV Exporter: CSV file downloaded');
114 }
115
116 // Function to create and add the export button
117 async function addExportButton() {
118 try {
119 // Wait for the Add User button to appear
120 const addUserButton = await waitForElement('button[data-test-id="add-user-button"]');
121 console.log('Firebase CSV Exporter: Add User button found');
122
123 // Check if export button already exists
124 if (document.getElementById('firebase-csv-export-btn')) {
125 console.log('Firebase CSV Exporter: Export button already exists');
126 return;
127 }
128
129 // Create the export button
130 const exportButton = document.createElement('button');
131 exportButton.id = 'firebase-csv-export-btn';
132 exportButton.className = 'mdc-button mat-mdc-button-base mdc-button--raised mat-mdc-raised-button mat-primary';
133 exportButton.style.marginLeft = '12px';
134 exportButton.innerHTML = `
135 <span class="mat-mdc-button-persistent-ripple mdc-button__ripple"></span>
136 <span class="mdc-button__label">Export CSV</span>
137 <span class="mat-focus-indicator"></span>
138 <span class="mat-mdc-button-touch-target"></span>
139 `;
140
141 // Add click event listener
142 exportButton.addEventListener('click', () => {
143 console.log('Firebase CSV Exporter: Export button clicked');
144
145 const users = extractUserData();
146
147 if (users.length === 0) {
148 alert('No users found to export');
149 console.log('Firebase CSV Exporter: No users found');
150 return;
151 }
152
153 const csvContent = convertToCSV(users);
154 downloadCSV(csvContent);
155
156 console.log(`Firebase CSV Exporter: Exported ${users.length} users`);
157 });
158
159 // Insert the button next to the Add User button
160 addUserButton.parentElement.appendChild(exportButton);
161 console.log('Firebase CSV Exporter: Export button added to page');
162
163 } catch (error) {
164 console.error('Firebase CSV Exporter: Error adding export button:', error);
165 }
166 }
167
168 // Initialize the extension
169 function init() {
170 // Check if we're on the authentication users page
171 if (window.location.href.includes('/authentication/users')) {
172 console.log('Firebase CSV Exporter: On authentication users page, adding export button');
173 addExportButton();
174
175 // Re-add button if page content changes (e.g., navigation)
176 const observer = new MutationObserver(() => {
177 if (document.querySelector('button[data-test-id="add-user-button"]') &&
178 !document.getElementById('firebase-csv-export-btn')) {
179 console.log('Firebase CSV Exporter: Page changed, re-adding export button');
180 addExportButton();
181 }
182 });
183
184 observer.observe(document.body, {
185 childList: true,
186 subtree: true
187 });
188 }
189 }
190
191 // Wait for page to load and initialize
192 if (document.readyState === 'loading') {
193 document.addEventListener('DOMContentLoaded', init);
194 } else {
195 init();
196 }
197
198})();