From 7af20327a4096215210e197f91dd6ab018b34b10 Mon Sep 17 00:00:00 2001 From: EwwwGiddings Date: Wed, 23 Oct 2024 11:20:28 -0400 Subject: [PATCH] feat: add `verifyBeforeUpdateEmail` method (#741) * Added verifyBeforeUpdateEmail method to web, iOS, Android. Updated documents to include the new method. * Updated README from wrong version to latest. * Handle success and failure callbacks for Java. Alphabetize iOS method ordering. Update version number. * Updated README definition for newEmail and added example. * Added result callback to method that handles errors and success * Fix prettier issues * EmptyResultCallback change * Added changeset * Update packages/authentication/src/definitions.ts --------- Co-authored-by: Robin Genz --- .changeset/tall-eagles-learn.md | 5 ++ packages/authentication/README.md | 47 +++++++++++++++++++ .../FirebaseAuthentication.java | 12 +++++ .../FirebaseAuthenticationPlugin.java | 44 +++++++++++++++++ .../ios/Plugin/FirebaseAuthentication.swift | 13 +++++ .../ios/Plugin/FirebaseAuthenticationPlugin.m | 1 + .../Plugin/FirebaseAuthenticationPlugin.swift | 26 ++++++++++ packages/authentication/src/definitions.ts | 26 ++++++++++ packages/authentication/src/web.ts | 17 +++++++ 9 files changed, 191 insertions(+) create mode 100644 .changeset/tall-eagles-learn.md diff --git a/.changeset/tall-eagles-learn.md b/.changeset/tall-eagles-learn.md new file mode 100644 index 00000000..cf4d2057 --- /dev/null +++ b/.changeset/tall-eagles-learn.md @@ -0,0 +1,5 @@ +--- +'@capacitor-firebase/authentication': minor +--- + +feat: add `verifyBeforeUpdateEmail` method diff --git a/packages/authentication/README.md b/packages/authentication/README.md index 255b5c28..a8ab284b 100644 --- a/packages/authentication/README.md +++ b/packages/authentication/README.md @@ -400,6 +400,28 @@ const useEmulator = async () => { port: 9099, }); }; + +const verifyBeforeUpdateEmail = async () => { + const currentUser = await getCurrentUser(); + if (!currentUser) { + return; + } + await FirebaseAuthentication.verifyBeforeUpdateEmail({ + newEmail: 'new.mail@example.com', + actionCodeSettings: { + url: 'https://www.example.com/cart?email=user@example.com&cartId=123', + iOS: { + bundleId: 'com.example.ios' + }, + android: { + packageName: 'com.example.android', + installApp: true, + minimumVersion: '12' + }, + handleCodeInApp: true + } + }); +}; ``` ## API @@ -457,6 +479,7 @@ const useEmulator = async () => { * [`signOut()`](#signout) * [`unlink(...)`](#unlink) * [`updateEmail(...)`](#updateemail) +* [`verifyBeforeUpdateEmail(...)`](#verifyBeforeUpdateEmail) * [`updatePassword(...)`](#updatepassword) * [`updateProfile(...)`](#updateprofile) * [`useAppLanguage()`](#useapplanguage) @@ -1450,6 +1473,22 @@ 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(...) @@ -1953,6 +1992,14 @@ 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 | 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 6a00af5e..adea0c7d 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 @@ -587,6 +587,18 @@ public void updateEmail(FirebaseUser user, @NonNull String newEmail, @NonNull Ru ); } + public void verifyBeforeUpdateEmail( + FirebaseUser user, + @NonNull String newEmail, + @NonNull ActionCodeSettings actionCodeSettings, + @NonNull EmptyResultCallback callback + ) { + user + .verifyBeforeUpdateEmail(newEmail, actionCodeSettings) + .addOnSuccessListener(unused -> callback.success()) + .addOnFailureListener(exception -> callback.error(exception)); + } + public void updatePassword(FirebaseUser user, @NonNull String newPassword, @NonNull Runnable callback) { user .updatePassword(newPassword) 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 d0eaf120..6287df73 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 @@ -840,6 +840,50 @@ public void updateEmail(PluginCall call) { } } + @PluginMethod + public void verifyBeforeUpdateEmail(PluginCall call) { + try { + String newEmail = call.getString("newEmail"); + if (newEmail == null) { + call.reject(ERROR_NEW_EMAIL_MISSING); + return; + } + + FirebaseUser user = implementation.getCurrentUser(); + if (user == null) { + call.reject(ERROR_NO_USER_SIGNED_IN); + return; + } + + JSObject settings = call.getObject("actionCodeSettings"); + if (settings == null) { + call.reject(ERROR_ACTION_CODE_SETTINGS_MISSING); + return; + } + ActionCodeSettings actionCodeSettings = FirebaseAuthenticationHelper.createActionCodeSettings(settings); + + EmptyResultCallback callback = new EmptyResultCallback() { + @Override + public void success() { + call.resolve(); + } + + @Override + public void error(Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + String code = FirebaseAuthenticationHelper.createErrorCode(exception); + call.reject(exception.getMessage(), code); + } + }; + + implementation.verifyBeforeUpdateEmail(user, newEmail, actionCodeSettings, callback); + } catch (Exception exception) { + Logger.error(TAG, exception.getMessage(), exception); + String code = FirebaseAuthenticationHelper.createErrorCode(exception); + call.reject(exception.getMessage(), code); + } + } + @PluginMethod public void updatePassword(PluginCall call) { try { diff --git a/packages/authentication/ios/Plugin/FirebaseAuthentication.swift b/packages/authentication/ios/Plugin/FirebaseAuthentication.swift index dddc4ebc..fd4b08b8 100644 --- a/packages/authentication/ios/Plugin/FirebaseAuthentication.swift +++ b/packages/authentication/ios/Plugin/FirebaseAuthentication.swift @@ -266,6 +266,19 @@ public typealias AuthStateChangedObserver = () -> Void } } + @objc func verifyBeforeUpdateEmail(_ newEmail: String, actionCodeSettings: ActionCodeSettings, completion: @escaping (Error?) -> Void) { + guard let user = self.getCurrentUser() else { + completion(RuntimeError(plugin.errorNoUserSignedIn)) + return + } + + let completion: (Error?) -> Void = { error in + completion(error) + } + + user.sendEmailVerification(beforeUpdatingEmail: newEmail, actionCodeSettings: actionCodeSettings, completion: completion) + } + @objc func sendPasswordResetEmail(_ options: SendPasswordResetEmailOptions, completion: @escaping (Error?) -> Void) { let email = options.getEmail() let actionCodeSettings = options.getActionCodeSettings() diff --git a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.m b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.m index 73121ed0..8e40276a 100644 --- a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.m +++ b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.m @@ -59,4 +59,5 @@ CAP_PLUGIN_METHOD(updateProfile, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(useAppLanguage, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(useEmulator, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(verifyBeforeUpdateEmail, CAPPluginReturnPromise); ) diff --git a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift index d3502346..7ebd3578 100644 --- a/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift +++ b/packages/authentication/ios/Plugin/FirebaseAuthenticationPlugin.swift @@ -503,6 +503,32 @@ public class FirebaseAuthenticationPlugin: CAPPlugin { }) } + @objc func verifyBeforeUpdateEmail(_ call: CAPPluginCall) { + guard let newEmail = call.getString("newEmail") else { + call.reject(errorNewEmailMissing) + return + } + + guard let actionCodeSettingsDict = call.getObject("actionCodeSettings") else { + call.reject(errorActionCodeSettingsMissing) + return + } + guard let actionCodeSettings = FirebaseAuthenticationHelper.createActionCodeSettings(actionCodeSettingsDict) else { + call.reject(errorActionCodeSettingsMissing) + return + } + + implementation?.verifyBeforeUpdateEmail(newEmail, actionCodeSettings: actionCodeSettings, completion: { error in + if let error = error { + CAPLog.print("[", self.tag, "] ", error) + let code = FirebaseAuthenticationHelper.createErrorCode(error: error) + call.reject(error.localizedDescription, code) + return + } + call.resolve() + }) + } + @objc func updatePassword(_ call: CAPPluginCall) { guard let newPassword = call.getString("newPassword") else { call.reject(errorNewPasswordMissing) diff --git a/packages/authentication/src/definitions.ts b/packages/authentication/src/definitions.ts index c8211609..fcd993b3 100644 --- a/packages/authentication/src/definitions.ts +++ b/packages/authentication/src/definitions.ts @@ -464,6 +464,14 @@ export interface FirebaseAuthenticationPlugin { * @since 0.2.0 */ useEmulator(options: UseEmulatorOptions): Promise; + /** + * Verifies the new email address before updating the email address of the currently signed in user. + * + * @since 6.3.0 + */ + verifyBeforeUpdateEmail( + options: VerifyBeforeUpdateEmailOptions, + ): Promise; /** * Listen for the user's sign-in state changes. * @@ -780,6 +788,24 @@ export interface UpdateEmailOptions { newEmail: string; } +/** + * @since 6.3.0 + */ +export interface VerifyBeforeUpdateEmailOptions { + /** + * The new email address to be verified before update. + * + * @since 6.3.0 + */ + newEmail: string; + /** + * The action code settings + * + * @since 6.3.0 + */ + actionCodeSettings: ActionCodeSettings; +} + /** * @since 0.2.2 */ diff --git a/packages/authentication/src/web.ts b/packages/authentication/src/web.ts index d0c369a4..da0f1198 100644 --- a/packages/authentication/src/web.ts +++ b/packages/authentication/src/web.ts @@ -53,6 +53,7 @@ import { updateEmail, updatePassword, updateProfile, + verifyBeforeUpdateEmail, } from 'firebase/auth'; import type { @@ -102,6 +103,7 @@ import type { User, UserInfo, UserMetadata, + VerifyBeforeUpdateEmailOptions, } from './definitions'; import { Persistence, ProviderId } from './definitions'; @@ -761,6 +763,21 @@ export class FirebaseAuthenticationWeb } } + public async verifyBeforeUpdateEmail( + options: VerifyBeforeUpdateEmailOptions, + ): Promise { + const auth = getAuth(); + const currentUser = auth.currentUser; + if (!currentUser) { + throw new Error(FirebaseAuthenticationWeb.ERROR_NO_USER_SIGNED_IN); + } + return verifyBeforeUpdateEmail( + currentUser, + options?.newEmail, + options?.actionCodeSettings, + ); + } + private handleAuthStateChange(user: FirebaseUser | null): void { const userResult = this.createUserResult(user); const change: AuthStateChange = {