Firebase Users CSV Exporter

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})();