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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +13 -0
  3. package/README.md +94 -0
  4. package/dist/CloudFireAuth.d.ts +291 -0
  5. package/dist/CloudFireAuth.js +346 -0
  6. package/dist/google-auth/get-oauth-2-token.d.ts +15 -0
  7. package/dist/google-auth/get-oauth-2-token.js +66 -0
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.js +2 -0
  10. package/dist/rest-api/create-user.d.ts +2 -0
  11. package/dist/rest-api/create-user.js +3 -0
  12. package/dist/rest-api/delete-user.d.ts +175 -0
  13. package/dist/rest-api/delete-user.js +207 -0
  14. package/dist/rest-api/delete-users.d.ts +2 -0
  15. package/dist/rest-api/delete-users.js +3 -0
  16. package/dist/rest-api/get-user-by-email.d.ts +2 -0
  17. package/dist/rest-api/get-user-by-email.js +3 -0
  18. package/dist/rest-api/get-user-by-phone-number.d.ts +2 -0
  19. package/dist/rest-api/get-user-by-phone-number.js +3 -0
  20. package/dist/rest-api/get-user-by-provider-uid.d.ts +2 -0
  21. package/dist/rest-api/get-user-by-provider-uid.js +3 -0
  22. package/dist/rest-api/get-user.d.ts +99 -0
  23. package/dist/rest-api/get-user.js +177 -0
  24. package/dist/rest-api/get-users.d.ts +2 -0
  25. package/dist/rest-api/get-users.js +3 -0
  26. package/dist/rest-api/list-users.d.ts +2 -0
  27. package/dist/rest-api/list-users.js +3 -0
  28. package/dist/rest-api/revoke-refresh-tokens.d.ts +116 -0
  29. package/dist/rest-api/revoke-refresh-tokens.js +151 -0
  30. package/dist/rest-api/set-custom-user-claims.d.ts +173 -0
  31. package/dist/rest-api/set-custom-user-claims.js +261 -0
  32. package/dist/rest-api/standard-request.d.ts +12 -0
  33. package/dist/rest-api/standard-request.js +20 -0
  34. package/dist/rest-api/update-user.d.ts +175 -0
  35. package/dist/rest-api/update-user.js +375 -0
  36. package/dist/rest-api/verify-id-token.d.ts +127 -0
  37. package/dist/rest-api/verify-id-token.js +273 -0
  38. package/dist/rest-api/verify-session-cookie.d.ts +2 -0
  39. package/dist/rest-api/verify-session-cookie.js +3 -0
  40. package/dist/types/firebase-admin/auth-config.d.ts +851 -0
  41. package/dist/types/firebase-admin/auth-config.js +1 -0
  42. package/dist/types/firebase-admin/identifier.d.ts +57 -0
  43. package/dist/types/firebase-admin/identifier.js +1 -0
  44. package/dist/types/firebase-admin/index.d.ts +153 -0
  45. package/dist/types/firebase-admin/index.js +1 -0
  46. package/dist/types/firebase-admin/token-verifier.d.ts +219 -0
  47. package/dist/types/firebase-admin/token-verifier.js +1 -0
  48. package/dist/types/firebase-admin/user-record.d.ts +289 -0
  49. package/dist/types/firebase-admin/user-record.js +1 -0
  50. package/dist/types/google-auth.d.ts +25 -0
  51. package/dist/types/google-auth.js +1 -0
  52. package/dist/types/service-account-key.d.ts +13 -0
  53. package/dist/types/service-account-key.js +1 -0
  54. package/dist/types.d.ts +6 -0
  55. package/dist/types.js +1 -0
  56. package/package.json +56 -0
  57. package/third_party/firebase-admin-license.txt +201 -0
