Skip to content

Commit

Permalink
feat(authentication): add idTokenChange event listener (#751)
Browse files Browse the repository at this point in the history
* feat: extend authentication to support id token change event (#710)

* adds type definitions

* feat: extend authentication to support id token change event (#710)

* implements idTokenChange on the web

* feat: extend authentication to support id token change event (#710)

* refactors handling id token change in separate method on the web

* feat: extend authentication to support id token change event (#710)

* implements idTokenChange on ios

* feat: extend authentication to support id token change event (#710)

* implements idTokenChange on android

* feat: extend authentication to support id token change event (#710)

* ios pod install

* feat: extend authentication to support id token change event (#710)

* replaces user with an object with token property

* feat: extend authentication to support id token change event (#710)

* removes IdTokenChange interface

* feat: extend authentication to support id token change event (#710)

* refactors handleIdTokenChange on the web

* feat: extend authentication to support id token change event (#710)

* refactors handleIdTokenChange on ios and android

* feat: extend authentication to support id token change event (#710)

* removes todos

* feat: extend authentication to support id token change event (#710)

* adds changeset

* Update .changeset/chilled-pumpkins-kick.md

* Update packages/authentication/src/definitions.ts

* Update packages/authentication/src/definitions.ts

* feat: extend authentication to support id token change event (#710)

* implements suggested changes

---------

Co-authored-by: Robin Genz <[email protected]>
  • Loading branch information
ebarooni and robingenz authored Nov 7, 2024
1 parent 7af2032 commit b57f1ee
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-pumpkins-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@capacitor-firebase/authentication': minor
---

feat: add `idTokenChange` event listener
81 changes: 56 additions & 25 deletions packages/authentication/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -1473,22 +1474,6 @@ Updates the email address of the currently signed in user.

--------------------

### verifyBeforeUpdateEmail(...)

```typescript
verifyBeforeUpdateEmail(options: VerifyBeforeUpdateEmailOptions) => Promise<void>
```

Verifies the email before updating the email address of the currently signed in user.

| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| **`options`** | <code><a href="#verifybeforeupdateemailoptions">VerifyBeforeUpdateEmailOptions</a></code> |

**Since:** 6.2.0

--------------------


### updatePassword(...)

Expand Down Expand Up @@ -1554,6 +1539,23 @@ Instrument your app to talk to the Authentication emulator.
--------------------


### verifyBeforeUpdateEmail(...)

```typescript
verifyBeforeUpdateEmail(options: VerifyBeforeUpdateEmailOptions) => Promise<void>
```

Verifies the new email address before updating the email address of the currently signed in user.

| Param | Type |
| ------------- | ----------------------------------------------------------------------------------------- |
| **`options`** | <code><a href="#verifybeforeupdateemailoptions">VerifyBeforeUpdateEmailOptions</a></code> |

**Since:** 6.3.0

--------------------


### addListener('authStateChange', ...)

```typescript
Expand All @@ -1576,6 +1578,28 @@ Listen for the user's sign-in state changes.
--------------------


### addListener('idTokenChange', ...)

```typescript
addListener(eventName: 'idTokenChange', listenerFunc: IdTokenChangeListener) => Promise<PluginListenerHandle>
```

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`** | <code>'idTokenChange'</code> |
| **`listenerFunc`** | <code><a href="#idtokenchangelistener">IdTokenChangeListener</a></code> |

**Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>

**Since:** 6.3.0

--------------------


### addListener('phoneVerificationCompleted', ...)

```typescript
Expand Down Expand Up @@ -1992,14 +2016,6 @@ An interface covering the possible persistence mechanism types.
| **`newEmail`** | <code>string</code> | The new email address. | 0.2.2 |


#### VerifyBeforeUpdateEmailOptions

| Prop | Type | Description | Since |
| -------------- | ------------------- | ---------------------- | ----- |
| **`newEmail`** | <code>string</code> | The new email address to be verified before update. | 0.2.2 |
| **`actionCodeSettings`** | <code><a href="#actioncodesettings">ActionCodeSettings</a></code> | Structure that contains the required continue/state URL with optional Android and iOS bundle identifiers. | 1.1.0 |


#### UpdatePasswordOptions

| Prop | Type | Description | Since |
Expand All @@ -2024,6 +2040,14 @@ An interface covering the possible persistence mechanism types.
| **`scheme`** | <code>string</code> | The emulator scheme. Only available for Web. | <code>"http"</code> | 5.2.0 |


#### VerifyBeforeUpdateEmailOptions

| Prop | Type | Description | Since |
| ------------------------ | ----------------------------------------------------------------- | --------------------------------------------------- | ----- |
| **`newEmail`** | <code>string</code> | The new email address to be verified before update. | 6.3.0 |
| **`actionCodeSettings`** | <code><a href="#actioncodesettings">ActionCodeSettings</a></code> | The action code settings | 6.3.0 |


#### PluginListenerHandle

| Prop | Type |
Expand Down Expand Up @@ -2089,6 +2113,13 @@ Callback to receive the user's sign-in state change notifications.
<code>(change: <a href="#authstatechange">AuthStateChange</a>): void</code>


#### IdTokenChangeListener

Callback to receive the ID token change notifications.

<code>(change: <a href="#getidtokenresult">GetIdTokenResult</a>): void</code>


#### PhoneVerificationCompletedListener

Callback to receive the verification code sent to the user's phone number.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions packages/authentication/ios/Plugin.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -508,7 +509,7 @@ public class FirebaseAuthenticationPlugin: CAPPlugin {
call.reject(errorNewEmailMissing)
return
}

guard let actionCodeSettingsDict = call.getObject("actionCodeSettings") else {
call.reject(errorActionCodeSettingsMissing)
return
Expand Down Expand Up @@ -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()
Expand Down
18 changes: 18 additions & 0 deletions packages/authentication/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,17 @@ export interface FirebaseAuthenticationPlugin {
eventName: 'authStateChange',
listenerFunc: AuthStateChangeListener,
): Promise<PluginListenerHandle>;
/**
* 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<PluginListenerHandle>;
/**
* Listen for a completed phone verification.
*
Expand Down Expand Up @@ -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
*/
Expand Down
17 changes: 17 additions & 0 deletions packages/authentication/src/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<void> {
Expand Down Expand Up @@ -790,6 +792,21 @@ export class FirebaseAuthenticationWeb
);
}

private async handleIdTokenChange(user: FirebaseUser | null): Promise<void> {
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,
Expand Down

0 comments on commit b57f1ee

Please sign in to comment.