Skip to content

Commit b8c33e1

Browse files
Added TOTP MFA Assertion logics (#11380)
* Added TOTP Assertion and Generator logics
1 parent 978f6d7 commit b8c33e1

10 files changed

+209
-39
lines changed

FirebaseAuth/Sources/Backend/FIRAuthBackend.m

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474

7575
#import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
7676
#import "FirebaseAuth/Sources/MultiFactor/Phone/FIRPhoneMultiFactorInfo+Internal.h"
77+
#import "FirebaseAuth/Sources/MultiFactor/TOTP/FIRTOTPMultiFactorInfo.h"
78+
7779
#endif
7880

7981
NS_ASSUME_NONNULL_BEGIN
@@ -846,9 +848,17 @@ - (void)verifyPassword:(FIRVerifyPasswordRequest *)request
846848
#if TARGET_OS_IOS
847849
NSMutableArray<FIRMultiFactorInfo *> *multiFactorInfo = [NSMutableArray array];
848850
for (FIRAuthProtoMFAEnrollment *MFAEnrollment in response.MFAInfo) {
849-
FIRPhoneMultiFactorInfo *info =
850-
[[FIRPhoneMultiFactorInfo alloc] initWithProto:MFAEnrollment];
851-
[multiFactorInfo addObject:info];
851+
// check which MFA factors are enabled.
852+
if (MFAEnrollment.phoneInfo != nil) {
853+
FIRPhoneMultiFactorInfo *info =
854+
[[FIRPhoneMultiFactorInfo alloc] initWithProto:MFAEnrollment];
855+
[multiFactorInfo addObject:info];
856+
}
857+
if (MFAEnrollment.TOTPInfo != nil) {
858+
FIRTOTPMultiFactorInfo *info =
859+
[[FIRTOTPMultiFactorInfo alloc] initWithProto:MFAEnrollment];
860+
[multiFactorInfo addObject:info];
861+
}
852862
}
853863
NSError *multiFactorRequiredError = [FIRAuthErrorUtils
854864
secondFactorRequiredErrorWithPendingCredential:response.MFAPendingCredential

FirebaseAuth/Sources/Backend/RPC/Proto/FIRAuthProtoMFAEnrollment.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
2121
@interface FIRAuthProtoMFAEnrollment : NSObject <FIRAuthProto>
2222

2323
@property(nonatomic, copy, readonly, nullable) NSString *phoneInfo;
24+
2425
@property(nonatomic, copy, readonly, nullable) NSObject *TOTPInfo;
2526

2627
@property(nonatomic, copy, readonly, nullable) NSString *MFAEnrollmentID;

FirebaseAuth/Sources/Backend/RPC/Proto/TOTP/FIRAuthProtoFinalizeMFATOTPSignInRequestInfo.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,28 @@
1818

1919
NS_ASSUME_NONNULL_BEGIN
2020

21+
/**
22+
@brief FIRAuthProtoFinalizeMFATOTPSignInRequestInfo class. This class is used to compose
23+
finalizeMFASignInRequest for TOTP case.
24+
*/
2125
@interface FIRAuthProtoFinalizeMFATOTPSignInRequestInfo : NSObject <FIRAuthProto>
2226

27+
/**
28+
@brief Multifactor enrollment ID.
29+
*/
2330
@property(nonatomic, strong, readonly, nullable) NSString *mfaEnrollmentID;
2431

32+
/**
33+
@brief Verification code.
34+
*/
2535
@property(nonatomic, strong, readonly, nullable) NSString *verificationCode;
2636

37+
/**
38+
@fn initWithMfaEnrollmentID:verificationCode
39+
@brief initialize function for FIRAuthProtoFinalizeMFATOTPSignInRequestInfo.
40+
@param mfaEnrollmentID Multifactor enrollment ID.
41+
@param verificationCode One time verification code.
42+
*/
2743
- (instancetype)initWithMfaEnrollmentID:(NSString *)mfaEnrollmentID
2844
verificationCode:(NSString *)verificationCode;
2945
@end

FirebaseAuth/Sources/MultiFactor/FIRMultiFactorResolver.m

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,18 @@
2424
#import "FirebaseAuth/Sources/Backend/FIRAuthBackend+MultiFactor.h"
2525
#import "FirebaseAuth/Sources/Backend/RPC/MultiFactor/SignIn/FIRFinalizeMFASignInRequest.h"
2626
#import "FirebaseAuth/Sources/Backend/RPC/Proto/Phone/FIRAuthProtoFinalizeMFAPhoneRequestInfo.h"
27+
#import "FirebaseAuth/Sources/Backend/RPC/Proto/TOTP/FIRAuthProtoFinalizeMFATOTPSignInRequestInfo.h"
2728
#import "FirebaseAuth/Sources/MultiFactor/FIRMultiFactorResolver+Internal.h"
2829
#import "FirebaseAuth/Sources/MultiFactor/FIRMultiFactorSession+Internal.h"
2930

3031
#if TARGET_OS_IOS
3132
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRPhoneMultiFactorAssertion.h"
33+
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRTOTPMultiFactorAssertion.h"
3234

3335
#import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
3436
#import "FirebaseAuth/Sources/MultiFactor/Phone/FIRPhoneMultiFactorAssertion+Internal.h"
37+
#import "FirebaseAuth/Sources/MultiFactor/TOTP/FIRTOTPMultiFactorAssertion+Internal.h"
38+
3539
#endif
3640

3741
NS_ASSUME_NONNULL_BEGIN
@@ -55,43 +59,53 @@ - (instancetype)initWithMFAPendingCredential:(NSString *_Nullable)MFAPendingCred
5559
- (void)resolveSignInWithAssertion:(nonnull FIRMultiFactorAssertion *)assertion
5660
completion:(nullable FIRAuthDataResultCallback)completion {
5761
#if TARGET_OS_IOS
58-
FIRPhoneMultiFactorAssertion *phoneAssertion = (FIRPhoneMultiFactorAssertion *)assertion;
59-
FIRAuthProtoFinalizeMFAPhoneRequestInfo *finalizeMFAPhoneRequestInfo =
60-
[[FIRAuthProtoFinalizeMFAPhoneRequestInfo alloc]
61-
initWithSessionInfo:phoneAssertion.authCredential.verificationID
62-
verificationCode:phoneAssertion.authCredential.verificationCode];
63-
FIRFinalizeMFASignInRequest *request = [[FIRFinalizeMFASignInRequest alloc]
64-
initWithMFAPendingCredential:self.MFAPendingCredential
65-
verificationInfo:finalizeMFAPhoneRequestInfo
66-
requestConfiguration:self.auth.requestConfiguration];
67-
[FIRAuthBackend
68-
finalizeMultiFactorSignIn:request
69-
callback:^(FIRFinalizeMFASignInResponse *_Nullable response,
70-
NSError *_Nullable error) {
71-
if (error) {
72-
if (completion) {
73-
completion(nil, error);
62+
NSObject<FIRAuthProto> *finalizedMFARequestInfo;
63+
if ([assertion isKindOfClass:[FIRPhoneMultiFactorAssertion class]]) {
64+
FIRPhoneMultiFactorAssertion *phoneAssertion = (FIRPhoneMultiFactorAssertion *)assertion;
65+
finalizedMFARequestInfo = [[FIRAuthProtoFinalizeMFAPhoneRequestInfo alloc]
66+
initWithSessionInfo:phoneAssertion.authCredential.verificationID
67+
verificationCode:phoneAssertion.authCredential.verificationCode];
68+
} else if ([assertion isKindOfClass:[FIRTOTPMultiFactorAssertion class]]) {
69+
FIRTOTPMultiFactorAssertion *totpAssertion = (FIRTOTPMultiFactorAssertion *)assertion;
70+
finalizedMFARequestInfo = [[FIRAuthProtoFinalizeMFATOTPSignInRequestInfo alloc]
71+
initWithMfaEnrollmentID:totpAssertion.enrollmentID
72+
verificationCode:totpAssertion.oneTimePassword];
73+
}
74+
if (finalizedMFARequestInfo != nil) {
75+
FIRFinalizeMFASignInRequest *request = [[FIRFinalizeMFASignInRequest alloc]
76+
initWithMFAPendingCredential:self.MFAPendingCredential
77+
verificationInfo:finalizedMFARequestInfo
78+
requestConfiguration:self.auth.requestConfiguration];
79+
[FIRAuthBackend
80+
finalizeMultiFactorSignIn:request
81+
callback:^(FIRFinalizeMFASignInResponse *_Nullable response,
82+
NSError *_Nullable error) {
83+
if (error) {
84+
if (completion) {
85+
completion(nil, error);
86+
}
87+
} else {
88+
[FIRAuth.auth
89+
completeSignInWithAccessToken:response.IDToken
90+
accessTokenExpirationDate:nil
91+
refreshToken:response.refreshToken
92+
anonymous:NO
93+
callback:^(FIRUser *_Nullable user,
94+
NSError *_Nullable error) {
95+
FIRAuthDataResult *result =
96+
[[FIRAuthDataResult alloc]
97+
initWithUser:user
98+
additionalUserInfo:nil];
99+
FIRAuthDataResultCallback
100+
decoratedCallback = [FIRAuth.auth
101+
signInFlowAuthDataResultCallbackByDecoratingCallback:
102+
completion];
103+
decoratedCallback(result, error);
104+
}];
74105
}
75-
} else {
76-
[FIRAuth.auth
77-
completeSignInWithAccessToken:response.IDToken
78-
accessTokenExpirationDate:nil
79-
refreshToken:response.refreshToken
80-
anonymous:NO
81-
callback:^(FIRUser *_Nullable user,
82-
NSError *_Nullable error) {
83-
FIRAuthDataResult *result =
84-
[[FIRAuthDataResult alloc]
85-
initWithUser:user
86-
additionalUserInfo:nil];
87-
FIRAuthDataResultCallback decoratedCallback =
88-
[FIRAuth.auth
89-
signInFlowAuthDataResultCallbackByDecoratingCallback:
90-
completion];
91-
decoratedCallback(result, error);
92-
}];
93-
}
94-
}];
106+
}];
107+
}
108+
95109
#endif
96110
}
97111

FirebaseAuth/Sources/MultiFactor/TOTP/FIRTOTPMultiFactorAssertion+Internal.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,17 @@ NS_ASSUME_NONNULL_BEGIN
3232
@brief secret TOTPSecret
3333
*/
3434
@property(nonatomic, copy, readonly, nonnull) FIRTOTPSecret *secret;
35+
3536
/**
3637
@brief one time password string
3738
*/
3839
@property(nonatomic, copy, readonly, nonnull) NSString *oneTimePassword;
3940

41+
/**
42+
@brief the enrollment ID
43+
*/
44+
@property(nonatomic, copy, readonly, nonnull) NSString *enrollmentID;
45+
4046
/**
4147
@fn initWithSecret
4248
@brief initializing function
@@ -45,6 +51,15 @@ NS_ASSUME_NONNULL_BEGIN
4551
*/
4652
- (instancetype)initWithSecret:(FIRTOTPSecret *)secret oneTimePassword:(NSString *)oneTimePassword;
4753

54+
/**
55+
@fn initWithEnrollmentID:oneTimePassword
56+
@brief initializing function
57+
@param enrollmentID enrollment ID
58+
@param oneTimePassword one time password string
59+
*/
60+
- (instancetype)initWithEnrollmentID:(NSString *)enrollmentID
61+
oneTimePassword:(NSString *)oneTimePassword;
62+
4863
@end
4964

5065
NS_ASSUME_NONNULL_END

FirebaseAuth/Sources/MultiFactor/TOTP/FIRTOTPMultiFactorAssertion.m

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ - (instancetype)initWithSecret:(FIRTOTPSecret *)secret oneTimePassword:(NSString
4545
return self;
4646
}
4747

48+
- (instancetype)initWithEnrollmentID:(NSString *)enrollmentID
49+
oneTimePassword:(NSString *)oneTimePassword {
50+
self = [super init];
51+
if (self) {
52+
_factorID = FIRTOTPMultiFactorID;
53+
_enrollmentID = enrollmentID;
54+
_oneTimePassword = oneTimePassword;
55+
}
56+
return self;
57+
}
58+
4859
@end
4960

5061
NS_ASSUME_NONNULL_END

FirebaseAuth/Sources/MultiFactor/TOTP/FIRTOTPMultiFactorGenerator.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ + (FIRTOTPMultiFactorAssertion *)assertionForEnrollmentWithSecret:(FIRTOTPSecret
9393
oneTimePassword:oneTimePassword];
9494
}
9595

96+
+ (FIRTOTPMultiFactorAssertion *)assertionForSignInWithEnrollmentID:(NSString *)enrollmentID
97+
oneTimePassword:(NSString *)oneTimePassword {
98+
return [[FIRTOTPMultiFactorAssertion alloc] initWithEnrollmentID:enrollmentID
99+
oneTimePassword:oneTimePassword];
100+
}
101+
96102
@end
97103

98104
#endif
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRMultiFactorInfo.h"
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
/**
24+
@class FIRTotpMultiFactorInfo
25+
@brief Extends the MultiFactorInfo class for time based one-time password second factors.
26+
The identifier of this second factor is "totp".
27+
This class is available on iOS only.
28+
*/
29+
NS_SWIFT_NAME(TOTPMultiFactorInfo) API_UNAVAILABLE(macos, tvos, watchos)
30+
@interface FIRTOTPMultiFactorInfo : FIRMultiFactorInfo
31+
32+
/**
33+
@brief This is the totp info for the second factor.
34+
*/
35+
@property(nonatomic, readonly, nullable) NSObject *TOTPInfo;
36+
37+
/**
38+
@fn initWithProto:
39+
@brief Initilize the FIRAuthProtoMFAEnrollment instance with proto.
40+
@param proto FIRAuthProtoMFAEnrollment proto object.
41+
*/
42+
- (instancetype)initWithProto:(FIRAuthProtoMFAEnrollment *)proto;
43+
44+
@end
45+
46+
NS_ASSUME_NONNULL_END
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <TargetConditionals.h>
18+
#if TARGET_OS_IOS
19+
20+
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRMultiFactorInfo.h"
21+
22+
#import "FirebaseAuth/Sources/Backend/RPC/Proto/FIRAuthProtoMFAEnrollment.h"
23+
#import "FirebaseAuth/Sources/MultiFactor/FIRMultiFactorInfo+Internal.h"
24+
#import "FirebaseAuth/Sources/MultiFactor/TOTP/FIRTOTPMultiFactorInfo.h"
25+
26+
extern NSString *const FIRTOTPMultiFactorID;
27+
28+
@implementation FIRTOTPMultiFactorInfo
29+
30+
- (instancetype)initWithProto:(FIRAuthProtoMFAEnrollment *)proto {
31+
self = [super initWithProto:proto];
32+
if (self) {
33+
_factorID = FIRTOTPMultiFactorID;
34+
_TOTPInfo = proto.TOTPInfo;
35+
}
36+
return self;
37+
}
38+
39+
@end
40+
41+
#endif

FirebaseAuth/Sources/Public/FirebaseAuth/FIRTOTPMultiFactorGenerator.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ NS_SWIFT_NAME(TOTPMultiFactorGenerator) API_UNAVAILABLE(macos, tvos, watchos)
5353
+ (FIRTOTPMultiFactorAssertion *)assertionForEnrollmentWithSecret:(FIRTOTPSecret *)secret
5454
oneTimePassword:(NSString *)oneTimePassword;
5555

56+
/**
57+
@fn assertionForSignInWithenrollmentID:
58+
@brief Initializes the MFA assertion to confirm ownership of the totp second factor. This
59+
assertion is used to complete signIn with TOTP as a second factor.
60+
@param enrollmentID The id that identifies the enrolled TOTP second factor.
61+
@param oneTimePassword one time password string.
62+
*/
63+
+ (FIRTOTPMultiFactorAssertion *)assertionForSignInWithEnrollmentID:(NSString *)enrollmentID
64+
oneTimePassword:(NSString *)oneTimePassword;
65+
5666
@end
5767

5868
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)