Skip to content

Commit

Permalink
(ios) implement enrollSecondAuthFactor() flow
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Alden committed Nov 1, 2022
1 parent b0db10e commit 402643f
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 53 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2650,9 +2650,9 @@ There are 3 verification scenarios:
- {function} error - callback function which will be passed a {string} error message as an argument
- {string} phoneNumber - phone number to verify
- {object} opts - (optional) parameters
- {integer} timeOutDuration - time to wait in seconds before timing out. Defaults to 30 seconds if not specified.
- {boolean} requireSmsValidation - whether to always required SMS validation on Android even if instant verification is available. Defaults to false if not specified.
- {string} fakeVerificationCode - to test instant verification on Android, specify a fake verification code to return for whitelisted phone numbers.
- {integer} timeOutDuration - (Android only) time to wait in seconds before timing out. Defaults to 30 seconds if not specified.
- {boolean} requireSmsValidation - (Android only) whether to always required SMS validation on Android even if instant verification is available. Defaults to false if not specified.
- {string} fakeVerificationCode - (Android only) to test instant verification on Android, specify a fake verification code to return for whitelisted phone numbers.
- See [Firebase SDK Phone Auth Android Integration Testing](https://firebase.google.com/docs/auth/android/phone-auth#integration-testing) for more info.

The success callback will be passed a credential object with the following possible properties:
Expand Down Expand Up @@ -2746,9 +2746,9 @@ As with [verifyPhoneNumber](#verifyphonenumber), this may require the user to ma
- {object} opts - (optional) parameters
- {string} displayName - display name for second factor. Used when a user has multiple second factor phone numbers enrolled and asking them which to use since the full phone number is masked.
- {object} credential - if manual entry of the verification code in an SMS is required, the `credential` object will be passed to the `success` function. The user-entered code should be appended to this object as the `code` property then this function re-invoked with the `credential` specified in the `opts` argument.
- {integer} timeOutDuration - time to wait in seconds before timing out. Defaults to 30 seconds if not specified.
- {boolean} requireSmsValidation - whether to always required SMS validation on Android even if instant verification is available. Defaults to false if not specified.
- {string} fakeVerificationCode - to test instant verification on Android, specify a fake verification code to return for whitelisted phone numbers.
- {integer} timeOutDuration - (Android only) time to wait in seconds before timing out. Defaults to 30 seconds if not specified.
- {boolean} requireSmsValidation - (Android only) whether to always required SMS validation on Android even if instant verification is available. Defaults to false if not specified.
- {string} fakeVerificationCode - (Android only) to test instant verification on Android, specify a fake verification code to return for whitelisted phone numbers.
- See [Firebase SDK Phone Auth Android Integration Testing](https://firebase.google.com/docs/auth/android/phone-auth#integration-testing) for more info.

Example usage:
Expand Down
1 change: 1 addition & 0 deletions src/ios/FirebasePlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
- (void)deleteUser:(CDVInvokedUrlCommand*)command;
- (void)useAuthEmulator:(CDVInvokedUrlCommand*)command;
- (void)getClaims:(CDVInvokedUrlCommand*)command;
- (void)enrollSecondAuthFactor:(CDVInvokedUrlCommand*)command;

// Remote notifications
- (void)getId:(CDVInvokedUrlCommand*)command;
Expand Down
150 changes: 103 additions & 47 deletions src/ios/FirebasePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,85 @@ - (void)verifyPhoneNumber:(CDVInvokedUrlCommand *)command {
}
}

- (void)enrollSecondAuthFactor:(CDVInvokedUrlCommand*)command {
@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;

NSString* number = [command.arguments objectAtIndex:0];
NSDictionary* opts = [command.arguments objectAtIndex:1];

NSString* displayName = nil;
if([opts objectForKey:@"displayName"] != nil){
displayName = [opts objectForKey:@"displayName"];
}

NSString* verificationId = nil;
NSString* verificationCode = nil;
if([opts objectForKey:@"credential"] != nil){
NSDictionary* credential = [opts objectForKey:@"credential"];
if([credential objectForKey:@"verificationId"] != nil && [credential objectForKey:@"code"] != nil){
verificationId = [credential objectForKey:@"verificationId"];
verificationCode = [credential objectForKey:@"code"];
}else{
[self sendPluginErrorWithMessage:@"'verificationId' and/or 'code' properties not found on 'credential' object" :command];
return;
}
}

[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession* _Nullable session,
NSError* _Nullable error) {
@try {
if(error != nil) return [self sendPluginErrorWithError:error command:command];

// If credential specifying verification ID and code has been provided
if(verificationId != nil && verificationCode != nil){
// Use them to enroll
FIRPhoneAuthCredential* credential =
[FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationId
verificationCode:verificationCode];
FIRMultiFactorAssertion* assertion =
[FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

// Complete enrollment. This will update the underlying tokens
// and trigger ID token change listener.
[user.multiFactor enrollWithAssertion:assertion
displayName:displayName
completion:^(NSError* _Nullable error) {
@try {
if(error != nil) return [self sendPluginErrorWithError:error command:command];
[self sendPluginBoolResult:true command:command callbackId:command.callbackId];
}@catch (NSException *exception) {
[self handlePluginExceptionWithContext:exception :command];
}
}];
}else{
// Send SMS verification code.
[FIRPhoneAuthProvider.provider
verifyPhoneNumber:number
UIDelegate:nil
multiFactorSession:session
completion:^(NSString* _Nullable verificationID, NSError* _Nullable error) {
@try {
if(error != nil) return [self sendPluginErrorWithError:error command:command];

NSMutableDictionary* result = [[NSMutableDictionary alloc] init];
[result setValue:verificationID forKey:@"verificationId"];
[self sendPluginDictionaryResult:result command:command callbackId:command.callbackId];
}@catch (NSException *exception) {
[self handlePluginExceptionWithContext:exception :command];
}
}];
}
}@catch (NSException *exception) {
[self handlePluginExceptionWithContext:exception :command];
}
}];
}@catch (NSException *exception) {
[self handlePluginExceptionWithContext:exception :command];
}
}

- (void)setLanguageCode:(CDVInvokedUrlCommand *)command {
NSString* lang = [command.arguments objectAtIndex:0];
@try {
Expand Down Expand Up @@ -828,11 +907,8 @@ - (void)signInWithCredential:(CDVInvokedUrlCommand*)command {

- (void)reauthenticateWithCredential:(CDVInvokedUrlCommand*)command{
@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;
if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}

FIRAuthCredential* credential = [self obtainAuthCredential:[command.arguments objectAtIndex:0] command:command];
if(credential == nil) return;
Expand Down Expand Up @@ -863,7 +939,7 @@ - (void)linkUserWithCredential:(CDVInvokedUrlCommand*)command {

- (void)isUserSignedIn:(CDVInvokedUrlCommand*)command {
@try {
bool isSignedIn = [FIRAuth auth].currentUser ? true : false;
bool isSignedIn = [self isSignedIn];
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:isSignedIn] callbackId:command.callbackId];

}@catch (NSException *exception) {
Expand All @@ -873,11 +949,7 @@ - (void)isUserSignedIn:(CDVInvokedUrlCommand*)command {

- (void)signOutUser:(CDVInvokedUrlCommand*)command {
@try {
bool isSignedIn = [FIRAuth auth].currentUser ? true : false;
if(!isSignedIn){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}
if([self userNotSignedInError:command]) return;

// If signed in with Google
if([GIDSignIn.sharedInstance currentUser] != nil){
Expand Down Expand Up @@ -910,11 +982,7 @@ - (void)signOutOfFirebase:(CDVInvokedUrlCommand*)command {
- (void)getCurrentUser:(CDVInvokedUrlCommand *)command {

@try {
FIRUser* user = [FIRAuth auth].currentUser;
if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}
if([self userNotSignedInError:command]) return;
[self extractAndReturnUserInfo:command];

}@catch (NSException *exception) {
Expand All @@ -925,11 +993,8 @@ - (void)getCurrentUser:(CDVInvokedUrlCommand *)command {
- (void)reloadCurrentUser:(CDVInvokedUrlCommand *)command {

@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;
if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}
[user reloadWithCompletion:^(NSError * _Nullable error) {
if (error != nil) {
[self handleEmptyResultWithPotentialError:error command:command];
Expand Down Expand Up @@ -967,11 +1032,8 @@ - (void) extractAndReturnUserInfo:(CDVInvokedUrlCommand *)command {

- (void)updateUserProfile:(CDVInvokedUrlCommand*)command {
@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;
if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}

NSDictionary* profile = [command.arguments objectAtIndex:0];

Expand All @@ -997,11 +1059,8 @@ - (void)updateUserProfile:(CDVInvokedUrlCommand*)command {

- (void)updateUserEmail:(CDVInvokedUrlCommand*)command {
@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;
if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}

NSString* email = [command.arguments objectAtIndex:0];
[user updateEmail:email completion:^(NSError *_Nullable error) {
Expand All @@ -1018,14 +1077,10 @@ - (void)updateUserEmail:(CDVInvokedUrlCommand*)command {

- (void)sendUserEmailVerification:(CDVInvokedUrlCommand*)command{
@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;
NSDictionary* actionCodeSettingsParams = [command.arguments objectAtIndex:0];

if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}

NSDictionary* actionCodeSettingsParams = [command.arguments objectAtIndex:0];
if(![actionCodeSettingsParams isEqual:[NSNull null]]) {
FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init];
if([actionCodeSettingsParams objectForKey:@"handleCodeInApp"] != nil){
Expand Down Expand Up @@ -1073,11 +1128,8 @@ - (void)sendUserEmailVerification:(CDVInvokedUrlCommand*)command{

- (void)updateUserPassword:(CDVInvokedUrlCommand*)command{
@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;
if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}

NSString* password = [command.arguments objectAtIndex:0];
[user updatePassword:password completion:^(NSError *_Nullable error) {
Expand Down Expand Up @@ -1109,11 +1161,8 @@ - (void)sendUserPasswordResetEmail:(CDVInvokedUrlCommand*)command{

- (void)deleteUser:(CDVInvokedUrlCommand*)command{
@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;
if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}

[user deleteWithCompletion:^(NSError *_Nullable error) {
@try {
Expand Down Expand Up @@ -1200,13 +1249,9 @@ - (void)useAuthEmulator:(CDVInvokedUrlCommand *)command {

- (void)getClaims:(CDVInvokedUrlCommand *)command {
@try {
if([self userNotSignedInError:command]) return;
FIRUser* user = [FIRAuth auth].currentUser;

if(!user){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId];
return;
}


[user getIDTokenResultWithCompletion:^(FIRAuthTokenResult * _Nullable tokenResult, NSError * _Nullable error) {
if(error != nil){
[self sendPluginErrorWithError:error command:command];
Expand Down Expand Up @@ -2489,6 +2534,17 @@ - (NSNumber*) saveAuthCredential: (FIRAuthCredential*) authCredential {
return key;
}

- (bool) isSignedIn {
return [FIRAuth auth].currentUser ? true : false;
}

- (bool) userNotSignedInError:(CDVInvokedUrlCommand *)command {
bool isError = ![self isSignedIn];
if(isError){
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No user is currently signed"] callbackId:command.callbackId]; }
return isError;
}

- (int) generateId {
int key = -1;
while (key < 0
Expand Down

0 comments on commit 402643f

Please sign in to comment.