From 98fa7782a9e73eac094f749e1c3cb276cdf526c5 Mon Sep 17 00:00:00 2001 From: "Mr.BeanBopper" Date: Sun, 31 Mar 2024 15:48:32 -0400 Subject: [PATCH 1/3] totp --- .../auth/ReactNativeFirebaseAuthModule.java | 122 ++++++++++++++++++ packages/auth/ios/RNFBAuth/RNFBAuthModule.m | 89 ++++++++++++- packages/auth/lib/index.d.ts | 10 ++ packages/auth/lib/index.js | 10 ++ packages/auth/lib/multiFactor.js | 6 + 5 files changed, 236 insertions(+), 1 deletion(-) diff --git a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java index ef2ff264b9..c6889df668 100644 --- a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java +++ b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java @@ -65,6 +65,9 @@ import com.google.firebase.auth.PhoneMultiFactorAssertion; import com.google.firebase.auth.PhoneMultiFactorGenerator; import com.google.firebase.auth.PhoneMultiFactorInfo; +import com.google.firebase.auth.TotpMultiFactorAssertion; +import com.google.firebase.auth.TotpMultiFactorGenerator; +import com.google.firebase.auth.TotpSecret; import com.google.firebase.auth.TwitterAuthProvider; import com.google.firebase.auth.UserInfo; import com.google.firebase.auth.UserProfileChangeRequest; @@ -106,6 +109,7 @@ class ReactNativeFirebaseAuthModule extends ReactNativeFirebaseModule { private final HashMap mCachedResolvers = new HashMap<>(); private final HashMap mMultiFactorSessions = new HashMap<>(); + private TotpSecret totpSecret = null; ReactNativeFirebaseAuthModule(ReactApplicationContext reactContext) { super(reactContext, TAG); @@ -1105,6 +1109,124 @@ public void getSession(final String appName, final Promise promise) { }); } + @ReactMethod + public void generateSecret( + final String appName, + final String sessionKey, + final Boolean openInApp, + final Promise promise + ) { + final MultiFactorSession session = mMultiFactorSessions.get(sessionKey); + if (session == null) { + rejectPromiseWithCodeAndMessage(promise, "unknown", "can't find session for provided key"); + return; + } + + TotpMultiFactorGenerator.generateSecret(session) + .addOnCompleteListener( + task -> { + if (!task.isSuccessful()) { + rejectPromiseWithExceptionMap(promise, task.getException()); + return; + } + + final TotpSecret secret = task.getResult(); + final String sharedSecret = secret.getSharedSecretKey(); + + if (openInApp) { + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + FirebaseUser user = firebaseAuth.getCurrentUser(); + final String email; + if (user == null) { + rejectPromiseWithExceptionMap(promise, "unknown", "current user must be set"); + return; + } + email = user.getEmail(); + if (email == null) { + rejectPromiseWithExceptionMap(promise, "unknown", "email must be set"); + return; + } + final String qrCodeUrl = secret.generateQrCodeUrl(email, appName); + secret.openInOtpApp(qrCodeUrl); + } + this.totpSecret = secret; + promise.resolve(sharedSecret); + }); + } + + private void resolveTotpMultiFactorCredential( + final String verificationId, final String verificationCode, final String sessionKey, final Promise promise) { + + final MultiFactorAssertion multiFactorAssertion = TotpMultiFactorGenerator.getAssertionForSignIn(verificationId, verificationCode); + + final MultiFactorResolver resolver = mCachedResolvers.get(sessionKey); + if (resolver == null) { + rejectPromiseWithCodeAndMessage( + promise, + "invalid-multi-factor-session", + "No resolver for session found. Is the session id correct?"); + return; + } + + resolver + .resolveSignIn(multiFactorAssertion) + .addOnCompleteListener( + task -> { + if (task.isSuccessful()) { + AuthResult authResult = task.getResult(); + promiseWithAuthResult(authResult, promise); + } else { + promiseRejectAuthException(promise, task.getException()); + } + }); + } + + @ReactMethod + public void resolveTotpMultiFactorSignIn( + final String appName, + final String session, + final String verificationId, + final String verificationCode, + final Promise promise) { + resolveTotpMultiFactorCredential(verificationId, verificationCode, session, promise); + } + + @ReactMethod + public void enrollTotp( + final String appName, + final String verificationCode, + @Nullable final String displayName, + final Promise promise) { + + if (this.totpSecret == null) { + rejectPromiseWithCodeAndMessage( + promise, + "unknown", + "totp secret isn't set yet"); + return; + } + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + + final TotpMultiFactorAssertion assertion = TotpMultiFactorGenerator.getAssertionForEnrollment(this.totpSecret, verificationCode); + firebaseAuth + .getCurrentUser() + .getMultiFactor() + .enroll(assertion, displayName) + .addOnCompleteListener( + task -> { + if (task.isSuccessful()) { + Log.d(TAG, "finalizeMultiFactorEnrollment:onComplete:success"); + this.totpSecret = null; + promise.resolve(null); + } else { + Exception exception = task.getException(); + Log.e(TAG, "finalizeMultiFactorEnrollment:onComplete:failure", exception); + promiseRejectAuthException(promise, exception); + } + }); + } + @ReactMethod public void verifyPhoneNumberWithMultiFactorInfo( final String appName, final String hintUid, final String sessionKey, final Promise promise) { diff --git a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m index 76862bc069..887f473bc9 100644 --- a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m +++ b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m @@ -57,6 +57,7 @@ static __strong NSMutableDictionary *credentials; static __strong NSMutableDictionary *cachedResolver; static __strong NSMutableDictionary *cachedSessions; +static __strong TotpSecret *totpSecret; @implementation RNFBAuthModule #pragma mark - @@ -948,6 +949,92 @@ - (void)invalidate { }]; } +RCT_EXPORT_METHOD(generateSecret:(NSString *)appName sessionKey:(NSString *)sessionKey openInApp:(BOOL)openInApp resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + if ([cachedResolver valueForKey:sessionKey] == nil) { + [RNFBSharedUtils + rejectPromiseWithUserInfo:reject + userInfo:(NSMutableDictionary *)@{ + @"code" : @"invalid-multi-factor-session", + @"message" : @"No resolver for session found. Is the session id correct?" + }]; + return; + } + FIRMultiFactorSession *session = cachedResolver[sessionKey].session; + + [TotpMultiFactorGenerator generateSecretWithSession:session completion:^(TotpSecret * _Nullable secret, NSError * _Nullable error) { + if (error) { + [self promiseRejectAuthException:reject + error:error]; + return; + } + + NSString *sharedSecret = secret.sharedSecretKey; + + if (openInApp) { + FirebaseAuth *firebaseAuth = [FIRAuth authWithApp:[FIRApp appNamed:appName]]; + FIRUser *user = [firebaseAuth currentUser]; + if (user == nil) { + reject(@"unknown", @"current user must be set", nil); + return; + } + NSString *email = user.email; + if (email == nil) { + reject(@"unknown", @"email must be set", nil); + return; + } + NSString *qrCodeUrl = [secret generateQrCodeUrlWithEmail:email appName:appName]; + [secret openInOtpAppWithQrCodeUrl:qrCodeUrl]; + } + self.totpSecret = secret; + resolve(sharedSecret); + }]; +} + +RCT_EXPORT_METHOD(resolveTotpMultiFactorSignIn:(NSString *)appName session:(NSString *)session verificationId:(NSString *)verificationId verificationCode:(NSString *)verificationCode resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + [self resolveTotpMultiFactorCredentialWithVerificationId:verificationId verificationCode:verificationCode sessionKey:session resolver:resolve rejecter:reject]; +} + +RCT_EXPORT_METHOD(enrollTotp:(NSString *)appName verificationCode:(NSString *)verificationCode displayName:(NSString *)displayName resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + if (self.totpSecret == nil) { + reject(@"unknown", @"totp secret isn't set yet", nil); + return; + } + TotpMultiFactorAssertion *assertion = [TotpMultiFactorGenerator getAssertionForEnrollmentWithSecret:self.totpSecret verificationCode:verificationCode]; + FIRUser *user = [FIRAuth authWithApp:firebaseApp].currentUser; + [user.multiFactor enrollWithAssertion:assertion + displayName:displayName + completion:^(NSError *_Nullable error) { + if (error != nil) { + [self promiseRejectAuthException:reject error:error]; + return; + } + + resolve(nil); + return; + }]; +} + +- (void)resolveTotpMultiFactorCredentialWithVerificationId:(NSString *)verificationId verificationCode:(NSString *)verificationCode sessionKey:(NSString *)sessionKey resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject { + MultiFactorAssertion *multiFactorAssertion = [TotpMultiFactorGenerator getAssertionForSignInWithVerificationId:verificationId verificationCode:verificationCode]; + + MultiFactorResolver *resolver = [self.cachedResolvers objectForKey:sessionKey]; + if (resolver == nil) { + reject(@"invalid-multi-factor-session", @"No resolver for session found. Is the session id correct?", nil); + return; + } + + [resolver resolveSignInWithAssertion:multiFactorAssertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { + if (error) { + reject(@"unknown", error.localizedDescription, error); + } else { + // Handle the authResult as needed + resolve(authResult); + } + }]; +} + + + RCT_EXPORT_METHOD(finalizeMultiFactorEnrollment : (FIRApp *)firebaseApp : (NSString *)verificationId @@ -1745,4 +1832,4 @@ - (FIRActionCodeSettings *)buildActionCodeSettings:(NSDictionary *)actionCodeSet return settings; } -@end \ No newline at end of file +@end diff --git a/packages/auth/lib/index.d.ts b/packages/auth/lib/index.d.ts index 90650342bf..1342a2fcc8 100644 --- a/packages/auth/lib/index.d.ts +++ b/packages/auth/lib/index.d.ts @@ -577,6 +577,12 @@ export namespace FirebaseAuthTypes { * The method will ensure the user state is reloaded after successfully enrolling a factor. */ enroll(assertion: MultiFactorAssertion, displayName?: string): Promise; + + /** + * Enroll TOTP factor. Provide an optional display name that can be shown to the user. + * The method will ensure the user state is reloaded after successfully enrolling a factor. + */ + enrollTotp(verificationCode: string, displayName?: string): Promise; } /** @@ -1841,6 +1847,10 @@ export namespace FirebaseAuthTypes { */ signInWithCredential(credential: AuthCredential): Promise; + generateSecret(session: MultiFactorSession, openInApp?: boolean): Promise; + + resolveTotpMultiFactorSignIn(session: MultiFactorSession, verificationId: string, verificationCode: string): Promise; + /** * Signs the user in with a specified provider. This is a web-compatible API along with signInWithRedirect. * They both share the same call to the underlying native SDK signInWithProvider method. diff --git a/packages/auth/lib/index.js b/packages/auth/lib/index.js index 9c8c1e511b..67a75d0b32 100644 --- a/packages/auth/lib/index.js +++ b/packages/auth/lib/index.js @@ -375,6 +375,16 @@ class FirebaseAuthModule extends FirebaseModule { .then(userCredential => this._setUserCredential(userCredential)); } + generateSecret(session, openInApp = false) { + return this.native.generateSecret(session, openInApp); + } + + resolveTotpMultiFactorSignIn(session, verificationId, verificationCode) { + return this.native + .resolveTotpMultiFactorSignIn(session, verificationId, verificationCode) + .then(userCredential => this._setUserCredential(userCredential)); + } + revokeToken(authorizationCode) { return this.native.revokeToken(authorizationCode); } diff --git a/packages/auth/lib/multiFactor.js b/packages/auth/lib/multiFactor.js index 889f01b617..35405a8bf9 100644 --- a/packages/auth/lib/multiFactor.js +++ b/packages/auth/lib/multiFactor.js @@ -34,6 +34,12 @@ export class MultiFactorUser { return this._auth.currentUser.reload(); } + async enrollTotp(verificationCode, displayName) { + await this._auth.native.enrollTotp(verificationCode, displayName); + + return this._auth.currentUser.reload(); + } + unenroll() { return Promise.reject(new Error('No implemented yet.')); } From 93201b163944f6d4ea48beec1d19333bca073ac6 Mon Sep 17 00:00:00 2001 From: "Mr.BeanBopper" Date: Sun, 31 Mar 2024 16:42:30 -0400 Subject: [PATCH 2/3] fix for android build --- .../auth/ReactNativeFirebaseAuthModule.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java index c6889df668..4c7077537b 100644 --- a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java +++ b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java @@ -1134,20 +1134,22 @@ public void generateSecret( final String sharedSecret = secret.getSharedSecretKey(); if (openInApp) { - FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); - FirebaseUser user = firebaseAuth.getCurrentUser(); - final String email; - if (user == null) { - rejectPromiseWithExceptionMap(promise, "unknown", "current user must be set"); - return; - } - email = user.getEmail(); - if (email == null) { - rejectPromiseWithExceptionMap(promise, "unknown", "email must be set"); - return; - } - final String qrCodeUrl = secret.generateQrCodeUrl(email, appName); - secret.openInOtpApp(qrCodeUrl); + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); + final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + FirebaseUser user = firebaseAuth.getCurrentUser(); + + + if (user == null) { + rejectPromiseWithCodeAndMessage(promise, "unknown", "current user must be set"); + return; + } + String email = user.getEmail(); + if (email == null) { + rejectPromiseWithCodeAndMessage(promise, "unknown", "email must be set"); + return; + } + final String qrCodeUrl = secret.generateQrCodeUrl(email, appName); + secret.openInOtpApp(qrCodeUrl); } this.totpSecret = secret; promise.resolve(sharedSecret); From 3a4289d4c87620dd7228ee64b30c03145673fbcb Mon Sep 17 00:00:00 2001 From: "Mr.BeanBopper" Date: Sun, 31 Mar 2024 16:53:05 -0400 Subject: [PATCH 3/3] proper formatting --- .../auth/ReactNativeFirebaseAuthModule.java | 200 +++++++++--------- packages/auth/ios/RNFBAuth/RNFBAuthModule.m | 150 +++++++------ 2 files changed, 191 insertions(+), 159 deletions(-) diff --git a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java index 4c7077537b..71af2b0fa8 100644 --- a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java +++ b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java @@ -1111,122 +1111,122 @@ public void getSession(final String appName, final Promise promise) { @ReactMethod public void generateSecret( - final String appName, - final String sessionKey, - final Boolean openInApp, - final Promise promise - ) { - final MultiFactorSession session = mMultiFactorSessions.get(sessionKey); - if (session == null) { - rejectPromiseWithCodeAndMessage(promise, "unknown", "can't find session for provided key"); - return; - } + final String appName, + final String sessionKey, + final Boolean openInApp, + final Promise promise) { + final MultiFactorSession session = mMultiFactorSessions.get(sessionKey); + if (session == null) { + rejectPromiseWithCodeAndMessage(promise, "unknown", "can't find session for provided key"); + return; + } + + TotpMultiFactorGenerator.generateSecret(session) + .addOnCompleteListener( + task -> { + if (!task.isSuccessful()) { + rejectPromiseWithExceptionMap(promise, task.getException()); + return; + } + + final TotpSecret secret = task.getResult(); + final String sharedSecret = secret.getSharedSecretKey(); - TotpMultiFactorGenerator.generateSecret(session) - .addOnCompleteListener( - task -> { - if (!task.isSuccessful()) { - rejectPromiseWithExceptionMap(promise, task.getException()); - return; - } - - final TotpSecret secret = task.getResult(); - final String sharedSecret = secret.getSharedSecretKey(); - - if (openInApp) { - FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); - final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); - FirebaseUser user = firebaseAuth.getCurrentUser(); - - - if (user == null) { - rejectPromiseWithCodeAndMessage(promise, "unknown", "current user must be set"); - return; - } - String email = user.getEmail(); - if (email == null) { - rejectPromiseWithCodeAndMessage(promise, "unknown", "email must be set"); - return; - } - final String qrCodeUrl = secret.generateQrCodeUrl(email, appName); - secret.openInOtpApp(qrCodeUrl); - } - this.totpSecret = secret; - promise.resolve(sharedSecret); - }); + if (openInApp) { + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); + final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + FirebaseUser user = firebaseAuth.getCurrentUser(); + + if (user == null) { + rejectPromiseWithCodeAndMessage(promise, "unknown", "current user must be set"); + return; + } + String email = user.getEmail(); + if (email == null) { + rejectPromiseWithCodeAndMessage(promise, "unknown", "email must be set"); + return; + } + final String qrCodeUrl = secret.generateQrCodeUrl(email, appName); + secret.openInOtpApp(qrCodeUrl); + } + this.totpSecret = secret; + promise.resolve(sharedSecret); + }); } private void resolveTotpMultiFactorCredential( - final String verificationId, final String verificationCode, final String sessionKey, final Promise promise) { + final String verificationId, + final String verificationCode, + final String sessionKey, + final Promise promise) { - final MultiFactorAssertion multiFactorAssertion = TotpMultiFactorGenerator.getAssertionForSignIn(verificationId, verificationCode); + final MultiFactorAssertion multiFactorAssertion = + TotpMultiFactorGenerator.getAssertionForSignIn(verificationId, verificationCode); - final MultiFactorResolver resolver = mCachedResolvers.get(sessionKey); - if (resolver == null) { - rejectPromiseWithCodeAndMessage( - promise, - "invalid-multi-factor-session", - "No resolver for session found. Is the session id correct?"); - return; - } + final MultiFactorResolver resolver = mCachedResolvers.get(sessionKey); + if (resolver == null) { + rejectPromiseWithCodeAndMessage( + promise, + "invalid-multi-factor-session", + "No resolver for session found. Is the session id correct?"); + return; + } - resolver - .resolveSignIn(multiFactorAssertion) - .addOnCompleteListener( - task -> { - if (task.isSuccessful()) { - AuthResult authResult = task.getResult(); - promiseWithAuthResult(authResult, promise); - } else { - promiseRejectAuthException(promise, task.getException()); - } - }); + resolver + .resolveSignIn(multiFactorAssertion) + .addOnCompleteListener( + task -> { + if (task.isSuccessful()) { + AuthResult authResult = task.getResult(); + promiseWithAuthResult(authResult, promise); + } else { + promiseRejectAuthException(promise, task.getException()); + } + }); } @ReactMethod public void resolveTotpMultiFactorSignIn( - final String appName, - final String session, - final String verificationId, - final String verificationCode, - final Promise promise) { - resolveTotpMultiFactorCredential(verificationId, verificationCode, session, promise); + final String appName, + final String session, + final String verificationId, + final String verificationCode, + final Promise promise) { + resolveTotpMultiFactorCredential(verificationId, verificationCode, session, promise); } @ReactMethod public void enrollTotp( - final String appName, - final String verificationCode, - @Nullable final String displayName, - final Promise promise) { - - if (this.totpSecret == null) { - rejectPromiseWithCodeAndMessage( - promise, - "unknown", - "totp secret isn't set yet"); - return; - } - FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); - FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + final String appName, + final String verificationCode, + @Nullable final String displayName, + final Promise promise) { - final TotpMultiFactorAssertion assertion = TotpMultiFactorGenerator.getAssertionForEnrollment(this.totpSecret, verificationCode); - firebaseAuth - .getCurrentUser() - .getMultiFactor() - .enroll(assertion, displayName) - .addOnCompleteListener( - task -> { - if (task.isSuccessful()) { - Log.d(TAG, "finalizeMultiFactorEnrollment:onComplete:success"); - this.totpSecret = null; - promise.resolve(null); - } else { - Exception exception = task.getException(); - Log.e(TAG, "finalizeMultiFactorEnrollment:onComplete:failure", exception); - promiseRejectAuthException(promise, exception); - } - }); + if (this.totpSecret == null) { + rejectPromiseWithCodeAndMessage(promise, "unknown", "totp secret isn't set yet"); + return; + } + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + + final TotpMultiFactorAssertion assertion = + TotpMultiFactorGenerator.getAssertionForEnrollment(this.totpSecret, verificationCode); + firebaseAuth + .getCurrentUser() + .getMultiFactor() + .enroll(assertion, displayName) + .addOnCompleteListener( + task -> { + if (task.isSuccessful()) { + Log.d(TAG, "finalizeMultiFactorEnrollment:onComplete:success"); + this.totpSecret = null; + promise.resolve(null); + } else { + Exception exception = task.getException(); + Log.e(TAG, "finalizeMultiFactorEnrollment:onComplete:failure", exception); + promiseRejectAuthException(promise, exception); + } + }); } @ReactMethod diff --git a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m index 887f473bc9..e8d844ac25 100644 --- a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m +++ b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m @@ -949,7 +949,12 @@ - (void)invalidate { }]; } -RCT_EXPORT_METHOD(generateSecret:(NSString *)appName sessionKey:(NSString *)sessionKey openInApp:(BOOL)openInApp resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { +RCT_EXPORT_METHOD(generateSecret + : (NSString *)appName sessionKey + : (NSString *)sessionKey openInApp + : (BOOL)openInApp resolver + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) { if ([cachedResolver valueForKey:sessionKey] == nil) { [RNFBSharedUtils rejectPromiseWithUserInfo:reject @@ -961,47 +966,67 @@ - (void)invalidate { } FIRMultiFactorSession *session = cachedResolver[sessionKey].session; - [TotpMultiFactorGenerator generateSecretWithSession:session completion:^(TotpSecret * _Nullable secret, NSError * _Nullable error) { - if (error) { - [self promiseRejectAuthException:reject - error:error]; - return; - } - - NSString *sharedSecret = secret.sharedSecretKey; - - if (openInApp) { - FirebaseAuth *firebaseAuth = [FIRAuth authWithApp:[FIRApp appNamed:appName]]; - FIRUser *user = [firebaseAuth currentUser]; - if (user == nil) { - reject(@"unknown", @"current user must be set", nil); - return; - } - NSString *email = user.email; - if (email == nil) { - reject(@"unknown", @"email must be set", nil); - return; - } - NSString *qrCodeUrl = [secret generateQrCodeUrlWithEmail:email appName:appName]; - [secret openInOtpAppWithQrCodeUrl:qrCodeUrl]; - } - self.totpSecret = secret; - resolve(sharedSecret); - }]; -} - -RCT_EXPORT_METHOD(resolveTotpMultiFactorSignIn:(NSString *)appName session:(NSString *)session verificationId:(NSString *)verificationId verificationCode:(NSString *)verificationCode resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - [self resolveTotpMultiFactorCredentialWithVerificationId:verificationId verificationCode:verificationCode sessionKey:session resolver:resolve rejecter:reject]; -} - -RCT_EXPORT_METHOD(enrollTotp:(NSString *)appName verificationCode:(NSString *)verificationCode displayName:(NSString *)displayName resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - if (self.totpSecret == nil) { - reject(@"unknown", @"totp secret isn't set yet", nil); - return; - } - TotpMultiFactorAssertion *assertion = [TotpMultiFactorGenerator getAssertionForEnrollmentWithSecret:self.totpSecret verificationCode:verificationCode]; - FIRUser *user = [FIRAuth authWithApp:firebaseApp].currentUser; - [user.multiFactor enrollWithAssertion:assertion + [TotpMultiFactorGenerator + generateSecretWithSession:session + completion:^(TotpSecret *_Nullable secret, NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + return; + } + + NSString *sharedSecret = secret.sharedSecretKey; + + if (openInApp) { + FirebaseAuth *firebaseAuth = + [FIRAuth authWithApp:[FIRApp appNamed:appName]]; + FIRUser *user = [firebaseAuth currentUser]; + if (user == nil) { + reject(@"unknown", @"current user must be set", nil); + return; + } + NSString *email = user.email; + if (email == nil) { + reject(@"unknown", @"email must be set", nil); + return; + } + NSString *qrCodeUrl = [secret generateQrCodeUrlWithEmail:email + appName:appName]; + [secret openInOtpAppWithQrCodeUrl:qrCodeUrl]; + } + self.totpSecret = secret; + resolve(sharedSecret); + }]; +} + +RCT_EXPORT_METHOD(resolveTotpMultiFactorSignIn + : (NSString *)appName session + : (NSString *)session verificationId + : (NSString *)verificationId verificationCode + : (NSString *)verificationCode resolver + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) { + [self resolveTotpMultiFactorCredentialWithVerificationId:verificationId + verificationCode:verificationCode + sessionKey:session + resolver:resolve + rejecter:reject]; +} + +RCT_EXPORT_METHOD(enrollTotp + : (NSString *)appName verificationCode + : (NSString *)verificationCode displayName + : (NSString *)displayName resolver + : (RCTPromiseResolveBlock)resolve rejecter + : (RCTPromiseRejectBlock)reject) { + if (self.totpSecret == nil) { + reject(@"unknown", @"totp secret isn't set yet", nil); + return; + } + TotpMultiFactorAssertion *assertion = + [TotpMultiFactorGenerator getAssertionForEnrollmentWithSecret:self.totpSecret + verificationCode:verificationCode]; + FIRUser *user = [FIRAuth authWithApp:firebaseApp].currentUser; + [user.multiFactor enrollWithAssertion:assertion displayName:displayName completion:^(NSError *_Nullable error) { if (error != nil) { @@ -1014,27 +1039,34 @@ - (void)invalidate { }]; } -- (void)resolveTotpMultiFactorCredentialWithVerificationId:(NSString *)verificationId verificationCode:(NSString *)verificationCode sessionKey:(NSString *)sessionKey resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject { - MultiFactorAssertion *multiFactorAssertion = [TotpMultiFactorGenerator getAssertionForSignInWithVerificationId:verificationId verificationCode:verificationCode]; +- (void)resolveTotpMultiFactorCredentialWithVerificationId:(NSString *)verificationId + verificationCode:(NSString *)verificationCode + sessionKey:(NSString *)sessionKey + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject { + MultiFactorAssertion *multiFactorAssertion = + [TotpMultiFactorGenerator getAssertionForSignInWithVerificationId:verificationId + verificationCode:verificationCode]; - MultiFactorResolver *resolver = [self.cachedResolvers objectForKey:sessionKey]; - if (resolver == nil) { - reject(@"invalid-multi-factor-session", @"No resolver for session found. Is the session id correct?", nil); - return; - } + MultiFactorResolver *resolver = [self.cachedResolvers objectForKey:sessionKey]; + if (resolver == nil) { + reject(@"invalid-multi-factor-session", + @"No resolver for session found. Is the session id correct?", nil); + return; + } - [resolver resolveSignInWithAssertion:multiFactorAssertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { - if (error) { - reject(@"unknown", error.localizedDescription, error); - } else { - // Handle the authResult as needed - resolve(authResult); - } - }]; + [resolver resolveSignInWithAssertion:multiFactorAssertion + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + if (error) { + reject(@"unknown", error.localizedDescription, error); + } else { + // Handle the authResult as needed + resolve(authResult); + } + }]; } - - RCT_EXPORT_METHOD(finalizeMultiFactorEnrollment : (FIRApp *)firebaseApp : (NSString *)verificationId