@prmichaelsen/firebase-admin-sdk-v8 2.8.0 → 2.9.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/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.9.0] - 2026-03-19
9
+
10
+ ### Added
11
+ - **Password Reset Link Generation (`generatePasswordResetLink`)**: Generate password reset links via Identity Toolkit API
12
+ - `generatePasswordResetLink(email, actionCodeSettings?)` - Generate out-of-band password reset link
13
+ - Supports `ActionCodeSettings` for custom continue URLs, mobile deep linking (iOS/Android), and custom hosting domains
14
+ - Returns the OOB link for sending via custom email delivery
15
+ - 6 unit tests + 3 e2e tests
16
+
8
17
  ## [2.8.0] - 2026-03-12
9
18
 
10
19
  ### Added
package/dist/index.d.mts CHANGED
@@ -258,6 +258,36 @@ interface ListUsersResult {
258
258
  /** Token for fetching the next page (if available) */
259
259
  pageToken?: string;
260
260
  }
261
+ /**
262
+ * Settings for email action links (password reset, email verification, etc.)
263
+ */
264
+ interface ActionCodeSettings {
265
+ /** The continue/state URL to redirect to after the action is completed */
266
+ url: string;
267
+ /** Whether the action link should be opened in a mobile app or web */
268
+ handleCodeInApp?: boolean;
269
+ /** iOS-specific settings */
270
+ iOS?: {
271
+ /** The iOS bundle ID of the app to open the link in */
272
+ bundleId: string;
273
+ };
274
+ /** Android-specific settings */
275
+ android?: {
276
+ /** The Android package name of the app to open the link in */
277
+ packageName: string;
278
+ /** Whether to install the app if not already installed */
279
+ installApp?: boolean;
280
+ /** Minimum Android app version required */
281
+ minimumVersion?: string;
282
+ };
283
+ /**
284
+ * The domain to use for Dynamic Links
285
+ * @deprecated Use linkDomain instead
286
+ */
287
+ dynamicLinkDomain?: string;
288
+ /** Custom Firebase Hosting domain to use for the link */
289
+ linkDomain?: string;
290
+ }
261
291
 
262
292
  /**
263
293
  * Firebase Admin SDK v8 - Configuration
@@ -518,6 +548,26 @@ declare function listUsers(maxResults?: number, pageToken?: string): Promise<Lis
518
548
  * @param customClaims - Custom claims object (max 1000 bytes when serialized)
519
549
  */
520
550
  declare function setCustomUserClaims(uid: string, customClaims: Record<string, any> | null): Promise<void>;
551
+ /**
552
+ * Generate a password reset link for the given email address
553
+ *
554
+ * Returns an out-of-band (OOB) link that can be sent to the user
555
+ * via a custom email delivery mechanism.
556
+ *
557
+ * @param email - User's email address
558
+ * @param actionCodeSettings - Optional settings for the action code
559
+ * @returns Password reset link URL
560
+ *
561
+ * @example
562
+ * ```typescript
563
+ * const link = await generatePasswordResetLink('user@example.com', {
564
+ * url: 'https://example.com/login',
565
+ * });
566
+ * // Send the link via your own email service
567
+ * await sendEmail(email, `Reset your password: ${link}`);
568
+ * ```
569
+ */
570
+ declare function generatePasswordResetLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise<string>;
521
571
 
522
572
  /**
523
573
  * Firebase Admin SDK v8 - Firestore CRUD Operations
@@ -1286,4 +1336,4 @@ declare function getAdminAccessToken(): Promise<string>;
1286
1336
  */
1287
1337
  declare function clearTokenCache(): void;
1288
1338
 
