diff --git a/.changeset/chilled-pumpkins-kick.md b/.changeset/chilled-pumpkins-kick.md new file mode 100644 index 00000000..453b262b --- /dev/null +++ b/.changeset/chilled-pumpkins-kick.md @@ -0,0 +1,5 @@ +--- +'@capacitor-firebase/authentication': minor +--- + +feat: add `idTokenChange` event listener diff --git a/packages/authentication/README.md b/packages/authentication/README.md index a8ab284b..407621d1 100644 --- a/packages/authentication/README.md +++ b/packages/authentication/README.md @@ -479,12 +479,13 @@ const verifyBeforeUpdateEmail = async () => { * [`signOut()`](#signout) * [`unlink(...)`](#unlink) * [`updateEmail(...)`](#updateemail) -* [`verifyBeforeUpdateEmail(...)`](#verifyBeforeUpdateEmail) * [`updatePassword(...)`](#updatepassword) * [`updateProfile(...)`](#updateprofile) * [`useAppLanguage()`](#useapplanguage) * [`useEmulator(...)`](#useemulator) +* [`verifyBeforeUpdateEmail(...)`](#verifybeforeupdateemail) * [`addListener('authStateChange', ...)`](#addlistenerauthstatechange) +* [`addListener('idTokenChange', ...)`](#addlisteneridtokenchange) * [`addListener('phoneVerificationCompleted', ...)`](#addlistenerphoneverificationcompleted) * [`addListener('phoneVerificationFailed', ...)`](#addlistenerphoneverificationfailed) * [`addListener('phoneCodeSent', ...)`](#addlistenerphonecodesent) @@ -1473,22 +1474,6 @@ Updates the email address of the currently signed in user. -------------------- -### verifyBeforeUpdateEmail(...) - -```typescript -verifyBeforeUpdateEmail(options: VerifyBeforeUpdateEmailOptions) => Promise -``` - -Verifies the email before updating the email address of the currently signed in user. - -| Param | Type | -| ------------- | ----------------------------------------------------------------- | -| **`options`** | VerifyBeforeUpdateEmailOptions | - -**Since:** 6.2.0 - --------------------- - ### updatePassword(...) @@ -1554,6 +1539,23 @@ Instrument your app to talk to the Authentication emulator. -------------------- +### verifyBeforeUpdateEmail(...) + +```typescript +verifyBeforeUpdateEmail(options: VerifyBeforeUpdateEmailOptions) => Promise +``` + +Verifies the new email address before updating the email address of the currently signed in user. + +| Param | Type | +| ------------- | ----------------------------------------------------------------------------------------- | +| **`options`** | VerifyBeforeUpdateEmailOptions | + +**Since:** 6.3.0 + +-------------------- + + ### addListener('authStateChange', ...) ```typescript @@ -1576,6 +1578,28 @@ Listen for the user's sign-in state changes. -------------------- +### addListener('idTokenChange', ...) + +```typescript +addListener(eventName: 'idTokenChange', listenerFunc: IdTokenChangeListener) => Promise +``` + +Listen to ID token changes for the currently signed-in user. + +**Attention:** This listener is not triggered when the `skipNativeAuth` is used. Use the Firebase JavaScript SDK instead. + +| Param | Type | +| ------------------ | ----------------------------------------------------------------------- | +| **`eventName`** | 'idTokenChange' | +| **`listenerFunc`** | IdTokenChangeListener | + +**Returns:** Promise<PluginListenerHandle> + +**Since:** 6.3.0 + +-------------------- + + ### addListener('phoneVerificationCompleted', ...) ```typescript @@ -1992,14 +2016,6 @@ An interface covering the possible persistence mechanism types. | **`newEmail`** | string | The new email address. | 0.2.2 | -#### VerifyBeforeUpdateEmailOptions - -| Prop | Type | Description | Since | -| -------------- | ------------------- | ---------------------- | ----- | -| **`newEmail`** | string | The new email address to be verified before update. | 0.2.2 | -| **`actionCodeSettings`** | ActionCodeSettings | Structure that contains the required continue/state URL with optional Android and iOS bundle identifiers. | 1.1.0 | - - #### UpdatePasswordOptions | Prop | Type | Description | Since | @@ -2024,6 +2040,14 @@ An interface covering the possible persistence mechanism types. | **`scheme`** | string | The emulator scheme. Only available for Web. | "http" | 5.2.0 | +#### VerifyBeforeUpdateEmailOptions + +| Prop | Type | Description | Since | +| ------------------------ | ----------------------------------------------------------------- | --------------------------------------------------- | ----- | +| **`newEmail`** | string | The new email address to be verified before update. | 6.3.0 | +| **`actionCodeSettings`** | ActionCodeSettings | The action code settings | 6.3.0 | + + #### PluginListenerHandle | Prop | Type | @@ -2089,6 +2113,13 @@ Callback to receive the user's sign-in state change notifications. (change: AuthStateChange): void +#### IdTokenChangeListener + +Callback to receive the ID token change notifications. + +(change: GetIdTokenResult): void + + #### PhoneVerificationCompletedListener Callback to receive the verification code sent to the user's phone number. diff --git a/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthentication.java b/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthentication.java index adea0c7d..a6437ae6 100644 --- a/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthentication.java +++ b/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthentication.java @@ -54,6 +54,7 @@ public class FirebaseAuthentication { private FirebaseAuthenticationPlugin plugin; private FirebaseAuthenticationConfig config; private FirebaseAuth.AuthStateListener firebaseAuthStateListener; + private FirebaseAuth.IdTokenListener firebaseIdTokenChangeListener; private AppleAuthProviderHandler appleAuthProviderHandler; private FacebookAuthProviderHandler facebookAuthProviderHandler; private GoogleAuthProviderHandler googleAuthProviderHandler; @@ -70,6 +71,11 @@ public FirebaseAuthentication(FirebaseAuthenticationPlugin plugin, FirebaseAuthe this.plugin.handleAuthStateChange(); }; getFirebaseAuthInstance().addAuthStateListener(this.firebaseAuthStateListener); + this.firebaseIdTokenChangeListener = + firebaseAuth -> { + this.plugin.handleIdTokenChange(); + }; + getFirebaseAuthInstance().addIdTokenListener(this.firebaseIdTokenChangeListener); } public void applyActionCode(@NonNull String oobCode, @NonNull Runnable callback) { diff --git a/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthenticationPlugin.java b/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthenticationPlugin.java index 6287df73..d13ab6ed 100644 --- a/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthenticationPlugin.java +++ b/packages/authentication/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/authentication/FirebaseAuthenticationPlugin.java @@ -62,6 +62,7 @@ public class FirebaseAuthenticationPlugin extends Plugin { public static final String ERROR_SIGN_IN_ANONYMOUSLY_SKIP_NATIVE_AUTH = "signInAnonymously cannot be used in combination with skipNativeAuth."; public static final String AUTH_STATE_CHANGE_EVENT = "authStateChange"; + public static final String ID_TOKEN_CHANGE_EVENT = "idTokenChange"; private FirebaseAuthenticationConfig config; private FirebaseAuthentication implementation; @@ -984,6 +985,21 @@ public void handleAuthStateChange() { notifyListeners(AUTH_STATE_CHANGE_EVENT, result, true); } + public void handleIdTokenChange() { + NonEmptyResultCallback callback = new NonEmptyResultCallback() { + @Override + public void success(Result result) { + notifyListeners(ID_TOKEN_CHANGE_EVENT, result.toJSObject(), true); + } + + @Override + public void error(Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + } + }; + implementation.getIdToken(false, callback); + } + @Override protected void handleOnActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.handleOnActivityResult(requestCode, resultCode, data); diff --git a/packages/authentication/ios/Plugin.xcodeproj/project.pbxproj b/packages/authentication/ios/Plugin.xcodeproj/project.pbxproj index af148aa7..88edbe5e 100644 --- a/packages/authentication/ios/Plugin.xcodeproj/project.pbxproj +++ b/packages/authentication/ios/Plugin.xcodeproj/project.pbxproj @@ -379,13 +379,14 @@ "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework", "${BUILT_PRODUCTS_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework", "${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseAuthInterop/FirebaseAuthInterop.framework", "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework", "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", "${BUILT_PRODUCTS_DIR}/GTMAppAuth/GTMAppAuth.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", "${BUILT_PRODUCTS_DIR}/GoogleSignIn/GoogleSignIn.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", "${BUILT_PRODUCTS_DIR}/RecaptchaInterop/RecaptchaInterop.framework", "${PODS_XCFRAMEWORKS_BUILD_DIR}/FBAEMKit/FBAEMKit.framework/FBAEMKit", "${PODS_XCFRAMEWORKS_BUILD_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework/FBSDKCoreKit", @@ -399,13 +400,14 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAppCheckInterop.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuthInterop.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreExtension.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMAppAuth.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleSignIn.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RecaptchaInterop.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBAEMKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework", diff --git a/packages/authentication/ios/Plugin/FirebaseAuthentication.swift b/packages/authentication/ios/Plugin/FirebaseAuthentication.swift index fd4b08b8..e361f523 100644 --- a/packages/authentication/ios/Plugin/FirebaseAuthentication.swift +++ b/packages/authentication/ios/Plugin/FirebaseAuthentication.swift @@ -28,6 +28,9 @@ public typealias AuthStateChangedObserver = () -> Void Auth.auth().addStateDidChangeListener {_, _ in self.plugin.handleAuthStateChange() } + _ = Auth.auth().addIDTokenDidChangeListener {_, _ in + self.plugin.handleIdTokenChange() + } } @objc func applyActionCode(oobCode: String, completion: @escaping (Error?) -> Void) { diff --git a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift index 7ebd3578..382ea80f 100644 --- a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift +++ b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift @@ -36,6 +36,7 @@ public class FirebaseAuthenticationPlugin: CAPPlugin { "signInAnonymously cannot be used in combination with skipNativeAuth." public let errorDeviceUnsupported = "Device is not supported. At least iOS 13 is required." public let authStateChangeEvent = "authStateChange" + public let idTokenChangeEvent = "idTokenChange" public let phoneVerificationFailedEvent = "phoneVerificationFailed" public let phoneCodeSentEvent = "phoneCodeSent" private var implementation: FirebaseAuthentication? @@ -508,7 +509,7 @@ public class FirebaseAuthenticationPlugin: CAPPlugin { call.reject(errorNewEmailMissing) return } - + guard let actionCodeSettingsDict = call.getObject("actionCodeSettings") else { call.reject(errorActionCodeSettingsMissing) return @@ -594,6 +595,18 @@ public class FirebaseAuthenticationPlugin: CAPPlugin { notifyListeners(authStateChangeEvent, data: result, retainUntilConsumed: true) } + @objc func handleIdTokenChange() { + implementation?.getIdToken(false, completion: { result, error in + if let error = error { + CAPLog.print("[", self.tag, "] ", error) + return + } + if let result = result { + self.notifyListeners(self.idTokenChangeEvent, data: result.toJSObject(), retainUntilConsumed: true) + } + }) + } + @objc func handlePhoneVerificationFailed(_ error: Error) { CAPLog.print("[", self.tag, "] ", error) var result = JSObject() diff --git a/packages/authentication/src/definitions.ts b/packages/authentication/src/definitions.ts index fcd993b3..078bd701 100644 --- a/packages/authentication/src/definitions.ts +++ b/packages/authentication/src/definitions.ts @@ -483,6 +483,17 @@ export interface FirebaseAuthenticationPlugin { eventName: 'authStateChange', listenerFunc: AuthStateChangeListener, ): Promise; + /** + * Listen to ID token changes for the currently signed-in user. + * + * **Attention:** This listener is not triggered when the `skipNativeAuth` is used. Use the Firebase JavaScript SDK instead. + * + * @since 6.3.0 + */ + addListener( + eventName: 'idTokenChange', + listenerFunc: IdTokenChangeListener, + ): Promise; /** * Listen for a completed phone verification. * @@ -1409,6 +1420,13 @@ export interface AdditionalUserInfo { */ export type AuthStateChangeListener = (change: AuthStateChange) => void; +/** + * Callback to receive the ID token change notifications. + * + * @since 6.3.0 + */ +export type IdTokenChangeListener = (change: GetIdTokenResult) => void; + /** * @since 0.1.0 */ diff --git a/packages/authentication/src/web.ts b/packages/authentication/src/web.ts index da0f1198..00da8f4e 100644 --- a/packages/authentication/src/web.ts +++ b/packages/authentication/src/web.ts @@ -112,6 +112,7 @@ export class FirebaseAuthenticationWeb implements FirebaseAuthenticationPlugin { public static readonly AUTH_STATE_CHANGE_EVENT = 'authStateChange'; + public static readonly ID_TOKEN_CHANGE_EVENT = 'idTokenChange'; public static readonly PHONE_CODE_SENT_EVENT = 'phoneCodeSent'; public static readonly PHONE_VERIFICATION_FAILED_EVENT = 'phoneVerificationFailed'; @@ -129,6 +130,7 @@ export class FirebaseAuthenticationWeb super(); const auth = getAuth(); auth.onAuthStateChanged(user => this.handleAuthStateChange(user)); + auth.onIdTokenChanged(user => void this.handleIdTokenChange(user)); } public async applyActionCode(options: ApplyActionCodeOptions): Promise { @@ -790,6 +792,21 @@ export class FirebaseAuthenticationWeb ); } + private async handleIdTokenChange(user: FirebaseUser | null): Promise { + if (!user) { + return; + } + const idToken = await user.getIdToken(false); + const result: GetIdTokenResult = { + token: idToken, + }; + this.notifyListeners( + FirebaseAuthenticationWeb.ID_TOKEN_CHANGE_EVENT, + result, + true, + ); + } + private applySignInOptions( options: SignInWithOAuthOptions, provider: OAuthProvider | GoogleAuthProvider | FacebookAuthProvider,