@@ -0,0 +1,151 @@
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 async function revokeRefreshTokensHandler(uid, oauth2AccessToken) {
117
+ // Validate input parameters
118
+ if (typeof uid !== "string" || uid.length === 0) {
119
+ throw new Error("uid must be a non-empty string");
120
+ }
121
+ if (typeof oauth2AccessToken !== "string" || oauth2AccessToken.length === 0) {
122
+ throw new Error("oauth2AccessToken must be a non-empty string");
123
+ }
124
+ // Set validSince to current timestamp (in seconds)
125
+ const validSince = Math.floor(Date.now() / 1000).toString();
126
+ // Make the API call to revoke refresh tokens
127
+ const response = await fetch("https://identitytoolkit.googleapis.com/v1/accounts:update", {
128
+ method: "POST",
129
+ headers: {
130
+ "Content-Type": "application/json",
131
+ Authorization: `Bearer ${oauth2AccessToken}`,
132
+ },
133
+ body: JSON.stringify({
134
+ localId: uid,
135
+ validSince: validSince,
136
+ }),
137
+ });
138
+ if (!response.ok) {
139
+ const errorText = await response.text();
140
+ let errorMessage;
141
+ try {
142
+ const errorData = JSON.parse(errorText);
143
+ const formattedErrorText = JSON.stringify(errorData, null, 2);
144
+ errorMessage = `Failed to revoke refresh tokens: ${response.status} ${response.statusText}\n${formattedErrorText}`;
145
+ }
146
+ catch {
147
+ errorMessage = `Failed to revoke refresh tokens: ${response.status} ${response.statusText} - ${errorText}`;
148
+ }
149
+ throw new Error(errorMessage);
150
+ }
151
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Sets custom user claims for a Firebase Auth user.
3
+ *
4
+ * Custom claims are key-value pairs that provide additional information about a user
5
+ * that can be used for access control and authorization. These claims are included in
6
+ * the user's ID token and can be accessed by client applications.
7
+ *
8
+ * **Validation & Security:**
9
+ * - Validates claims against Firebase Auth and OIDC reserved names
10
+ * - Enforces 1000-byte size limit for serialized claims
11
+ * - Rejects invalid input types (arrays, primitives, etc.)
12
+ * - Prevents privilege escalation by blocking system claims
13
+ *
14
+ * **Firebase API Integration:**
15
+ * - Uses Firebase Admin API's `accounts:update` endpoint
16
+ * - Requires valid OAuth2 access token with admin privileges
17
+ * - Handles network errors and API response validation
18
+ * - Supports both setting new claims and clearing existing claims (null)
19
+ *
20
+ * **Common Use Cases:**
21
+ * - Role-based access control: `{ role: 'admin', department: 'IT' }`
22
+ * - Feature flags: `{ features: ['beta', 'premium'] }`
23
+ * - Organization membership: `{ orgId: 'acme-corp', permissions: ['read', 'write'] }`
24
+ * - Subscription levels: `{ plan: 'enterprise', expires: '2024-12-31' }`
25
+ *
26
+ * @param uid - The Firebase Auth user ID (localId) to set claims for.
27
+ * Must be a valid Firebase user identifier.
28
+ * @param customUserClaims - The custom claims object to set for the user.
29
+ * Pass `null` to clear all existing custom claims.
30
+ * Must comply with Firebase Auth restrictions.
31
+ * @param oauth2AccessToken - Valid OAuth2 access token with Firebase Admin API privileges.
32
+ * Obtained via service account authentication.
33
+ *
34
+ * @returns Promise that resolves when claims are successfully set.
35
+ *
36
+ * @throws {Error} When validation or API operations fail:
37
+ * - "Invalid custom user claims" - Claims validation failed (see checkClaimsAreValid)
38
+ * - "Failed to set custom user claims: {status} {statusText}, {body}" - Firebase API error
39
+ * - Network errors during Firebase API communication
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * // Set role-based claims
44
+ * await setCustomUserClaimsHandler(
45
+ * 'user123',
46
+ * { role: 'admin', department: 'engineering' },
47
+ * oauth2Token
48
+ * );
49
+ *
50
+ * // Set organization claims
51
+ * await setCustomUserClaimsHandler(
52
+ * 'user456',
53
+ * {
54
+ * organization: 'acme-corp',
55
+ * permissions: ['users:read', 'billing:write'],
56
+ * subscription: { plan: 'enterprise', expires: '2024-12-31' }
57
+ * },
58
+ * oauth2Token
59
+ * );
60
+ *
61
+ * // Clear all custom claims
62
+ * await setCustomUserClaimsHandler('user789', null, oauth2Token);
63
+ * ```
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // Error handling
68
+ * try {
69
+ * await setCustomUserClaimsHandler(userId, claims, oauth2Token);
70
+ * console.log('Claims set successfully');
71
+ * } catch (error) {
72
+ * if (error.message.includes('Reserved claim name')) {
73
+ * console.error('Cannot use reserved claim names');
74
+ * } else if (error.message.includes('too large')) {
75
+ * console.error('Claims exceed 1000 byte limit');
76
+ * } else if (error.message.includes('Failed to set custom user claims')) {
77
+ * console.error('Firebase API error:', error.message);
78
+ * } else {
79
+ * console.error('Unexpected error:', error);
80
+ * }
81
+ * }
82
+ * ```
83
+ *
84
+ * **Important Notes:**
85
+ * - Claims are included in new ID tokens issued after this call
86
+ * - Existing ID tokens retain their original claims until refresh
87
+ * - Client applications can access claims via `firebase.auth().currentUser.getIdTokenResult()`
88
+ * - Claims persist until explicitly changed or user is deleted
89
+ * - Maximum 1000 custom claims per user (Firebase limit)
90
+ *
91
+ * @package
92
+ * @internal Used by CloudFireAuth.setCustomUserClaims()
93
+ * @since 1.0.0
94
+ *
95
+ * @see {@link https://firebase.google.com/docs/auth/admin/custom-claims Firebase Custom Claims Documentation}
96
+ * @see {@link https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.auth.md#authsetcustomuserclaims Firebase Admin SDK Reference}
97
+ * @see {@link checkClaimsAreValid} For validation rules and restrictions
98
+ */
99
+ export declare function setCustomUserClaimsHandler(uid: string, customUserClaims: object | null, oauth2AccessToken: string): Promise<void>;
100
+ /**
101
+ * Validates custom user claims for Firebase Auth compliance.
102
+ *
103
+ * Performs comprehensive validation to ensure custom claims meet Firebase Auth requirements:
104
+ * - Rejects reserved Firebase Auth claim names (iss, aud, auth_time, user_id, firebase, etc.)
105
+ * - Rejects reserved OIDC standard claim names (email, name, picture, etc.)
106
+ * - Enforces the 1000-byte size limit for serialized claims
107
+ * - Handles null/undefined input gracefully
108
+ *
109
+ * Reserved Firebase Auth claims that cannot be overridden:
110
+ * - `iss`, `aud`, `auth_time`, `user_id`, `firebase`
111
+ * - `iat`, `exp`, `sub`, `uid`
112
+ *
113
+ * Reserved OIDC claims that cannot be overridden:
114
+ * - `email`, `email_verified`, `phone_number`, `phone_number_verified`
115
+ * - `name`, `given_name`, `family_name`, `middle_name`, `nickname`, `preferred_username`
116
+ * - `profile`, `picture`, `website`, `gender`, `birthdate`, `zoneinfo`, `locale`, `updated_at`
117
+ * - `azp`, `nonce`, `at_hash`, `c_hash`
118
+ *
119
+ * **Size Calculation:**
120
+ * Claims are serialized to JSON and must not exceed 1000 bytes when UTF-8 encoded.
121
+ * This includes all property names, values, and JSON formatting characters.
122
+ *
123
+ * @param customUserClaims - The custom user claims object to validate.
124
+ * Can be null to clear existing claims.
125
+ *
126
+ * @returns `true` if the claims are valid or null.
127
+ *
128
+ * @throws {Error} When claims violate Firebase Auth restrictions:
129
+ * - "Reserved claim name: {name} is not allowed in custom user claims" - Uses reserved Firebase/OIDC claim
130
+ * - "Custom user claims are too large. Must be less than 1000 bytes. Size: {size} bytes" - Exceeds size limit
131
+ * - "Failed to serialize custom user claims: {error}" - JSON serialization failed (circular refs, etc.)
132
+ * - Returns `false` for invalid input types (arrays, primitives, etc.)
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * // Valid claims
137
+ * checkClaimsAreValid({ role: 'admin', permissions: ['read', 'write'] }); // → true
138
+ * checkClaimsAreValid(null); // → true (clears claims)
139
+ * checkClaimsAreValid({}); // → true (empty claims)
140
+ *
141
+ * // Invalid input types return false
142
+ * checkClaimsAreValid([]); // → false (arrays not allowed)
143
+ * checkClaimsAreValid("string"); // → false (primitives not allowed)
144
+ *
145
+ * // Invalid claims throw errors
146
+ * try {
147
+ * checkClaimsAreValid({ email: 'test@example.com' });
148
+ * } catch (error) {
149
+ * // Error: Reserved claim name: email is not allowed in custom user claims
150
+ * }
151
+ *
152
+ * try {
153
+ * checkClaimsAreValid({ firebase: { tenant: 'abc' } });
154
+ * } catch (error) {
155
+ * // Error: Reserved claim name: firebase is not allowed in custom user claims
156
+ * }
157
+ *
158
+ * try {
159
+ * const largeClaims = { data: 'x'.repeat(1000) };
160
+ * checkClaimsAreValid(largeClaims);
161
+ * } catch (error) {
162
+ * // Error: Custom user claims are too large. Must be less than 1000 bytes. Size: 1012 bytes
163
+ * }
164
+ * ```
165
+ *
166
+ * @package
167
+ * @internal Used by setCustomUserClaims handler
168
+ * @since 1.0.0
169
+ *
170
+ * @see {@link https://firebase.google.com/docs/auth/admin/custom-claims Firebase Custom Claims}
171
+ * @see {@link https://openid.net/specs/openid-connect-core-1_0.html#IDToken OIDC ID Token Claims}
172
+ */
173
+ export declare function checkClaimsAreValid(customUserClaims: object | null): boolean;
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Sets custom user claims for a Firebase Auth user.
3
+ *
4
+ * Custom claims are key-value pairs that provide additional information about a user
5
+ * that can be used for access control and authorization. These claims are included in
6
+ * the user's ID token and can be accessed by client applications.
7
+ *
8
+ * **Validation & Security:**
9
+ * - Validates claims against Firebase Auth and OIDC reserved names
10
+ * - Enforces 1000-byte size limit for serialized claims
11
+ * - Rejects invalid input types (arrays, primitives, etc.)
12
+ * - Prevents privilege escalation by blocking system claims
13
+ *
14
+ * **Firebase API Integration:**
15
+ * - Uses Firebase Admin API's `accounts:update` endpoint
16
+ * - Requires valid OAuth2 access token with admin privileges
17
+ * - Handles network errors and API response validation
18
+ * - Supports both setting new claims and clearing existing claims (null)
19
+ *
20
+ * **Common Use Cases:**
21
+ * - Role-based access control: `{ role: 'admin', department: 'IT' }`
22
+ * - Feature flags: `{ features: ['beta', 'premium'] }`
23
+ * - Organization membership: `{ orgId: 'acme-corp', permissions: ['read', 'write'] }`
24
+ * - Subscription levels: `{ plan: 'enterprise', expires: '2024-12-31' }`
25
+ *
26
+ * @param uid - The Firebase Auth user ID (localId) to set claims for.
27
+ * Must be a valid Firebase user identifier.
28
+ * @param customUserClaims - The custom claims object to set for the user.
29
+ * Pass `null` to clear all existing custom claims.
30
+ * Must comply with Firebase Auth restrictions.
31
+ * @param oauth2AccessToken - Valid OAuth2 access token with Firebase Admin API privileges.
32
+ * Obtained via service account authentication.
33
+ *
34
+ * @returns Promise that resolves when claims are successfully set.
35
+ *
36
+ * @throws {Error} When validation or API operations fail:
37
+ * - "Invalid custom user claims" - Claims validation failed (see checkClaimsAreValid)
38
+ * - "Failed to set custom user claims: {status} {statusText}, {body}" - Firebase API error
39
+ * - Network errors during Firebase API communication
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * // Set role-based claims
44
+ * await setCustomUserClaimsHandler(
45
+ * 'user123',
46
+ * { role: 'admin', department: 'engineering' },
47
+ * oauth2Token
48
+ * );
49
+ *
50
+ * // Set organization claims
51
+ * await setCustomUserClaimsHandler(
52
+ * 'user456',
53
+ * {
54
+ * organization: 'acme-corp',
55
+ * permissions: ['users:read', 'billing:write'],
56
+ * subscription: { plan: 'enterprise', expires: '2024-12-31' }
57
+ * },
58
+ * oauth2Token
59
+ * );
60
+ *
61
+ * // Clear all custom claims
62
+ * await setCustomUserClaimsHandler('user789', null, oauth2Token);
63
+ * ```
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // Error handling
68
+ * try {
69
+ * await setCustomUserClaimsHandler(userId, claims, oauth2Token);
70
+ * console.log('Claims set successfully');
71
+ * } catch (error) {
72
+ * if (error.message.includes('Reserved claim name')) {
73
+ * console.error('Cannot use reserved claim names');
74
+ * } else if (error.message.includes('too large')) {
75
+ * console.error('Claims exceed 1000 byte limit');
76
+ * } else if (error.message.includes('Failed to set custom user claims')) {
77
+ * console.error('Firebase API error:', error.message);
78
+ * } else {
79
+ * console.error('Unexpected error:', error);
80
+ * }
81
+ * }
82
+ * ```
83
+ *
84
+ * **Important Notes:**
85
+ * - Claims are included in new ID tokens issued after this call
86
+ * - Existing ID tokens retain their original claims until refresh
87
+ * - Client applications can access claims via `firebase.auth().currentUser.getIdTokenResult()`
88
+ * - Claims persist until explicitly changed or user is deleted
89
+ * - Maximum 1000 custom claims per user (Firebase limit)
90
+ *
91
+ * @package
92
+ * @internal Used by CloudFireAuth.setCustomUserClaims()
93
+ * @since 1.0.0
94
+ *
95
+ * @see {@link https://firebase.google.com/docs/auth/admin/custom-claims Firebase Custom Claims Documentation}
96
+ * @see {@link https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.auth.md#authsetcustomuserclaims Firebase Admin SDK Reference}
97
+ * @see {@link checkClaimsAreValid} For validation rules and restrictions
98
+ */
99
+ export async function setCustomUserClaimsHandler(uid, customUserClaims, oauth2AccessToken) {
100
+ if (!checkClaimsAreValid(customUserClaims)) {
101
+ throw new Error("Invalid custom user claims", { cause: customUserClaims });
102
+ }
103
+ const body = JSON.stringify({
104
+ localId: uid,
105
+ customAttributes: JSON.stringify(customUserClaims ?? {}),
106
+ });
107
+ console.log("body", body);
108
+ const response = await fetch(`https://identitytoolkit.googleapis.com/v1/accounts:update`, {
109
+ method: "POST",
110
+ headers: {
111
+ "Content-Type": "application/json",
112
+ Authorization: `Bearer ${oauth2AccessToken}`,
113
+ },
114
+ body,
115
+ });
116
+ if (!response.ok) {
117
+ console.log("response", response);
118
+ throw new Error(`Failed to set custom user claims: ${response.status} ${response.statusText}, ${await response.text()}, response: ${response}`);
119
+ }
120
+ return;
121
+ }
122
+ /**
123
+ * Validates custom user claims for Firebase Auth compliance.
124
+ *
125
+ * Performs comprehensive validation to ensure custom claims meet Firebase Auth requirements:
126
+ * - Rejects reserved Firebase Auth claim names (iss, aud, auth_time, user_id, firebase, etc.)
127
+ * - Rejects reserved OIDC standard claim names (email, name, picture, etc.)
128
+ * - Enforces the 1000-byte size limit for serialized claims
129
+ * - Handles null/undefined input gracefully
130
+ *
131
+ * Reserved Firebase Auth claims that cannot be overridden:
132
+ * - `iss`, `aud`, `auth_time`, `user_id`, `firebase`
133
+ * - `iat`, `exp`, `sub`, `uid`
134
+ *
135
+ * Reserved OIDC claims that cannot be overridden:
136
+ * - `email`, `email_verified`, `phone_number`, `phone_number_verified`
137
+ * - `name`, `given_name`, `family_name`, `middle_name`, `nickname`, `preferred_username`
138
+ * - `profile`, `picture`, `website`, `gender`, `birthdate`, `zoneinfo`, `locale`, `updated_at`
139
+ * - `azp`, `nonce`, `at_hash`, `c_hash`
140
+ *
141
+ * **Size Calculation:**
142
+ * Claims are serialized to JSON and must not exceed 1000 bytes when UTF-8 encoded.
143
+ * This includes all property names, values, and JSON formatting characters.
144
+ *
145
+ * @param customUserClaims - The custom user claims object to validate.
146
+ * Can be null to clear existing claims.
147
+ *
148
+ * @returns `true` if the claims are valid or null.
149
+ *
150
+ * @throws {Error} When claims violate Firebase Auth restrictions:
151
+ * - "Reserved claim name: {name} is not allowed in custom user claims" - Uses reserved Firebase/OIDC claim
152
+ * - "Custom user claims are too large. Must be less than 1000 bytes. Size: {size} bytes" - Exceeds size limit
153
+ * - "Failed to serialize custom user claims: {error}" - JSON serialization failed (circular refs, etc.)
154
+ * - Returns `false` for invalid input types (arrays, primitives, etc.)
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * // Valid claims
159
+ * checkClaimsAreValid({ role: 'admin', permissions: ['read', 'write'] }); // → true
160
+ * checkClaimsAreValid(null); // → true (clears claims)
161
+ * checkClaimsAreValid({}); // → true (empty claims)
162
+ *
163
+ * // Invalid input types return false
164
+ * checkClaimsAreValid([]); // → false (arrays not allowed)
165
+ * checkClaimsAreValid("string"); // → false (primitives not allowed)
166
+ *
167
+ * // Invalid claims throw errors
168
+ * try {
169
+ * checkClaimsAreValid({ email: 'test@example.com' });
170
+ * } catch (error) {
171
+ * // Error: Reserved claim name: email is not allowed in custom user claims
172
+ * }
173
+ *
174
+ * try {
175
+ * checkClaimsAreValid({ firebase: { tenant: 'abc' } });
176
+ * } catch (error) {
177
+ * // Error: Reserved claim name: firebase is not allowed in custom user claims
178
+ * }
179
+ *
180
+ * try {
181
+ * const largeClaims = { data: 'x'.repeat(1000) };
182
+ * checkClaimsAreValid(largeClaims);
183
+ * } catch (error) {
184
+ * // Error: Custom user claims are too large. Must be less than 1000 bytes. Size: 1012 bytes
185
+ * }
186
+ * ```
187
+ *
188
+ * @package
189
+ * @internal Used by setCustomUserClaims handler
190
+ * @since 1.0.0
191
+ *
192
+ * @see {@link https://firebase.google.com/docs/auth/admin/custom-claims Firebase Custom Claims}
193
+ * @see {@link https://openid.net/specs/openid-connect-core-1_0.html#IDToken OIDC ID Token Claims}
194
+ */
195
+ export function checkClaimsAreValid(customUserClaims) {
196
+ // Null is valid (used to clear existing claims)
197
+ if (customUserClaims === null || customUserClaims === undefined) {
198
+ return true;
199
+ }
200
+ // Must be an object
201
+ if (typeof customUserClaims !== "object" || Array.isArray(customUserClaims)) {
202
+ return false;
203
+ }
204
+ const claims = customUserClaims;
205
+ // Reserved Firebase Auth claims
206
+ const firebaseReservedClaims = new Set([
207
+ "iss",
208
+ "aud",
209
+ "auth_time",
210
+ "user_id",
211
+ "firebase",
212
+ "iat",
213
+ "exp",
214
+ "sub",
215
+ "uid",
216
+ ]);
217
+ // Reserved OIDC standard claims
218
+ const oidcReservedClaims = new Set([
219
+ "email",
220
+ "email_verified",
221
+ "phone_number",
222
+ "phone_number_verified",
223
+ "name",
224
+ "given_name",
225
+ "family_name",
226
+ "middle_name",
227
+ "nickname",
228
+ "preferred_username",
229
+ "profile",
230
+ "picture",
231
+ "website",
232
+ "gender",
233
+ "birthdate",
234
+ "zoneinfo",
235
+ "locale",
236
+ "updated_at",
237
+ "azp",
238
+ "nonce",
239
+ "at_hash",
240
+ "c_hash",
241
+ ]);
242
+ // Check for reserved claim names
243
+ for (const key of Object.keys(claims)) {
244
+ if (firebaseReservedClaims.has(key) || oidcReservedClaims.has(key)) {
245
+ throw new Error(`Reserved claim name: ${key} is not allowed in custom user claims`);
246
+ }
247
+ }
248
+ // Check size limit (1000 bytes when JSON serialized)
249
+ try {
250
+ const serialized = JSON.stringify(claims);
251
+ const sizeInBytes = new TextEncoder().encode(serialized).length;
252
+ if (sizeInBytes > 1000) {
253
+ throw new Error(`Custom user claims are too large. Must be less than 1000 bytes. Size: ${sizeInBytes} bytes`);
254
+ }
255
+ }
256
+ catch (error) {
257
+ // JSON serialization failed (circular references, etc.)
258
+ throw new Error(`Failed to serialize custom user claims: ${error}`);
259
+ }
260
+ return true;
261
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Makes a standard request to the Firebase Auth REST API. AS all the
3
+ * requests use the same authentication method, this function is used to
4
+ * make the request.
5
+ *
6
+ * @param url - The URL to make the request to.
7
+ * @param method - The HTTP method to use.
8
+ * @param oauth2Token - The OAuth2 token to use for authentication.
9
+ * @param body - The body of the request.
10
+ * @returns The response from the request.
11
+ */
12
+ export declare function standardRequest(url: string, method: string, oauth2Token: string, body?: any): Promise<Response>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Makes a standard request to the Firebase Auth REST API. AS all the
3
+ * requests use the same authentication method, this function is used to
4
+ * make the request.
5
+ *
6
+ * @param url - The URL to make the request to.
7
+ * @param method - The HTTP method to use.
8
+ * @param oauth2Token - The OAuth2 token to use for authentication.
9
+ * @param body - The body of the request.
10
+ * @returns The response from the request.
11
+ */
12
+ export async function standardRequest(url, method, oauth2Token, body) {
13
+ return fetch(url, {
14
+ method,
15
+ body,
16
+ headers: {
17
+ Authorization: `Bearer ${oauth2Token}`,
18
+ },
19
+ });
20
+ }