Skip to content

Commit 768c405

Browse files
authored
Adds reCAPTCHA Token (#211)
* Adds reCAPTCHA Token to the "send verification code" request.
1 parent 6e12c50 commit 768c405

11 files changed

+115
-20
lines changed

Example/Auth/Tests/FIRAuthBackendRPCImplementationTests.m

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -747,23 +747,7 @@ - (void)testCaptchaCheckFailedResponse {
747747

748748
XCTAssertNotNil(callbackError);
749749
XCTAssertEqualObjects(callbackError.domain, FIRAuthErrorDomain);
750-
XCTAssertEqual(callbackError.code, FIRAuthErrorCodeInternalError);
751-
752-
NSError *underlyingError = callbackError.userInfo[NSUnderlyingErrorKey];
753-
XCTAssertNotNil(underlyingError);
754-
XCTAssertEqualObjects(underlyingError.domain, FIRAuthInternalErrorDomain);
755-
XCTAssertEqual(underlyingError.code, FIRAuthInternalErrorCodeUnexpectedErrorResponse);
756-
757-
NSError *underlyingUnderlyingError = underlyingError.userInfo[NSUnderlyingErrorKey];
758-
XCTAssertNil(underlyingUnderlyingError);
759-
760-
id deserializedResponse = underlyingError.userInfo[FIRAuthErrorUserInfoDeserializedResponseKey];
761-
XCTAssertNotNil(deserializedResponse);
762-
XCTAssert([deserializedResponse isKindOfClass:[NSDictionary class]]);
763-
XCTAssertNotNil(deserializedResponse[@"message"]);
764-
765-
id dataResponse = underlyingError.userInfo[FIRAuthErrorUserInfoDataKey];
766-
XCTAssertNil(dataResponse);
750+
XCTAssertEqual(callbackError.code, FIRAuthErrorCodeCaptchaCheckFailed);
767751
}
768752

769753
/** @fn testCaptchaRequiredInvalidPasswordResponse

Example/Auth/Tests/FIRSendVerificationCodeRequestTests.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
*/
4343
static NSString *const kTestReceipt = @"receipt";
4444

45+
/** @var kTestReCAPTCHAToken
46+
@brief Fake reCAPTCHA token used for testing.
47+
*/
48+
static NSString *const kTestReCAPTCHAToken = @"reCAPTCHAToken";
49+
4550
/** @var kPhoneNumberKey
4651
@brief The key for the "phone number" value in the request.
4752
*/
@@ -101,10 +106,12 @@ - (void)testSendVerificationCodeRequest {
101106
FIRSendVerificationCodeRequest *request =
102107
[[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:kTestPhoneNumber
103108
appCredential:credential
109+
reCAPTCHAToken:kTestReCAPTCHAToken
104110
requestConfiguration:requestConfiguration];
105111
XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
106112
XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
107113
XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
114+
XCTAssertEqualObjects(request.reCAPTCHAToken, kTestReCAPTCHAToken);
108115

109116
[FIRAuthBackend sendVerificationCode:request
110117
callback:^(FIRSendVerificationCodeResponse *_Nullable response,

Example/Auth/Tests/FIRSendVerificationCodeResponseTests.m

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
*/
6060
static NSString *const kTestReceipt = @"receipt";
6161

62+
/** @var kTestReCAPTCHAToken
63+
@brief Fake reCAPTCHA token used for testing.
64+
*/
65+
static NSString *const kTestReCAPTCHAToken = @"reCAPTCHAToken";
66+
6267
/** @var kInvalidPhoneNumberErrorMessage
6368
@brief This is the error message the server will respond with if an incorrectly formatted phone
6469
number is provided.
@@ -77,6 +82,12 @@
7782
*/
7883
static NSString *const kAppNotVerifiedErrorMessage = @"APP_NOT_VERIFIED";
7984

85+
/** @var kCaptchaCheckFailedErrorMessage
86+
@brief This is the error message the server will respond with if the reCAPTCHA token provided is
87+
invalid.
88+
*/
89+
static NSString *const kCaptchaCheckFailedErrorMessage = @"CAPTCHA_CHECK_FAILED";
90+
8091
/** @class FIRSendVerificationCodeResponseTests
8192
@brief Tests for @c FIRSendVerificationCodeResponseTests.
8293
*/
@@ -120,6 +131,7 @@ - (void)testSendVerificationCodeResponseInvalidPhoneNumber {
120131
FIRSendVerificationCodeRequest *request =
121132
[[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:kTestInvalidPhoneNumber
122133
appCredential:credential
134+
reCAPTCHAToken:nil
123135
requestConfiguration:_requestConfiguration];
124136
__block BOOL callbackInvoked;
125137
__block FIRSendVerificationCodeResponse *RPCResponse;
@@ -148,6 +160,7 @@ - (void)testSendVerificationCodeResponseQuotaExceededError {
148160
FIRSendVerificationCodeRequest *request =
149161
[[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:kTestPhoneNumber
150162
appCredential:credential
163+
reCAPTCHAToken:nil
151164
requestConfiguration:_requestConfiguration];
152165
__block BOOL callbackInvoked;
153166
__block FIRSendVerificationCodeResponse *RPCResponse;
@@ -177,6 +190,7 @@ - (void)testSendVerificationCodeResponseAppNotVerifiedError {
177190
FIRSendVerificationCodeRequest *request =
178191
[[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:kTestPhoneNumber
179192
appCredential:credential
193+
reCAPTCHAToken:nil
180194
requestConfiguration:_requestConfiguration];
181195
__block BOOL callbackInvoked;
182196
__block FIRSendVerificationCodeResponse *RPCResponse;
@@ -196,6 +210,36 @@ - (void)testSendVerificationCodeResponseAppNotVerifiedError {
196210
XCTAssertEqual(RPCError.code, FIRAuthErrorCodeAppNotVerified);
197211
}
198212

213+
/** @fn testSendVerificationCodeResponseCaptchaCheckFailedError
214+
@brief Tests a failed attempt to send a verification code due to an invalid reCAPTCHA token
215+
being provided in the request.
216+
*/
217+
- (void)testSendVerificationCodeResponseCaptchaCheckFailedError {
218+
FIRAuthAppCredential *credential =
219+
[[FIRAuthAppCredential alloc]initWithReceipt:kTestReceipt secret:kTestSecret];
220+
FIRSendVerificationCodeRequest *request =
221+
[[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:kTestPhoneNumber
222+
appCredential:credential
223+
reCAPTCHAToken:kTestReCAPTCHAToken
224+
requestConfiguration:_requestConfiguration];
225+
__block BOOL callbackInvoked;
226+
__block FIRSendVerificationCodeResponse *RPCResponse;
227+
__block NSError *RPCError;
228+
[FIRAuthBackend sendVerificationCode:request
229+
callback:^(FIRSendVerificationCodeResponse *_Nullable response,
230+
NSError *_Nullable error) {
231+
RPCResponse = response;
232+
RPCError = error;
233+
callbackInvoked = YES;
234+
}];
235+
236+
[_RPCIssuer respondWithServerErrorMessage:kCaptchaCheckFailedErrorMessage];
237+
238+
XCTAssert(callbackInvoked);
239+
XCTAssertNil(RPCResponse);
240+
XCTAssertEqual(RPCError.code, FIRAuthErrorCodeCaptchaCheckFailed);
241+
}
242+
199243
/** @fn testSuccessfulSendVerificationCodeResponse
200244
@brief Tests a succesful to send a verification code.
201245
*/
@@ -205,6 +249,7 @@ - (void)testSuccessfulSendVerificationCodeResponse {
205249
FIRSendVerificationCodeRequest *request =
206250
[[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:kTestPhoneNumber
207251
appCredential:credential
252+
reCAPTCHAToken:nil
208253
requestConfiguration:_requestConfiguration];
209254
__block BOOL callbackInvoked;
210255
__block FIRSendVerificationCodeResponse *RPCResponse;

Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ - (void)verifyClientAndSendVerificationCodeToPhoneNumber:(NSString *)phoneNumber
132132
FIRSendVerificationCodeRequest *request =
133133
[[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:phoneNumber
134134
appCredential:appCredential
135+
reCAPTCHAToken:nil
135136
requestConfiguration:_auth.requestConfiguration];
136137
[FIRAuthBackend sendVerificationCode:request
137138
callback:^(FIRSendVerificationCodeResponse *_Nullable response,

Firebase/Auth/Source/FIRAuthErrorUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,13 @@ NS_ASSUME_NONNULL_BEGIN
444444
*/
445445
+ (NSError *)appNotVerifiedErrorWithMessage:(nullable NSString *)message;
446446

447+
/** @fn captchaCheckFailedErrorWithMessage:
448+
@brief Constructs an @c NSError with the @c FIRAuthErrorCaptchaCheckFailed code.
449+
@param message Error message from the backend, if any.
450+
@return The NSError instance associated with the given FIRAuthError.
451+
*/
452+
+ (NSError *)captchaCheckFailedErrorWithMessage:(nullable NSString *)message;
453+
447454
/** @fn keychainErrorWithFunction:status:
448455
@brief Constructs an @c NSError with the @c FIRAuthErrorCodeKeychainError code.
449456
@param keychainFunction The keychain function which was invoked and yielded an unexpected

Firebase/Auth/Source/FIRAuthErrorUtils.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@
339339
"silent push notification and therefore could not verify your app. Ensure that you configured "
340340
"your app correctly to recieve push notifications.";
341341

342+
/** @var kFIRAuthErrorMessageCaptchaCheckFailed
343+
@brief Message for @c FIRAuthErrorCodeCaptchaCheckFailed error code.
344+
*/
345+
static NSString *const kFIRAuthErrorMessageCaptchaCheckFailed = @"The reCAPTCHA response token "
346+
"provided is either invalid, expired or already";
347+
342348
/** @var kFIRAuthErrorMessageInternalError
343349
@brief Message for @c FIRAuthErrorCodeInternalError error code.
344350
*/
@@ -447,6 +453,8 @@
447453
return kFIRAuthErrorMessageNotificationNotForwarded;
448454
case FIRAuthErrorCodeAppNotVerified:
449455
return kFIRAuthErrorMessageAppNotVerified;
456+
case FIRAuthErrorCodeCaptchaCheckFailed:
457+
return kFIRAuthErrorMessageCaptchaCheckFailed;
450458
}
451459
}
452460

@@ -552,6 +560,8 @@
552560
return @"ERROR_NOTIFICATION_NOT_FORWARDED";
553561
case FIRAuthErrorCodeAppNotVerified:
554562
return @"ERROR_APP_NOT_VERIFIED";
563+
case FIRAuthErrorCodeCaptchaCheckFailed:
564+
return @"ERROR_CAPTCHA_CHECK_FAILED";
555565
}
556566
}
557567

@@ -859,6 +869,10 @@ + (NSError *)appNotVerifiedErrorWithMessage:(nullable NSString *)message {
859869
return [self errorWithCode:FIRAuthInternalErrorCodeAppNotVerified message:message];
860870
}
861871

872+
+ (NSError *)captchaCheckFailedErrorWithMessage:(nullable NSString *)message {
873+
return [self errorWithCode:FIRAuthInternalErrorCodeCaptchaCheckFailed message:message];
874+
}
875+
862876
+ (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status {
863877
NSString *failureReason = [NSString stringWithFormat:@"%@ (%li)", keychainFunction, (long)status];
864878
return [self errorWithCode:FIRAuthInternalErrorCodeKeychainError userInfo:@{

Firebase/Auth/Source/FIRAuthInternalErrors.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,11 @@ typedef NS_ENUM(NSInteger, FIRAuthInternalErrorCode) {
307307
FIRAuthInternalErrorCodeInvalidAppCredential =
308308
FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidAppCredential,
309309

310+
/** Indicates that the reCAPTCHA token is not valid.
311+
*/
312+
FIRAuthInternalErrorCodeCaptchaCheckFailed =
313+
FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeCaptchaCheckFailed,
314+
310315
/** Indicates that an invalid verification ID was used in the verifyPhoneNumber request.
311316
*/
312317
FIRAuthInternalErrorCodeInvalidVerificationID =

Firebase/Auth/Source/Public/FIRAuthErrors.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ typedef NS_ENUM(NSInteger, FIRAuthErrorCode) {
272272
*/
273273
FIRAuthErrorCodeAppNotVerified = 17055,
274274

275+
/** Indicates that the reCAPTCHA token is not valid.
276+
*/
277+
FIRAuthErrorCodeCaptchaCheckFailed = 17056,
278+
275279
/** Indicates an error occurred while attempting to access the keychain.
276280
*/
277281
FIRAuthErrorCodeKeychainError = 17995,

Firebase/Auth/Source/RPCs/FIRAuthBackend.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,12 @@
341341
*/
342342
static NSString *const kAppNotVerifiedErrorMessage = @"APP_NOT_VERIFIED";
343343

344+
/** @var kCaptchaCheckFailedErrorMessage
345+
@brief This is the error message the server will respond with if the reCAPTCHA token provided is
346+
invalid.
347+
*/
348+
static NSString *const kCaptchaCheckFailedErrorMessage = @"CAPTCHA_CHECK_FAILED";
349+
344350
/** @var gBackendImplementation
345351
@brief The singleton FIRAuthBackendImplementation instance to use.
346352
*/
@@ -1014,6 +1020,10 @@ + (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorM
10141020
return [FIRAuthErrorUtils appNotVerifiedErrorWithMessage:serverErrorMessage];
10151021
}
10161022

1023+
if ([shortErrorMessage isEqualToString:kCaptchaCheckFailedErrorMessage]) {
1024+
return [FIRAuthErrorUtils captchaCheckFailedErrorWithMessage:serverErrorMessage];
1025+
}
1026+
10171027
// In this case we handle an error that might be specified in the underlying errors dictionary,
10181028
// the error message in determined based on the @c reason key in the dictionary.
10191029
if (errorDictionary[kErrorsKey]) {

Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ NS_ASSUME_NONNULL_BEGIN
3333
/** @property appCredential
3434
@brief The credential to prove the identity of the app in order to send the verification code.
3535
*/
36-
@property(nonatomic, strong, readonly) FIRAuthAppCredential *appCredential;
36+
@property(nonatomic, strong, readonly, nullable) FIRAuthAppCredential *appCredential;
37+
38+
/** @property reCAPTCHAToken
39+
@brief The reCAPTCHA token to prove the identity of the app in order to send the verification
40+
code.
41+
*/
42+
@property(nonatomic, strong, readonly, nullable) NSString *reCAPTCHAToken;
3743

3844
/** @fn initWithEndpoint:requestConfiguration:
3945
@brief Please use initWithPhoneNumber:appCredentials:requestConfiguration: instead.
@@ -46,10 +52,12 @@ NS_ASSUME_NONNULL_BEGIN
4652
@brief Designated initializer.
4753
@param phoneNumber The phone number to which the verification code is to be sent.
4854
@param appCredential The credential that proves the identity of the app.
55+
@param reCAPTCHAToken The reCAPTCHA token that proves the identity of the app.
4956
@param requestConfiguration An object containing configurations to be added to the request.
5057
*/
5158
- (nullable instancetype)initWithPhoneNumber:(NSString *)phoneNumber
52-
appCredential:(FIRAuthAppCredential *)appCredential
59+
appCredential:(nullable FIRAuthAppCredential *)appCredential
60+
reCAPTCHAToken:(nullable NSString *)reCAPTCHAToken
5361
requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration
5462
NS_DESIGNATED_INITIALIZER;
5563

Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.m

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,24 @@
4040
*/
4141
static NSString *const kSecretKey = @"iosSecret";
4242

43+
/** @var kreCAPTCHATokenKey
44+
@brief The key for the reCAPTCHAToken parameter in the request.
45+
*/
46+
static NSString *const kreCAPTCHATokenKey = @"recaptchaToken";
47+
4348
@implementation FIRSendVerificationCodeRequest {
4449
}
4550

4651
- (nullable instancetype)initWithPhoneNumber:(NSString *)phoneNumber
47-
appCredential:(FIRAuthAppCredential *)appCredential
52+
appCredential:(nullable FIRAuthAppCredential *)appCredential
53+
reCAPTCHAToken:(nullable NSString *)reCAPTCHAToken
4854
requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration {
4955
self = [super initWithEndpoint:kSendVerificationCodeEndPoint
5056
requestConfiguration:requestConfiguration];
5157
if (self) {
5258
_phoneNumber = [phoneNumber copy];
5359
_appCredential = appCredential;
60+
_reCAPTCHAToken = [reCAPTCHAToken copy];
5461
}
5562
return self;
5663
}
@@ -66,6 +73,9 @@ - (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)
6673
if (_appCredential.secret) {
6774
postBody[kSecretKey] = _appCredential.secret;
6875
}
76+
if (_reCAPTCHAToken) {
77+
postBody[kreCAPTCHATokenKey] = _reCAPTCHAToken;
78+
}
6979
return postBody;
7080
}
7181

0 commit comments

Comments
 (0)