aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Xiangtian Dai <xiangtian@google.com>2017-10-02 14:15:05 -0700
committerGravatar GitHub <noreply@github.com>2017-10-02 14:15:05 -0700
commitbf550507ffa8beee149383a5bf1e2363bccefbb4 (patch)
tree234ac2ff5df20d8c54c7cd89b590791f3429a25b
parent7fa0b0de42141d90b8ba3da67cad037becf6f065 (diff)
Automatically signs user out if the token is no longer valid. (#323)
-rw-r--r--Example/Auth/Tests/FIRUserTests.m279
-rw-r--r--Firebase/Auth/Source/FIRAuth.m7
-rw-r--r--Firebase/Auth/Source/FIRUser.m37
-rw-r--r--Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m2
4 files changed, 308 insertions, 17 deletions
diff --git a/Example/Auth/Tests/FIRUserTests.m b/Example/Auth/Tests/FIRUserTests.m
index 3384885..820ade1 100644
--- a/Example/Auth/Tests/FIRUserTests.m
+++ b/Example/Auth/Tests/FIRUserTests.m
@@ -506,9 +506,41 @@ static const NSTimeInterval kExpectationTimeout = 1;
OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils invalidEmailErrorWithMessage:nil]);
[user updateEmail:kNewEmail completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidEmail);
// Email should not have changed on the client side.
XCTAssertEqualObjects(user.email, kEmail);
+ // User is still signed in.
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testUpdateEmailAutoSignOut
+ @brief Tests the flow of a failed @c updateEmail:completion: call that automatically signs out.
+ */
+- (void)testUpdateEmailAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ [self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
+ OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils invalidUserTokenErrorWithMessage:nil]);
+ [user updateEmail:kNewEmail completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidUserToken);
+ // Email should not have changed on the client side.
+ XCTAssertEqualObjects(user.email, kEmail);
+ // User is no longer signed in.
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -545,6 +577,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
verificationCode:kVerificationCode];
[user updatePhoneNumberCredential:credential
completion:^(NSError * _Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects([FIRAuth auth].currentUser.phoneNumber, kPhoneNumber);
[expectation fulfill];
@@ -572,7 +605,38 @@ static const NSTimeInterval kExpectationTimeout = 1;
[[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID
verificationCode:kVerificationCode];
[user updatePhoneNumberCredential:credential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidPhoneNumber);
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testUpdatePhoneNumberFailureAutoSignOut
+ @brief Tests the flow of a failed @c updatePhoneNumberCredential:completion: call that
+ automatically signs out.
+ */
+- (void)testUpdatePhoneNumberFailureAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ OCMExpect([_mockBackend verifyPhoneNumber:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils userTokenExpiredErrorWithMessage:nil]);
+ FIRPhoneAuthCredential *credential =
+ [[FIRPhoneAuthProvider provider] credentialWithVerificationID:kVerificationID
+ verificationCode:kVerificationCode];
+ [user updatePhoneNumberCredential:credential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserTokenExpired);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -614,6 +678,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
});
});
[user updatePassword:kNewPassword completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertFalse(user.isAnonymous);
[expectation fulfill];
@@ -637,9 +702,11 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRUser *user) {
[self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
- .andDispatchError2([FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ .andDispatchError2([FIRAuthErrorUtils requiresRecentLoginErrorWithMessage:nil]);
[user updatePassword:kNewPassword completion:^(NSError *_Nullable error) {
- XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeRequiresRecentLogin);
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
[expectation fulfill];
}];
}];
@@ -660,6 +727,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
[self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
completion:^(FIRUser *user) {
[user updatePassword:@"" completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeWeakPassword);
[expectation fulfill];
}];
@@ -667,6 +735,33 @@ static const NSTimeInterval kExpectationTimeout = 1;
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}
+/** @fn testUpdatePasswordFailureAutoSignOut
+ @brief Tests the flow of a failed @c updatePassword:completion: call that automatically signs
+ out.
+ */
+- (void)testUpdatePasswordFailureAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ [self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
+ OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ [user updatePassword:kNewPassword completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertNil([FIRAuth auth].currentUser);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
/** @fn testChangeProfileSuccess
@brief Tests a successful user profile change flow.
*/
@@ -704,6 +799,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
profileChange.photoURL = [NSURL URLWithString:kNewPhotoURL];
profileChange.displayName = kNewDisplayName;
[profileChange commitChangesWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(user.displayName, kNewDisplayName);
XCTAssertEqualObjects(user.photoURL, [NSURL URLWithString:kNewPhotoURL]);
@@ -732,8 +828,39 @@ static const NSTimeInterval kExpectationTimeout = 1;
FIRUserProfileChangeRequest *profileChange = [user profileChangeRequest];
profileChange.displayName = kNewDisplayName;
[profileChange commitChangesWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests);
XCTAssertEqualObjects(user.displayName, kGoogleDisplayName);
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testChangeProfileFailureAutoSignOut
+ @brief Tests a failed user profile change flow that automatically signs out.
+ */
+- (void)testChangeProfileFailureAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(kGoogleDisplayName);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ [self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUser];
+ OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils userNotFoundErrorWithMessage:nil]);
+ FIRUserProfileChangeRequest *profileChange = [user profileChangeRequest];
+ profileChange.displayName = kNewDisplayName;
+ [profileChange commitChangesWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserNotFound);
+ XCTAssertEqualObjects(user.displayName, kGoogleDisplayName);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -760,6 +887,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
OCMStub([mockGetAccountInfoResponseUserNew passwordHash]).andReturn(kPasswordHash);
[self expectGetAccountInfoWithMockUserInfoResponse:mockGetAccountInfoResponseUserNew];
[user reloadWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(user.email, kNewEmail);
XCTAssertEqualObjects(user.displayName, kNewDisplayName);
@@ -782,9 +910,35 @@ static const NSTimeInterval kExpectationTimeout = 1;
[self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
completion:^(FIRUser *user) {
OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils quotaExceededErrorWithMessage:nil]);
+ [user reloadWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeQuotaExceeded);
+ XCTAssertEqual([FIRAuth auth].currentUser, user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testReloadFailureAutoSignOut
+ @brief Tests the flow of a failed @c reloadWithCompletion: call that automtatically signs out.
+ */
+- (void)testReloadFailureAutoSignOut {
+ id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]);
+ OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID);
+ OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail);
+ OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [self signInWithEmailPasswordWithMockUserInfoResponse:mockGetAccountInfoResponseUser
+ completion:^(FIRUser *user) {
+ OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils userTokenExpiredErrorWithMessage:nil]);
[user reloadWithCompletion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertEqual(error.code, FIRAuthErrorCodeUserTokenExpired);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -832,6 +986,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[user reauthenticateWithCredential:emailCredential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
// Verify that the current user is unchanged.
XCTAssertEqual([FIRAuth auth].currentUser, user);
@@ -882,6 +1037,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
reauthenticateAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
// Verify that the current user is unchanged.
XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
@@ -946,6 +1102,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
FIRAuthCredential *emailCredential =
[FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
[user reauthenticateWithCredential:emailCredential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
// Verify user mismatch error.
XCTAssertEqual(error.code, FIRAuthErrorCodeUserMismatch);
// Verify that the current user is unchanged.
@@ -980,6 +1137,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
FIRAuthCredential *googleCredential =
[FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
[user reauthenticateWithCredential:googleCredential completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
// Verify user mismatch error.
XCTAssertEqual(error.code, FIRAuthErrorCodeUserMismatch);
// Verify that the current user is unchanged.
@@ -1030,6 +1188,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
// Verify that the current user is unchanged.
XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
@@ -1081,7 +1240,8 @@ static const NSTimeInterval kExpectationTimeout = 1;
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
- callback(nil, [FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ callback(nil,
+ [FIRAuthErrorUtils accountExistsWithDifferentCredentialErrorWithEmail:kEmail]);
});
});
@@ -1091,8 +1251,11 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
- XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeAccountExistsWithDifferentCredential);
+ XCTAssertEqual(error.userInfo[FIRAuthErrorUserInfoEmailKey], kEmail);
+ XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
[expectation fulfill];
}];
}];
@@ -1131,8 +1294,60 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked);
+ XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testlinkAndRetrieveDataErrorAutoSignOut
+ @brief Tests the flow of an unsuccessful @c linkAndRetrieveDataWithCredential:completion:
+ call that automatically signs out.
+ */
+- (void)testlinkAndRetrieveDataErrorAutoSignOut {
+ [self expectVerifyAssertionRequest:FIRFacebookAuthProviderID
+ federatedID:kFacebookID
+ displayName:kFacebookDisplayName
+ profile:[[self class] googleProfile]
+ providerIDToken:kFacebookIDToken
+ providerAccessToken:kFacebookAccessToken];
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] signOut:NULL];
+ FIRAuthCredential *facebookCredential =
+ [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken];
+ [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential
+ completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ [self assertUserFacebook:authResult.user];
+ XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]);
+ XCTAssertEqualObjects(authResult.additionalUserInfo.username, kUserName);
+ XCTAssertEqualObjects(authResult.additionalUserInfo.providerID, FIRFacebookAuthProviderID);
+ XCTAssertNil(error);
+
+ OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
+ FIRVerifyAssertionResponseCallback callback) {
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ callback(nil, [FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ });
+ });
+
+ FIRAuthCredential *linkGoogleCredential =
+ [FIRGoogleAuthProvider credentialWithIDToken:kGoogleIDToken accessToken:kGoogleAccessToken];
+ [authResult.user linkAndRetrieveDataWithCredential:linkGoogleCredential
+ completion:^(FIRAuthDataResult *_Nullable
+ linkAuthResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(linkAuthResult);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
@@ -1195,6 +1410,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(error);
XCTAssertEqualObjects(linkAuthResult.user.email, kEmail);
XCTAssertEqualObjects(linkAuthResult.user.displayName, kEmailDisplayName);
@@ -1271,6 +1487,7 @@ static const NSTimeInterval kExpectationTimeout = 1;
completion:^(FIRAuthDataResult *_Nullable
linkAuthResult,
NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeProviderAlreadyLinked);
[expectation fulfill];
@@ -1327,6 +1544,60 @@ static const NSTimeInterval kExpectationTimeout = 1;
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(linkAuthResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests);
+ XCTAssertEqual([FIRAuth auth].currentUser, authResult.user);
+ [expectation fulfill];
+ }];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testlinkEmailAndRetrieveDataError
+ @brief Tests the flow of an unsuccessful @c linkAndRetrieveDataWithCredential:completion:
+ invocation that automatically signs out.
+ */
+- (void)testlinkEmailAndRetrieveDataErrorAutoSignOut {
+ [self expectVerifyAssertionRequest:FIRFacebookAuthProviderID
+ federatedID:kFacebookID
+ displayName:kFacebookDisplayName
+ profile:[[self class] googleProfile]
+ providerIDToken:kFacebookIDToken
+ providerAccessToken:kFacebookAccessToken];
+
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] signOut:NULL];
+ FIRAuthCredential *facebookCredential =
+ [FIRFacebookAuthProvider credentialWithAccessToken:kFacebookAccessToken];
+ [[FIRAuth auth] signInAndRetrieveDataWithCredential:facebookCredential
+ completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ [self assertUserFacebook:authResult.user];
+ XCTAssertEqualObjects(authResult.additionalUserInfo.profile, [[self class] googleProfile]);
+ XCTAssertEqualObjects(authResult.additionalUserInfo.username, kUserName);
+ XCTAssertEqualObjects(authResult.additionalUserInfo.providerID, FIRFacebookAuthProviderID);
+ XCTAssertNil(error);
+
+ OCMExpect([_mockBackend getAccountInfo:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIRGetAccountInfoRequest *_Nullable request,
+ FIRGetAccountInfoResponseCallback callback) {
+ XCTAssertEqualObjects(request.APIKey, kAPIKey);
+ XCTAssertEqualObjects(request.accessToken, kAccessToken);
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ callback(nil, [FIRAuthErrorUtils userTokenExpiredErrorWithMessage:nil]);
+ });
+ });
+
+ FIRAuthCredential *linkEmailCredential =
+ [FIREmailAuthProvider credentialWithEmail:kEmail password:kFakePassword];
+ [authResult.user linkAndRetrieveDataWithCredential:linkEmailCredential
+ completion:^(FIRAuthDataResult *_Nullable
+ linkAuthResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(linkAuthResult);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserTokenExpired);
+ XCTAssertNil([FIRAuth auth].currentUser);
[expectation fulfill];
}];
}];
diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m
index f0a069c..245e600 100644
--- a/Firebase/Auth/Source/FIRAuth.m
+++ b/Firebase/Auth/Source/FIRAuth.m
@@ -1207,13 +1207,6 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
if (![strongSelf->_currentUser.uid isEqualToString:uid]) {
return;
}
- // If the error is an invalid token, sign the user out.
- if (error.code == FIRAuthErrorCodeInvalidUserToken) {
- FIRLogNotice(kFIRLoggerAuth, @"I-AUT000005",
- @"Invalid refresh token detected, user is automatically signed out.");
- [strongSelf signOutByForceWithUserID:uid error:nil];
- return;
- }
if (error) {
// Kicks off exponential back off logic to retry failed attempt. Starts with one minute
// delay (60 seconds) if this is the first failed attempt.
diff --git a/Firebase/Auth/Source/FIRUser.m b/Firebase/Auth/Source/FIRUser.m
index ef42a29..c4396ad 100644
--- a/Firebase/Auth/Source/FIRUser.m
+++ b/Firebase/Auth/Source/FIRUser.m
@@ -18,8 +18,6 @@
#import "FIRUser_Internal.h"
-#import "AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h"
-#import "FIREmailAuthProvider.h"
#import "FIRAdditionalUserInfo_Internal.h"
#import "FIRAuth.h"
#import "FIRAuthCredential_Internal.h"
@@ -29,18 +27,21 @@
#import "FIRAuthSerialTaskQueue.h"
#import "FIRAuthOperationType.h"
#import "FIRAuth_Internal.h"
-#import "FIRSecureTokenService.h"
-#import "FIRUserInfoImpl.h"
#import "FIRAuthBackend.h"
#import "FIRAuthRequestConfiguration.h"
#import "FIRDeleteAccountRequest.h"
#import "FIRDeleteAccountResponse.h"
+#import "FIREmailAuthProvider.h"
+#import "FIREmailPasswordAuthCredential.h"
#import "FIRGetAccountInfoRequest.h"
#import "FIRGetAccountInfoResponse.h"
#import "FIRGetOOBConfirmationCodeRequest.h"
#import "FIRGetOOBConfirmationCodeResponse.h"
+#import "FIRLogger.h"
+#import "FIRSecureTokenService.h"
#import "FIRSetAccountInfoRequest.h"
#import "FIRSetAccountInfoResponse.h"
+#import "FIRUserInfoImpl.h"
#import "FIRUserMetadata_Internal.h"
#import "FIRVerifyAssertionRequest.h"
#import "FIRVerifyAssertionResponse.h"
@@ -261,6 +262,7 @@ static void callInMainThreadWithAuthDataResultAndError(
callback:^(FIRGetAccountInfoResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
+ // No need to sign out user here for errors because the user hasn't been signed in yet.
callback(nil, error);
return;
}
@@ -386,6 +388,7 @@ static void callInMainThreadWithAuthDataResultAndError(
callback:^(FIRGetAccountInfoResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
+ [self signOutIfTokenIsInvalidWithError:error];
callback(nil, error);
return;
}
@@ -459,6 +462,7 @@ static void callInMainThreadWithAuthDataResultAndError(
callback:^(FIRSetAccountInfoResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
+ [self signOutIfTokenIsInvalidWithError:error];
complete();
callback(error);
return;
@@ -576,6 +580,7 @@ static void callInMainThreadWithAuthDataResultAndError(
callback:^(FIRGetAccountInfoResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
+ [self signOutIfTokenIsInvalidWithError:error];
callback(error);
return;
}
@@ -648,7 +653,8 @@ static void callInMainThreadWithAuthDataResultAndError(
callback:^(FIRVerifyPhoneNumberResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
- completion(error);;
+ [self signOutIfTokenIsInvalidWithError:error];
+ completion(error);
return;
}
// Get account info to update cached user info.
@@ -805,6 +811,7 @@ static void callInMainThreadWithAuthDataResultAndError(
NSError *_Nullable error,
BOOL tokenUpdated) {
if (error) {
+ [self signOutIfTokenIsInvalidWithError:error];
callback(nil, error);
return;
}
@@ -897,6 +904,7 @@ static void callInMainThreadWithAuthDataResultAndError(
[FIRAuthBackend verifyAssertion:request
callback:^(FIRVerifyAssertionResponse *response, NSError *error) {
if (error) {
+ [self signOutIfTokenIsInvalidWithError:error];
completeWithError(nil, error);
return;
}
@@ -923,6 +931,7 @@ static void callInMainThreadWithAuthDataResultAndError(
callback:^(FIRGetAccountInfoResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
+ [self signOutIfTokenIsInvalidWithError:error];
completeWithError(nil, error);
return;
}
@@ -976,6 +985,7 @@ static void callInMainThreadWithAuthDataResultAndError(
callback:^(FIRSetAccountInfoResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
+ [self signOutIfTokenIsInvalidWithError:error];
completeAndCallbackWithError(error);
return;
}
@@ -1055,6 +1065,7 @@ static void callInMainThreadWithAuthDataResultAndError(
callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable
response,
NSError *_Nullable error) {
+ [self signOutIfTokenIsInvalidWithError:error];
callInMainThreadWithError(completion, error);
}];
}];
@@ -1088,6 +1099,22 @@ static void callInMainThreadWithAuthDataResultAndError(
});
}
+/** @fn signOutIfTokenIsInvalidWithError:
+ @brief Signs out this user if the user or the token is invalid.
+ @param error The error from the server.
+ */
+- (void)signOutIfTokenIsInvalidWithError:(nullable NSError *)error {
+ NSInteger errorCode = error.code;
+ if (errorCode == FIRAuthErrorCodeUserNotFound ||
+ errorCode == FIRAuthErrorCodeUserDisabled ||
+ errorCode == FIRAuthErrorCodeInvalidUserToken ||
+ errorCode == FIRAuthErrorCodeUserTokenExpired) {
+ FIRLogNotice(kFIRLoggerAuth, @"I-AUT000016",
+ @"Invalid user token detected, user is automatically signed out.");
+ [_auth signOutByForceWithUserID:_userID error:NULL];
+ }
+}
+
@end
@implementation FIRUserProfileChangeRequest {
diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m
index 8af4c4e..022ab9e 100644
--- a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m
+++ b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m
@@ -86,7 +86,7 @@ static NSString *const kOperationKey = @"operation";
}
/** @fn FIRAuthOperationString
- @brief Returns a string object corresponding to the provided FIRAuthOperationType value.
+ @brief Returns a string object corresponding to the provided FIRAuthOperationType value.
@param operationType The value of the FIRAuthOperationType enum which will be translated to its
corresponding string value.
@return The string value corresponding to the FIRAuthOperationType argument.