1289
- export { type AndroidConfig, type AndroidNotification, type ApnsConfig, type BatchWrite, type BatchWriteResult, type CreateUserRequest, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, type Notification as FcmNotification, type FcmOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type ListUsersResult, type Message, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type SendResponse, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type TopicManagementError, type TopicManagementResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WebpushConfig, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAll, getAllByPaths, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, sendMessage, setCustomUserClaims, setDocument, signInWithCustomToken, subscribeToTopic, unsubscribeFromTopic, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
1339
+ export { type ActionCodeSettings, type AndroidConfig, type AndroidNotification, type ApnsConfig, type BatchWrite, type BatchWriteResult, type CreateUserRequest, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, type Notification as FcmNotification, type FcmOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type ListUsersResult, type Message, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type SendResponse, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type TopicManagementError, type TopicManagementResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WebpushConfig, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generatePasswordResetLink, generateSignedUrl, getAdminAccessToken, getAll, getAllByPaths, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, sendMessage, setCustomUserClaims, setDocument, signInWithCustomToken, subscribeToTopic, unsubscribeFromTopic, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
package/dist/index.d.ts CHANGED
@@ -258,6 +258,36 @@ interface ListUsersResult {
258
258
  /** Token for fetching the next page (if available) */
259
259
  pageToken?: string;
260
260
  }
261
+ /**
262
+ * Settings for email action links (password reset, email verification, etc.)
263
+ */
264
+ interface ActionCodeSettings {
265
+ /** The continue/state URL to redirect to after the action is completed */
266
+ url: string;
267
+ /** Whether the action link should be opened in a mobile app or web */
268
+ handleCodeInApp?: boolean;
269
+ /** iOS-specific settings */
270
+ iOS?: {
271
+ /** The iOS bundle ID of the app to open the link in */
272
+ bundleId: string;
273
+ };
274
+ /** Android-specific settings */
275
+ android?: {
276
+ /** The Android package name of the app to open the link in */
277
+ packageName: string;
278
+ /** Whether to install the app if not already installed */
279
+ installApp?: boolean;
280
+ /** Minimum Android app version required */
281
+ minimumVersion?: string;
282
+ };
283
+ /**
284
+ * The domain to use for Dynamic Links
285
+ * @deprecated Use linkDomain instead
286
+ */
287
+ dynamicLinkDomain?: string;
288
+ /** Custom Firebase Hosting domain to use for the link */
289
+ linkDomain?: string;
290
+ }
261
291
 
262
292
  /**
263
293
  * Firebase Admin SDK v8 - Configuration
@@ -518,6 +548,26 @@ declare function listUsers(maxResults?: number, pageToken?: string): Promise<Lis
518
548
  * @param customClaims - Custom claims object (max 1000 bytes when serialized)
519
549
  */
520
550
  declare function setCustomUserClaims(uid: string, customClaims: Record<string, any> | null): Promise<void>;
551
+ /**
552
+ * Generate a password reset link for the given email address
553
+ *
554
+ * Returns an out-of-band (OOB) link that can be sent to the user
555
+ * via a custom email delivery mechanism.
556
+ *
557
+ * @param email - User's email address
558
+ * @param actionCodeSettings - Optional settings for the action code
559
+ * @returns Password reset link URL
560
+ *
561
+ * @example
562
+ * ```typescript
563
+ * const link = await generatePasswordResetLink('user@example.com', {
564
+ * url: 'https://example.com/login',
565
+ * });
566
+ * // Send the link via your own email service
567
+ * await sendEmail(email, `Reset your password: ${link}`);
568
+ * ```
569
+ */
570
+ declare function generatePasswordResetLink(email: string, actionCodeSettings?: ActionCodeSettings): Promise<string>;
521
571
 
522
572
  /**
523
573
  * Firebase Admin SDK v8 - Firestore CRUD Operations
@@ -1286,4 +1336,4 @@ declare function getAdminAccessToken(): Promise<string>;
1286
1336
  */
1287
1337
  declare function clearTokenCache(): void;
1288
1338
 
1289
- export { type AndroidConfig, type AndroidNotification, type ApnsConfig, type BatchWrite, type BatchWriteResult, type CreateUserRequest, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, type Notification as FcmNotification, type FcmOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type ListUsersResult, type Message, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type SendResponse, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type TopicManagementError, type TopicManagementResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WebpushConfig, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generateSignedUrl, getAdminAccessToken, getAll, getAllByPaths, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, sendMessage, setCustomUserClaims, setDocument, signInWithCustomToken, subscribeToTopic, unsubscribeFromTopic, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
1339
+ export { type ActionCodeSettings, type AndroidConfig, type AndroidNotification, type ApnsConfig, type BatchWrite, type BatchWriteResult, type CreateUserRequest, type CustomClaims, type CustomTokenSignInResponse, type DataObject, type DecodedIdToken, type DocumentReference, type DownloadOptions, type Notification as FcmNotification, type FcmOptions, FieldValue, type FieldValue$1 as FieldValueSentinel, FieldValueType, type FileMetadata, type FirestoreDocument, type FirestoreValue, type ListFilesResult, type ListOptions, type ListUsersResult, type Message, type QueryFilter, type QueryOptions, type QueryOrder, type ResumableUploadOptions, type SendResponse, type ServiceAccount, type SessionCookieOptions, type SetOptions, type SignedUrlOptions, type TokenResponse, type TopicManagementError, type TopicManagementResponse, type UpdateOptions, type UpdateUserRequest, type UploadOptions, type UserInfo, type UserRecord, type WebpushConfig, type WhereFilterOp, addDocument, batchWrite, clearConfig, clearTokenCache, countDocuments, createCustomToken, createSessionCookie, createUser, deleteDocument, deleteFile, deleteUser, downloadFile, fileExists, generatePasswordResetLink, generateSignedUrl, getAdminAccessToken, getAll, getAllByPaths, getAuth, getConfig, getDocument, getFileMetadata, getProjectId, getServiceAccount, getUserByEmail, getUserByUid, getUserFromToken, initializeApp, iterateCollection, listDocuments, listFiles, listUsers, queryDocuments, sendMessage, setCustomUserClaims, setDocument, signInWithCustomToken, subscribeToTopic, unsubscribeFromTopic, updateDocument, updateUser, uploadFile, uploadFileResumable, verifyIdToken, verifySessionCookie };
package/dist/index.js CHANGED
@@ -214,6 +214,7 @@ __export(index_exports, {
214
214
  deleteUser: () => deleteUser,
215
215
  downloadFile: () => downloadFile,
216
216
  fileExists: () => fileExists,
217
+ generatePasswordResetLink: () => generatePasswordResetLink,
217
218
  generateSignedUrl: () => generateSignedUrl,
218
219
  getAdminAccessToken: () => getAdminAccessToken,
219
220
  getAll: () => getAll,
@@ -954,6 +955,67 @@ async function setCustomUserClaims(uid, customClaims) {
954
955
  throw new Error(`Failed to set custom claims: ${response.status} ${errorText}`);
955
956
  }
956
957
  }
958
+ function buildActionCodeSettingsRequest(settings) {
959
+ const request = {};
960
+ request.continueUrl = settings.url;
961
+ if (typeof settings.handleCodeInApp === "boolean") {
962
+ request.canHandleCodeInApp = settings.handleCodeInApp;
963
+ }
964
+ if (settings.iOS?.bundleId) {
965
+ request.iOSBundleId = settings.iOS.bundleId;
966
+ }
967
+ if (settings.android) {
968
+ request.androidPackageName = settings.android.packageName;
969
+ if (typeof settings.android.installApp === "boolean") {
970
+ request.androidInstallApp = settings.android.installApp;
971
+ }
972
+ if (settings.android.minimumVersion) {
973
+ request.androidMinimumVersion = settings.android.minimumVersion;
974
+ }
975
+ }
976
+ if (settings.dynamicLinkDomain) {
977
+ request.dynamicLinkDomain = settings.dynamicLinkDomain;
978
+ }
979
+ if (settings.linkDomain) {
980
+ request.linkDomain = settings.linkDomain;
981
+ }
982
+ return request;
983
+ }
984
+ async function generatePasswordResetLink(email, actionCodeSettings) {
985
+ if (!email || typeof email !== "string") {
986
+ throw new Error("email must be a non-empty string");
987
+ }
988
+ const projectId = getProjectId();
989
+ const accessToken = await getAdminAccessToken();
990
+ const requestBody = {
991
+ requestType: "PASSWORD_RESET",
992
+ email,
993
+ returnOobLink: true
994
+ };
995
+ if (actionCodeSettings) {
996
+ Object.assign(requestBody, buildActionCodeSettingsRequest(actionCodeSettings));
997
+ }
998
+ const response = await fetch(
999
+ `${IDENTITY_TOOLKIT_API}/projects/${projectId}/accounts:sendOobCode`,
1000
+ {
1001
+ method: "POST",
1002
+ headers: {
1003
+ "Authorization": `Bearer ${accessToken}`,
1004
+ "Content-Type": "application/json"
1005
+ },
1006
+ body: JSON.stringify(requestBody)
1007
+ }
1008
+ );
1009
+ if (!response.ok) {
1010
+ const errorText = await response.text();
1011
+ throw new Error(`Failed to generate password reset link: ${response.status} ${errorText}`);
1012
+ }
1013
+ const data = await response.json();
1014
+ if (!data.oobLink) {
1015
+ throw new Error("Password reset link not returned by server");
1016
+ }
1017
+ return data.oobLink;
1018
+ }
957
1019
 
958
1020
  // src/field-value.ts
959
1021
  function serverTimestamp() {
@@ -2332,6 +2394,7 @@ init_service_account();
2332
2394
  deleteUser,
2333
2395
  downloadFile,
2334
2396
  fileExists,
2397
+ generatePasswordResetLink,
2335
2398
  generateSignedUrl,
2336
2399
  getAdminAccessToken,
2337
2400
  getAll,
package/dist/index.mjs CHANGED
@@ -708,6 +708,67 @@ async function setCustomUserClaims(uid, customClaims) {
708
708
  throw new Error(`Failed to set custom claims: ${response.status} ${errorText}`);
709
709
  }
710
710
  }
711
+ function buildActionCodeSettingsRequest(settings) {
712
+ const request = {};
713
+ request.continueUrl = settings.url;
714
+ if (typeof settings.handleCodeInApp === "boolean") {
715
+ request.canHandleCodeInApp = settings.handleCodeInApp;
716
+ }
717
+ if (settings.iOS?.bundleId) {
718
+ request.iOSBundleId = settings.iOS.bundleId;
719
+ }
720
+ if (settings.android) {
721
+ request.androidPackageName = settings.android.packageName;
722
+ if (typeof settings.android.installApp === "boolean") {
723
+ request.androidInstallApp = settings.android.installApp;
724
+ }
725
+ if (settings.android.minimumVersion) {
726
+ request.androidMinimumVersion = settings.android.minimumVersion;
727
+ }
728
+ }
729
+ if (settings.dynamicLinkDomain) {
730
+ request.dynamicLinkDomain = settings.dynamicLinkDomain;
731
+ }
732
+ if (settings.linkDomain) {
733
+ request.linkDomain = settings.linkDomain;
734
+ }
735
+ return request;
736
+ }
737
+ async function generatePasswordResetLink(email, actionCodeSettings) {
738
+ if (!email || typeof email !== "string") {
739
+ throw new Error("email must be a non-empty string");
740
+ }
741
+ const projectId = getProjectId();
742
+ const accessToken = await getAdminAccessToken();
743
+ const requestBody = {
744
+ requestType: "PASSWORD_RESET",
745
+ email,
746
+ returnOobLink: true
747
+ };
748
+ if (actionCodeSettings) {
749
+ Object.assign(requestBody, buildActionCodeSettingsRequest(actionCodeSettings));
750
+ }
751
+ const response = await fetch(
752
+ `${IDENTITY_TOOLKIT_API}/projects/${projectId}/accounts:sendOobCode`,
753
+ {
754
+ method: "POST",
755
+ headers: {
756
+ "Authorization": `Bearer ${accessToken}`,
757
+ "Content-Type": "application/json"
758
+ },
759
+ body: JSON.stringify(requestBody)
760
+ }
761
+ );
762
+ if (!response.ok) {
763
+ const errorText = await response.text();
764
+ throw new Error(`Failed to generate password reset link: ${response.status} ${errorText}`);
765
+ }
766
+ const data = await response.json();
767
+ if (!data.oobLink) {
768
+ throw new Error("Password reset link not returned by server");
769
+ }
770
+ return data.oobLink;
771
+ }
711
772
 
712
773
  // src/field-value.ts
713
774
  function serverTimestamp() {
@@ -2070,6 +2131,7 @@ export {
2070
2131
  deleteUser,
2071
2132
  downloadFile,
2072
2133
  fileExists,
2134
+ generatePasswordResetLink,
2073
2135
  generateSignedUrl,
2074
2136
  getAdminAccessToken,
2075
2137
  getAll,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/firebase-admin-sdk-v8",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
4
  "description": "Firebase Admin SDK for Cloudflare Workers and edge runtimes using REST APIs",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",