cloudfire-auth 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE +13 -0
- package/README.md +94 -0
- package/dist/CloudFireAuth.d.ts +291 -0
- package/dist/CloudFireAuth.js +346 -0
- package/dist/google-auth/get-oauth-2-token.d.ts +15 -0
- package/dist/google-auth/get-oauth-2-token.js +66 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/rest-api/create-user.d.ts +2 -0
- package/dist/rest-api/create-user.js +3 -0
- package/dist/rest-api/delete-user.d.ts +175 -0
- package/dist/rest-api/delete-user.js +207 -0
- package/dist/rest-api/delete-users.d.ts +2 -0
- package/dist/rest-api/delete-users.js +3 -0
- package/dist/rest-api/get-user-by-email.d.ts +2 -0
- package/dist/rest-api/get-user-by-email.js +3 -0
- package/dist/rest-api/get-user-by-phone-number.d.ts +2 -0
- package/dist/rest-api/get-user-by-phone-number.js +3 -0
- package/dist/rest-api/get-user-by-provider-uid.d.ts +2 -0
- package/dist/rest-api/get-user-by-provider-uid.js +3 -0
- package/dist/rest-api/get-user.d.ts +99 -0
- package/dist/rest-api/get-user.js +177 -0
- package/dist/rest-api/get-users.d.ts +2 -0
- package/dist/rest-api/get-users.js +3 -0
- package/dist/rest-api/list-users.d.ts +2 -0
- package/dist/rest-api/list-users.js +3 -0
- package/dist/rest-api/revoke-refresh-tokens.d.ts +116 -0
- package/dist/rest-api/revoke-refresh-tokens.js +151 -0
- package/dist/rest-api/set-custom-user-claims.d.ts +173 -0
- package/dist/rest-api/set-custom-user-claims.js +261 -0
- package/dist/rest-api/standard-request.d.ts +12 -0
- package/dist/rest-api/standard-request.js +20 -0
- package/dist/rest-api/update-user.d.ts +175 -0
- package/dist/rest-api/update-user.js +375 -0
- package/dist/rest-api/verify-id-token.d.ts +127 -0
- package/dist/rest-api/verify-id-token.js +273 -0
- package/dist/rest-api/verify-session-cookie.d.ts +2 -0
- package/dist/rest-api/verify-session-cookie.js +3 -0
- package/dist/types/firebase-admin/auth-config.d.ts +851 -0
- package/dist/types/firebase-admin/auth-config.js +1 -0
- package/dist/types/firebase-admin/identifier.d.ts +57 -0
- package/dist/types/firebase-admin/identifier.js +1 -0
- package/dist/types/firebase-admin/index.d.ts +153 -0
- package/dist/types/firebase-admin/index.js +1 -0
- package/dist/types/firebase-admin/token-verifier.d.ts +219 -0
- package/dist/types/firebase-admin/token-verifier.js +1 -0
- package/dist/types/firebase-admin/user-record.d.ts +289 -0
- package/dist/types/firebase-admin/user-record.js +1 -0
- package/dist/types/google-auth.d.ts +25 -0
- package/dist/types/google-auth.js +1 -0
- package/dist/types/service-account-key.d.ts +13 -0
- package/dist/types/service-account-key.js +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +1 -0
- package/package.json +56 -0
- package/third_party/firebase-admin-license.txt +201 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deletes an existing Firebase Auth user permanently from the system.
|
|
3
|
+
*
|
|
4
|
+
* This function completely removes a user account and all associated data from Firebase Auth.
|
|
5
|
+
* Once deleted, the user cannot be recovered and will need to create a new account if they
|
|
6
|
+
* wish to access the system again. This operation is permanent and irreversible.
|
|
7
|
+
*
|
|
8
|
+
* **Important Implementation Details:**
|
|
9
|
+
* - Permanently deletes the user account from Firebase Auth
|
|
10
|
+
* - All user data including profile information, custom claims, and metadata is removed
|
|
11
|
+
* - Linked provider accounts are unlinked and removed
|
|
12
|
+
* - Multi-factor authentication settings are deleted
|
|
13
|
+
* - The operation is idempotent - deleting a non-existent user succeeds silently
|
|
14
|
+
* - Uses Firebase Admin API's `accounts:delete` endpoint
|
|
15
|
+
*
|
|
16
|
+
* **Security Considerations:**
|
|
17
|
+
* - This operation cannot be undone - user data is permanently lost
|
|
18
|
+
* - Should be used with extreme caution in production environments
|
|
19
|
+
* - Consider disabling users instead of deleting for audit and recovery purposes
|
|
20
|
+
* - Requires proper authorization and access control in production systems
|
|
21
|
+
* - May affect user sessions and active tokens immediately
|
|
22
|
+
*
|
|
23
|
+
* **Use Cases:**
|
|
24
|
+
* - User account closure requests (GDPR compliance)
|
|
25
|
+
* - Administrative account management
|
|
26
|
+
* - Cleanup of test or demo accounts
|
|
27
|
+
* - System maintenance and data purging
|
|
28
|
+
* - User request for account deletion
|
|
29
|
+
*
|
|
30
|
+
* **Behavioral Notes:**
|
|
31
|
+
* - **Idempotent Operation**: Deleting a non-existent user does not throw an error
|
|
32
|
+
* - **Immediate Effect**: User cannot authenticate immediately after deletion
|
|
33
|
+
* - **Related Data**: Only Firebase Auth data is deleted - external data must be handled separately
|
|
34
|
+
* - **Audit Trail**: Consider logging deletion events for compliance and debugging
|
|
35
|
+
* - **Batch Operations**: For bulk deletions, consider using deleteUsersHandler instead
|
|
36
|
+
*
|
|
37
|
+
* @param uid - The Firebase Auth user ID (localId) of the user to delete.
|
|
38
|
+
* Must be a valid Firebase user identifier string.
|
|
39
|
+
* @param oauth2AccessToken - Valid OAuth2 access token with Firebase Admin API privileges.
|
|
40
|
+
* Obtained via service account authentication.
|
|
41
|
+
*
|
|
42
|
+
* @returns Promise that resolves when the deletion is complete.
|
|
43
|
+
* No return value - success is indicated by promise resolution.
|
|
44
|
+
* The promise resolves successfully even if the user doesn't exist.
|
|
45
|
+
*
|
|
46
|
+
* @throws {Error} When deletion fails due to system errors:
|
|
47
|
+
* - **Validation Errors**:
|
|
48
|
+
* - "uid must be a non-empty string" - Invalid or missing uid parameter
|
|
49
|
+
* - "oauth2AccessToken must be a non-empty string" - Invalid or missing token parameter
|
|
50
|
+
* - **Firebase API Errors**:
|
|
51
|
+
* - "Failed to delete user: {status} {statusText} - {details}" - API errors with detailed information
|
|
52
|
+
* - "INVALID_ID_TOKEN" - OAuth2 token is invalid or expired
|
|
53
|
+
* - "PERMISSION_DENIED" - Insufficient permissions to delete users
|
|
54
|
+
* - **Network Errors**: Various network-related failures during API communication
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* // Delete a user account (basic usage)
|
|
59
|
+
* try {
|
|
60
|
+
* await deleteUserHandler('user123', oauth2Token);
|
|
61
|
+
* console.log('User account deleted successfully');
|
|
62
|
+
* } catch (error) {
|
|
63
|
+
* console.error('Failed to delete user:', error.message);
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* // User-initiated account deletion with confirmation
|
|
70
|
+
* async function handleAccountDeletion(userId: string) {
|
|
71
|
+
* try {
|
|
72
|
+
* // 1. Verify user identity and intent
|
|
73
|
+
* const user = await getUserHandler(userId, oauth2Token);
|
|
74
|
+
* console.log(`Deleting account for: ${user.email}`);
|
|
75
|
+
*
|
|
76
|
+
* // 2. Perform additional cleanup (external data, files, etc.)
|
|
77
|
+
* await cleanupUserData(userId);
|
|
78
|
+
*
|
|
79
|
+
* // 3. Delete the Firebase Auth account
|
|
80
|
+
* await deleteUserHandler(userId, oauth2Token);
|
|
81
|
+
*
|
|
82
|
+
* console.log('Account deletion completed');
|
|
83
|
+
* } catch (error) {
|
|
84
|
+
* console.error('Account deletion failed:', error.message);
|
|
85
|
+
* throw error;
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // Administrative cleanup with error handling
|
|
93
|
+
* async function adminDeleteInactiveUsers(userIds: string[]) {
|
|
94
|
+
* const results = [];
|
|
95
|
+
*
|
|
96
|
+
* for (const userId of userIds) {
|
|
97
|
+
* try {
|
|
98
|
+
* await deleteUserHandler(userId, oauth2Token);
|
|
99
|
+
* results.push({ userId, status: 'deleted', error: null });
|
|
100
|
+
* } catch (error) {
|
|
101
|
+
* results.push({ userId, status: 'failed', error: error.message });
|
|
102
|
+
* }
|
|
103
|
+
* }
|
|
104
|
+
*
|
|
105
|
+
* return results;
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* // GDPR compliance workflow
|
|
112
|
+
* async function processGDPRDeletionRequest(userEmail: string) {
|
|
113
|
+
* try {
|
|
114
|
+
* // 1. Find the user by email
|
|
115
|
+
* const user = await getUserByEmailHandler(userEmail, oauth2Token);
|
|
116
|
+
*
|
|
117
|
+
* // 2. Export user data for compliance (if required)
|
|
118
|
+
* const userData = await exportUserData(user.uid);
|
|
119
|
+
* await saveDataExport(userData, `${userEmail}-export.json`);
|
|
120
|
+
*
|
|
121
|
+
* // 3. Delete associated external data
|
|
122
|
+
* await deleteUserFromDatabase(user.uid);
|
|
123
|
+
* await deleteUserFiles(user.uid);
|
|
124
|
+
*
|
|
125
|
+
* // 4. Delete the Firebase Auth account
|
|
126
|
+
* await deleteUserHandler(user.uid, oauth2Token);
|
|
127
|
+
*
|
|
128
|
+
* // 5. Log the deletion for audit purposes
|
|
129
|
+
* await logGDPRDeletion({
|
|
130
|
+
* userEmail,
|
|
131
|
+
* userId: user.uid,
|
|
132
|
+
* deletedAt: new Date(),
|
|
133
|
+
* reason: 'GDPR_REQUEST'
|
|
134
|
+
* });
|
|
135
|
+
*
|
|
136
|
+
* console.log(`GDPR deletion completed for ${userEmail}`);
|
|
137
|
+
* } catch (error) {
|
|
138
|
+
* console.error(`GDPR deletion failed for ${userEmail}:`, error);
|
|
139
|
+
* throw error;
|
|
140
|
+
* }
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* **Technical Implementation:**
|
|
145
|
+
* - Makes POST request to `https://identitytoolkit.googleapis.com/v1/accounts:delete`
|
|
146
|
+
* - Sends `localId` parameter to identify the target user
|
|
147
|
+
* - Handles Firebase API error responses with detailed error information
|
|
148
|
+
* - Validates input parameters before making API calls
|
|
149
|
+
* - Operates idempotently - no error for non-existent users
|
|
150
|
+
*
|
|
151
|
+
* **Testing Considerations:**
|
|
152
|
+
* - Create test users specifically for deletion tests
|
|
153
|
+
* - Verify user is actually deleted by attempting to retrieve them
|
|
154
|
+
* - Test idempotent behavior by deleting non-existent users
|
|
155
|
+
* - Unit tests should mock the fetch call and verify request format
|
|
156
|
+
* - Integration tests should verify actual deletion from Firebase
|
|
157
|
+
*
|
|
158
|
+
* **Production Considerations:**
|
|
159
|
+
* - **Logging**: Log all deletion operations for audit trails
|
|
160
|
+
* - **Confirmation**: Implement confirmation flows for critical deletions
|
|
161
|
+
* - **Backup**: Consider backing up user data before deletion
|
|
162
|
+
* - **Related Data**: Ensure external data is properly cleaned up
|
|
163
|
+
* - **Rate Limits**: Be aware of Firebase API rate limits for bulk operations
|
|
164
|
+
* - **Alternative**: Consider using `disabled: true` instead of deletion for recoverable scenarios
|
|
165
|
+
*
|
|
166
|
+
* @see {@link getUserHandler} For retrieving user data before deletion
|
|
167
|
+
* @see {@link updateUserHandler} For disabling users instead of deletion
|
|
168
|
+
* @see {@link deleteUsersHandler} For bulk deletion operations
|
|
169
|
+
* @see {@link https://firebase.google.com/docs/auth/admin/manage-users#delete_a_user Firebase User Deletion}
|
|
170
|
+
* @see {@link https://firebase.google.com/docs/reference/rest/auth#section-delete-account Firebase REST API}
|
|
171
|
+
*
|
|
172
|
+
* @package
|
|
173
|
+
* @since 1.0.0
|
|
174
|
+
*/
|
|
175
|
+
export async function deleteUserHandler(uid, oauth2AccessToken) {
|
|
176
|
+
// Validate input parameters
|
|
177
|
+
if (typeof uid !== "string" || uid.length === 0) {
|
|
178
|
+
throw new Error("uid must be a non-empty string");
|
|
179
|
+
}
|
|
180
|
+
if (typeof oauth2AccessToken !== "string" || oauth2AccessToken.length === 0) {
|
|
181
|
+
throw new Error("oauth2AccessToken must be a non-empty string");
|
|
182
|
+
}
|
|
183
|
+
// Make the API call to delete the user
|
|
184
|
+
const response = await fetch("https://identitytoolkit.googleapis.com/v1/accounts:delete", {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": "application/json",
|
|
188
|
+
Authorization: `Bearer ${oauth2AccessToken}`,
|
|
189
|
+
},
|
|
190
|
+
body: JSON.stringify({
|
|
191
|
+
localId: uid,
|
|
192
|
+
}),
|
|
193
|
+
});
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
const errorText = await response.text();
|
|
196
|
+
let errorMessage;
|
|
197
|
+
try {
|
|
198
|
+
const errorData = JSON.parse(errorText);
|
|
199
|
+
const formattedErrorText = JSON.stringify(errorData, null, 2);
|
|
200
|
+
errorMessage = `Failed to delete user: ${response.status} ${response.statusText}\n${formattedErrorText}`;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
errorMessage = `Failed to delete user: ${response.status} ${response.statusText} - ${errorText}`;
|
|
204
|
+
}
|
|
205
|
+
throw new Error(errorMessage);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { UserRecord } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Retrieves a Firebase Auth user by their unique identifier (UID).
|
|
4
|
+
*
|
|
5
|
+
* This function provides comprehensive user data retrieval by making a direct call
|
|
6
|
+
* to the Firebase Admin API's accounts:lookup endpoint. It transforms the Firebase
|
|
7
|
+
* response into a standardized UserRecord format that matches the Firebase Admin SDK.
|
|
8
|
+
*
|
|
9
|
+
* **Retrieved User Information:**
|
|
10
|
+
* - **Basic Profile**: uid, email, emailVerified, displayName, photoURL, phoneNumber
|
|
11
|
+
* - **Account Status**: disabled status, creation and last sign-in timestamps
|
|
12
|
+
* - **Provider Data**: All linked authentication providers with their details
|
|
13
|
+
* - **Custom Claims**: Parsed from customAttributes if present
|
|
14
|
+
* - **Metadata**: Creation time, last sign-in time, last refresh time
|
|
15
|
+
*
|
|
16
|
+
* **Firebase API Integration:**
|
|
17
|
+
* - Uses Firebase Admin API's `accounts:lookup` endpoint
|
|
18
|
+
* - Requires valid OAuth2 access token with admin privileges
|
|
19
|
+
* - Handles Firebase response format `{ users: [GetAccountInfoUserResponse] }`
|
|
20
|
+
* - Safely processes optional fields with proper null handling
|
|
21
|
+
* - Transforms provider information to match Admin SDK format
|
|
22
|
+
*
|
|
23
|
+
* @param uid - The Firebase Auth user ID (localId) to retrieve.
|
|
24
|
+
* Must be a valid, existing Firebase user identifier.
|
|
25
|
+
* @param oauth2AccessToken - Valid OAuth2 access token with Firebase Admin API privileges.
|
|
26
|
+
* Obtained via service account authentication.
|
|
27
|
+
*
|
|
28
|
+
* @returns Promise that resolves to the UserRecord containing complete user information.
|
|
29
|
+
*
|
|
30
|
+
* @throws {Error} When user lookup fails or user doesn't exist:
|
|
31
|
+
* - "Failed to get user: {status} {statusText}, {details}" - Firebase API errors with HTTP details
|
|
32
|
+
* - "User not found: {uid}" - No user exists with the specified UID
|
|
33
|
+
* - Network errors during Firebase API communication
|
|
34
|
+
* - JSON parsing errors from malformed Firebase responses
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Basic user retrieval
|
|
39
|
+
* const user = await getUserHandler('user123', oauth2Token);
|
|
40
|
+
* console.log(`User: ${user.displayName} (${user.email})`);
|
|
41
|
+
* console.log(`Created: ${user.metadata.creationTime}`);
|
|
42
|
+
* console.log(`Verified: ${user.emailVerified}`);
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Check user status and providers
|
|
48
|
+
* const user = await getUserHandler('user456', oauth2Token);
|
|
49
|
+
*
|
|
50
|
+
* if (user.disabled) {
|
|
51
|
+
* console.log('Account is disabled');
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* // List all authentication providers
|
|
55
|
+
* user.providerData.forEach(provider => {
|
|
56
|
+
* console.log(`Provider: ${provider.providerId} - ${provider.email}`);
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* // Access custom claims if available
|
|
60
|
+
* const customClaims = user.customClaims;
|
|
61
|
+
* if (customClaims?.role) {
|
|
62
|
+
* console.log(`User role: ${customClaims.role}`);
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // Error handling with detailed information
|
|
69
|
+
* try {
|
|
70
|
+
* const user = await getUserHandler(userId, oauth2Token);
|
|
71
|
+
* console.log('User retrieved successfully:', user.uid);
|
|
72
|
+
* } catch (error) {
|
|
73
|
+
* if (error.message.includes('User not found')) {
|
|
74
|
+
* console.error('User does not exist:', userId);
|
|
75
|
+
* } else if (error.message.includes('Failed to get user')) {
|
|
76
|
+
* console.error('Firebase API error:', error.message);
|
|
77
|
+
* } else {
|
|
78
|
+
* console.error('Unexpected error:', error);
|
|
79
|
+
* }
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* **Important Notes:**
|
|
84
|
+
* - Returns complete user profile including sensitive information (use appropriate access controls)
|
|
85
|
+
* - Custom claims are automatically parsed from Firebase's customAttributes field
|
|
86
|
+
* - Provider data includes all linked authentication methods (Google, Facebook, etc.)
|
|
87
|
+
* - Metadata timestamps are in ISO 8601 format or empty strings if not available
|
|
88
|
+
* - Phone numbers are returned in the format stored in Firebase (may not be E.164)
|
|
89
|
+
* - Disabled users can still be retrieved but cannot authenticate
|
|
90
|
+
* - This function requires Firebase Admin privileges - ensure proper token scoping
|
|
91
|
+
*
|
|
92
|
+
* @see {@link https://firebase.google.com/docs/auth/admin/manage-users Firebase User Management}
|
|
93
|
+
* @see {@link https://firebase.google.com/docs/reference/rest/auth#section-get-account-info Firebase REST API}
|
|
94
|
+
* @see {@link UserRecord} For complete UserRecord interface documentation
|
|
95
|
+
*
|
|
96
|
+
* @package
|
|
97
|
+
* @since 1.0.0
|
|
98
|
+
*/
|
|
99
|
+
export declare function getUserHandler(uid: string, oauth2AccessToken: string): Promise<UserRecord>;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves a Firebase Auth user by their unique identifier (UID).
|
|
3
|
+
*
|
|
4
|
+
* This function provides comprehensive user data retrieval by making a direct call
|
|
5
|
+
* to the Firebase Admin API's accounts:lookup endpoint. It transforms the Firebase
|
|
6
|
+
* response into a standardized UserRecord format that matches the Firebase Admin SDK.
|
|
7
|
+
*
|
|
8
|
+
* **Retrieved User Information:**
|
|
9
|
+
* - **Basic Profile**: uid, email, emailVerified, displayName, photoURL, phoneNumber
|
|
10
|
+
* - **Account Status**: disabled status, creation and last sign-in timestamps
|
|
11
|
+
* - **Provider Data**: All linked authentication providers with their details
|
|
12
|
+
* - **Custom Claims**: Parsed from customAttributes if present
|
|
13
|
+
* - **Metadata**: Creation time, last sign-in time, last refresh time
|
|
14
|
+
*
|
|
15
|
+
* **Firebase API Integration:**
|
|
16
|
+
* - Uses Firebase Admin API's `accounts:lookup` endpoint
|
|
17
|
+
* - Requires valid OAuth2 access token with admin privileges
|
|
18
|
+
* - Handles Firebase response format `{ users: [GetAccountInfoUserResponse] }`
|
|
19
|
+
* - Safely processes optional fields with proper null handling
|
|
20
|
+
* - Transforms provider information to match Admin SDK format
|
|
21
|
+
*
|
|
22
|
+
* @param uid - The Firebase Auth user ID (localId) to retrieve.
|
|
23
|
+
* Must be a valid, existing Firebase user identifier.
|
|
24
|
+
* @param oauth2AccessToken - Valid OAuth2 access token with Firebase Admin API privileges.
|
|
25
|
+
* Obtained via service account authentication.
|
|
26
|
+
*
|
|
27
|
+
* @returns Promise that resolves to the UserRecord containing complete user information.
|
|
28
|
+
*
|
|
29
|
+
* @throws {Error} When user lookup fails or user doesn't exist:
|
|
30
|
+
* - "Failed to get user: {status} {statusText}, {details}" - Firebase API errors with HTTP details
|
|
31
|
+
* - "User not found: {uid}" - No user exists with the specified UID
|
|
32
|
+
* - Network errors during Firebase API communication
|
|
33
|
+
* - JSON parsing errors from malformed Firebase responses
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* // Basic user retrieval
|
|
38
|
+
* const user = await getUserHandler('user123', oauth2Token);
|
|
39
|
+
* console.log(`User: ${user.displayName} (${user.email})`);
|
|
40
|
+
* console.log(`Created: ${user.metadata.creationTime}`);
|
|
41
|
+
* console.log(`Verified: ${user.emailVerified}`);
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* // Check user status and providers
|
|
47
|
+
* const user = await getUserHandler('user456', oauth2Token);
|
|
48
|
+
*
|
|
49
|
+
* if (user.disabled) {
|
|
50
|
+
* console.log('Account is disabled');
|
|
51
|
+
* }
|
|
52
|
+
*
|
|
53
|
+
* // List all authentication providers
|
|
54
|
+
* user.providerData.forEach(provider => {
|
|
55
|
+
* console.log(`Provider: ${provider.providerId} - ${provider.email}`);
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* // Access custom claims if available
|
|
59
|
+
* const customClaims = user.customClaims;
|
|
60
|
+
* if (customClaims?.role) {
|
|
61
|
+
* console.log(`User role: ${customClaims.role}`);
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* // Error handling with detailed information
|
|
68
|
+
* try {
|
|
69
|
+
* const user = await getUserHandler(userId, oauth2Token);
|
|
70
|
+
* console.log('User retrieved successfully:', user.uid);
|
|
71
|
+
* } catch (error) {
|
|
72
|
+
* if (error.message.includes('User not found')) {
|
|
73
|
+
* console.error('User does not exist:', userId);
|
|
74
|
+
* } else if (error.message.includes('Failed to get user')) {
|
|
75
|
+
* console.error('Firebase API error:', error.message);
|
|
76
|
+
* } else {
|
|
77
|
+
* console.error('Unexpected error:', error);
|
|
78
|
+
* }
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* **Important Notes:**
|
|
83
|
+
* - Returns complete user profile including sensitive information (use appropriate access controls)
|
|
84
|
+
* - Custom claims are automatically parsed from Firebase's customAttributes field
|
|
85
|
+
* - Provider data includes all linked authentication methods (Google, Facebook, etc.)
|
|
86
|
+
* - Metadata timestamps are in ISO 8601 format or empty strings if not available
|
|
87
|
+
* - Phone numbers are returned in the format stored in Firebase (may not be E.164)
|
|
88
|
+
* - Disabled users can still be retrieved but cannot authenticate
|
|
89
|
+
* - This function requires Firebase Admin privileges - ensure proper token scoping
|
|
90
|
+
*
|
|
91
|
+
* @see {@link https://firebase.google.com/docs/auth/admin/manage-users Firebase User Management}
|
|
92
|
+
* @see {@link https://firebase.google.com/docs/reference/rest/auth#section-get-account-info Firebase REST API}
|
|
93
|
+
* @see {@link UserRecord} For complete UserRecord interface documentation
|
|
94
|
+
*
|
|
95
|
+
* @package
|
|
96
|
+
* @since 1.0.0
|
|
97
|
+
*/
|
|
98
|
+
export async function getUserHandler(uid, oauth2AccessToken) {
|
|
99
|
+
const response = await fetch("https://identitytoolkit.googleapis.com/v1/accounts:lookup", {
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: {
|
|
102
|
+
"Content-Type": "application/json",
|
|
103
|
+
Authorization: `Bearer ${oauth2AccessToken}`,
|
|
104
|
+
},
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
localId: [uid],
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
throw new Error(`Failed to get user: ${response.status} ${response.statusText}, ${await response.text()}`);
|
|
111
|
+
}
|
|
112
|
+
const data = (await response.json());
|
|
113
|
+
let userData;
|
|
114
|
+
try {
|
|
115
|
+
userData = data.users[0];
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error("Error parsing user data:", error);
|
|
119
|
+
throw new Error(`User not found: ${uid}`);
|
|
120
|
+
}
|
|
121
|
+
if (!userData) {
|
|
122
|
+
throw new Error(`User not found: ${uid}`);
|
|
123
|
+
}
|
|
124
|
+
const userRecord = convertToUserRecord(userData);
|
|
125
|
+
return userRecord;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Converts Firebase's GetAccountInfoUserResponse to a UserRecord to match
|
|
129
|
+
* the Firebase Admin SDK format.
|
|
130
|
+
*
|
|
131
|
+
* This function safely handles optional fields and transforms the Firebase API
|
|
132
|
+
* response structure into a standardized UserRecord. It includes proper null
|
|
133
|
+
* handling, custom claims parsing, and provider data transformation.
|
|
134
|
+
*
|
|
135
|
+
* @param userData - The raw user data from Firebase Admin API.
|
|
136
|
+
* @returns The converted UserRecord with all available user information.
|
|
137
|
+
* @internal
|
|
138
|
+
*/
|
|
139
|
+
function convertToUserRecord(userData) {
|
|
140
|
+
// Safely map provider user info, handling optional fields properly
|
|
141
|
+
const userInfo = userData.providerUserInfo?.map((providerUserInfo) => ({
|
|
142
|
+
uid: providerUserInfo.rawId || providerUserInfo.federatedId || "",
|
|
143
|
+
displayName: providerUserInfo.displayName || null,
|
|
144
|
+
email: providerUserInfo.email || null,
|
|
145
|
+
photoURL: providerUserInfo.photoUrl || null,
|
|
146
|
+
providerId: providerUserInfo.providerId,
|
|
147
|
+
phoneNumber: providerUserInfo.phoneNumber || null,
|
|
148
|
+
})) || [];
|
|
149
|
+
// Parse custom claims from customAttributes JSON string
|
|
150
|
+
let customClaims = null;
|
|
151
|
+
if (userData.customAttributes) {
|
|
152
|
+
try {
|
|
153
|
+
customClaims = JSON.parse(userData.customAttributes);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
// Invalid JSON in customAttributes, leave as null
|
|
157
|
+
console.warn("Failed to parse custom attributes as JSON:", error);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const userRecord = {
|
|
161
|
+
uid: userData.localId,
|
|
162
|
+
email: userData.email || null,
|
|
163
|
+
emailVerified: userData.emailVerified || false,
|
|
164
|
+
displayName: userData.displayName || null,
|
|
165
|
+
photoURL: userData.photoUrl || null,
|
|
166
|
+
phoneNumber: userData.phoneNumber || null,
|
|
167
|
+
disabled: userData.disabled || false,
|
|
168
|
+
providerData: userInfo,
|
|
169
|
+
customClaims: customClaims,
|
|
170
|
+
metadata: {
|
|
171
|
+
creationTime: userData.createdAt || "",
|
|
172
|
+
lastSignInTime: userData.lastLoginAt || "",
|
|
173
|
+
lastRefreshTime: userData.lastRefreshAt || null,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
return userRecord;
|
|
177
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Revokes all refresh tokens for an existing Firebase Auth user.
|
|
3
|
+
*
|
|
4
|
+
* This function updates the user's `tokensValidAfterTime` to the current UTC timestamp,
|
|
5
|
+
* effectively invalidating all existing refresh tokens and sessions for the specified user.
|
|
6
|
+
* After calling this function, the user will need to re-authenticate to obtain new tokens.
|
|
7
|
+
*
|
|
8
|
+
* **Important Implementation Details:**
|
|
9
|
+
* - Updates the user's `tokensValidAfterTime` to the current UTC timestamp (in seconds)
|
|
10
|
+
* - All existing refresh tokens become invalid immediately
|
|
11
|
+
* - Existing ID tokens remain valid until their natural expiration (1 hour) unless verified with `checkRevoked=true`
|
|
12
|
+
* - Requires accurate server time synchronization for proper functionality
|
|
13
|
+
* - Uses Firebase Admin API's `accounts:update` endpoint with `validSince` parameter
|
|
14
|
+
*
|
|
15
|
+
* **Security Considerations:**
|
|
16
|
+
* - Forces user re-authentication on all devices and sessions
|
|
17
|
+
* - Useful for compromised accounts or mandatory sign-out scenarios
|
|
18
|
+
* - Should be combined with ID token verification using `checkRevoked=true` for immediate effect
|
|
19
|
+
* - Does not affect the user's account data or profile information
|
|
20
|
+
*
|
|
21
|
+
* **Use Cases:**
|
|
22
|
+
* - Security incident response (compromised accounts)
|
|
23
|
+
* - Forced logout from all devices
|
|
24
|
+
* - Administrative account suspension workflows
|
|
25
|
+
* - Password change security measures
|
|
26
|
+
* - Device management and session control
|
|
27
|
+
*
|
|
28
|
+
* @param uid - The Firebase Auth user ID (localId) whose refresh tokens should be revoked.
|
|
29
|
+
* Must be a valid, existing Firebase user identifier.
|
|
30
|
+
* @param oauth2AccessToken - Valid OAuth2 access token with Firebase Admin API privileges.
|
|
31
|
+
* Obtained via service account authentication.
|
|
32
|
+
*
|
|
33
|
+
* @returns Promise that resolves when the revocation is complete.
|
|
34
|
+
* No return value - success is indicated by promise resolution.
|
|
35
|
+
*
|
|
36
|
+
* @throws {Error} When revocation fails:
|
|
37
|
+
* - **Validation Errors**:
|
|
38
|
+
* - "uid must be a non-empty string" - Invalid or missing uid parameter
|
|
39
|
+
* - **Firebase API Errors**:
|
|
40
|
+
* - "Failed to revoke refresh tokens: {status} {statusText} - {details}" - API errors with detailed information
|
|
41
|
+
* - "USER_NOT_FOUND" - Specified user does not exist
|
|
42
|
+
* - "INVALID_ID_TOKEN" - OAuth2 token is invalid or expired
|
|
43
|
+
* - **Network Errors**: Various network-related failures during API communication
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Revoke all refresh tokens for a user (security incident)
|
|
48
|
+
* try {
|
|
49
|
+
* await revokeRefreshTokensHandler('user123', oauth2Token);
|
|
50
|
+
* console.log('All refresh tokens revoked successfully');
|
|
51
|
+
*
|
|
52
|
+
* // Verify that existing ID tokens are now invalid
|
|
53
|
+
* await verifyIdTokenHandler(existingIdToken, projectId, oauth2Token, kvNamespace, true);
|
|
54
|
+
* } catch (error) {
|
|
55
|
+
* if (error.message.includes('id-token-revoked')) {
|
|
56
|
+
* console.log('Tokens successfully revoked - existing ID token is invalid');
|
|
57
|
+
* } else {
|
|
58
|
+
* console.error('Failed to revoke tokens:', error);
|
|
59
|
+
* }
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Force logout from all devices after password change
|
|
66
|
+
* try {
|
|
67
|
+
* // First update the password
|
|
68
|
+
* await updateUserHandler('user456', { password: 'newSecurePassword' }, oauth2Token);
|
|
69
|
+
*
|
|
70
|
+
* // Then revoke all existing sessions for security
|
|
71
|
+
* await revokeRefreshTokensHandler('user456', oauth2Token);
|
|
72
|
+
*
|
|
73
|
+
* console.log('Password updated and all sessions revoked');
|
|
74
|
+
* } catch (error) {
|
|
75
|
+
* console.error('Security update failed:', error);
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // Administrative account suspension workflow
|
|
82
|
+
* try {
|
|
83
|
+
* // Disable the account
|
|
84
|
+
* await updateUserHandler('suspendedUser', { disabled: true }, oauth2Token);
|
|
85
|
+
*
|
|
86
|
+
* // Revoke all active sessions
|
|
87
|
+
* await revokeRefreshTokensHandler('suspendedUser', oauth2Token);
|
|
88
|
+
*
|
|
89
|
+
* console.log('Account suspended and all sessions terminated');
|
|
90
|
+
* } catch (error) {
|
|
91
|
+
* console.error('Account suspension failed:', error);
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* **Technical Implementation:**
|
|
96
|
+
* - Makes POST request to `https://identitytoolkit.googleapis.com/v1/accounts:update`
|
|
97
|
+
* - Sets `validSince` to current timestamp: `Math.floor(Date.now() / 1000)`
|
|
98
|
+
* - Uses `localId` parameter to identify the target user
|
|
99
|
+
* - Handles Firebase API error responses with detailed error information
|
|
100
|
+
* - Validates input parameters before making API calls
|
|
101
|
+
*
|
|
102
|
+
* **Testing Considerations:**
|
|
103
|
+
* - Can be verified by attempting to verify existing ID tokens with `checkRevoked=true`
|
|
104
|
+
* - Integration tests should create tokens, revoke them, then verify revocation
|
|
105
|
+
* - Unit tests should mock the fetch call and verify request format
|
|
106
|
+
* - Error scenarios should test various Firebase API error responses
|
|
107
|
+
*
|
|
108
|
+
* @see {@link verifyIdTokenHandler} For verifying ID tokens with revocation checking
|
|
109
|
+
* @see {@link updateUserHandler} For other user management operations
|
|
110
|
+
* @see {@link https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens Firebase Session Management}
|
|
111
|
+
* @see {@link https://firebase.google.com/docs/reference/rest/auth#section-update-account Firebase REST API}
|
|
112
|
+
*
|
|
113
|
+
* @package
|
|
114
|
+
* @since 1.0.0
|
|
115
|
+
*/
|
|
116
|
+
export declare function revokeRefreshTokensHandler(uid: string, oauth2AccessToken: string): Promise<void>;
|