diff options
-rw-r--r-- | Example/Auth/Sample/MainViewController.m | 45 | ||||
-rw-r--r-- | Example/Auth/Tests/FIRAuthTests.m | 60 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuth.m | 57 | ||||
-rw-r--r-- | Firebase/Auth/Source/Public/FIRAuth.h | 33 |
4 files changed, 187 insertions, 8 deletions
diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m index 5831d34..42f0a8e 100644 --- a/Example/Auth/Sample/MainViewController.m +++ b/Example/Auth/Sample/MainViewController.m @@ -106,6 +106,12 @@ static NSString *const kSignInFacebookAndRetrieveDataButtonText = */ static NSString *const kSignInEmailPasswordButtonText = @"Sign in with Email/Password"; +/** @var kSignInEmailPasswordAuthDataResultButtonText + @brief The text of the "Email/Password SignIn (AuthDataResult)" button. + */ +static NSString *const kSignInEmailPasswordAuthDataResultButtonText = + @"Sign in with Email/Password (AuthDataReult)"; + /** @var kSignInWithCustomTokenButtonText @brief The text of the "Sign In (BYOAuth)" button. */ @@ -711,6 +717,8 @@ typedef enum { action:^{ [weakSelf signInFacebookAndRetrieveData]; }], [StaticContentTableViewCell cellWithTitle:kSignInEmailPasswordButtonText action:^{ [weakSelf signInEmailPassword]; }], + [StaticContentTableViewCell cellWithTitle:kSignInEmailPasswordAuthDataResultButtonText + action:^{ [weakSelf signInEmailPasswordAuthDataResult]; }], [StaticContentTableViewCell cellWithTitle:kSignInWithCustomTokenButtonText action:^{ [weakSelf signInWithCustomToken]; }], [StaticContentTableViewCell cellWithTitle:kSignInAnonymouslyButtonText @@ -1650,15 +1658,48 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) { } FIRAuthCredential *credential = [FIREmailAuthProvider credentialWithEmail:email - password:password]; + password:password]; [self showSpinner:^{ [[AppManager auth] signInWithCredential:credential - completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { + completion:^(FIRUser *_Nullable user, + NSError *_Nullable error) { + [self hideSpinner:^{ + if (error) { + [self logFailure:@"sign-in with Email/Password failed" error:error]; + } else { + [self logSuccess:@"sign-in with Email/Password succeeded."]; + } + [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error]; + }]; + }]; + }]; + }]; + }]; +} + +- (void)signInEmailPasswordAuthDataResult { + [self showTextInputPromptWithMessage:@"Email Address:" + keyboardType:UIKeyboardTypeEmailAddress + completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) { + if (!userPressedOK || !email.length) { + return; + } + [self showTextInputPromptWithMessage:@"Password:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable password) { + if (!userPressedOK) { + return; + } + [self showSpinner:^{ + [[AppManager auth] signInAndRetrieveDataWithEmail:email + password:password + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { [self hideSpinner:^{ if (error) { [self logFailure:@"sign-in with Email/Password failed" error:error]; } else { [self logSuccess:@"sign-in with Email/Password succeeded."]; + [self log:[NSString stringWithFormat:@"UID: %@",authResult.user.uid]]; } [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error]; }]; diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m index 35c8362..770f2c7 100644 --- a/Example/Auth/Tests/FIRAuthTests.m +++ b/Example/Auth/Tests/FIRAuthTests.m @@ -548,6 +548,66 @@ static const NSTimeInterval kWaitInterval = .5; OCMVerifyAll(_mockBackend); } +/** @fn testSignInAndRetrieveDataWithEmailPasswordSuccess + @brief Tests the flow of a successful @c signInAndRetrieveDataWithEmail:password:completion: + call. + */ +- (void)testSignInAndRetrieveDataWithEmailPasswordSuccess { + OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request, + FIRVerifyPasswordResponseCallback callback) { + XCTAssertEqualObjects(request.APIKey, kAPIKey); + XCTAssertEqualObjects(request.email, kEmail); + XCTAssertEqualObjects(request.password, kFakePassword); + XCTAssertTrue(request.returnSecureToken); + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + id mockVerifyPasswordResponse = OCMClassMock([FIRVerifyPasswordResponse class]); + [self stubTokensWithMockResponse:mockVerifyPasswordResponse]; + callback(mockVerifyPasswordResponse, nil); + }); + }); + [self expectGetAccountInfo]; + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] signOut:NULL]; + [[FIRAuth auth] signInAndRetrieveDataWithEmail:kEmail + password:kFakePassword + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + [self assertUser:result.user]; + XCTAssertFalse(result.additionalUserInfo.isNewUser); + XCTAssertEqualObjects(result.additionalUserInfo.providerID, FIREmailAuthProviderID); + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + [self assertUser:[FIRAuth auth].currentUser]; + OCMVerifyAll(_mockBackend); +} + +/** @fn testSignInAndRetrieveDataWithEmailPasswordFailure + @brief Tests the flow of a failed @c signInAndRetrieveDataWithEmail:password:completion: call. + */ +- (void)testSignInAndRetrieveDataWithEmailPasswordFailure { + OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]]) + .andDispatchError2([FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]); + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [[FIRAuth auth] signOut:NULL]; + [[FIRAuth auth] signInAndRetrieveDataWithEmail:kEmail + password:kFakePassword + completion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNil(result); + XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword); + XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + XCTAssertNil([FIRAuth auth].currentUser); + OCMVerifyAll(_mockBackend); +} + /** @fn testResetPasswordSuccess @brief Tests the flow of a successful @c confirmPasswordResetWithCode:newPassword:completion: call. diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m index 9020d3f..a104ad8 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/FIRAuth.m @@ -512,9 +512,14 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; password:(NSString *)password completion:(FIRAuthResultCallback)completion { dispatch_async(FIRAuthGlobalWorkQueue(), ^{ - [self signInWithEmail:email - password:password - callback:[self signInFlowAuthResultCallbackByDecoratingCallback:completion]]; + FIRAuthResultCallback decoratedCallback = + [self signInFlowAuthResultCallbackByDecoratingCallback:completion]; + [self internalSignInAndRetrieveDataWithEmail:email + password:password + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + decoratedCallback(authResult.user, error); + }]; }); } @@ -554,6 +559,37 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; }]; } +- (void)signInAndRetrieveDataWithEmail:(NSString *)email + password:(NSString *)password + completion:(FIRAuthDataResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRAuthDataResultCallback decoratedCallback = + [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion]; + [self internalSignInAndRetrieveDataWithEmail:email + password:password + completion:decoratedCallback]; + }); +} + +/** @fn internalSignInAndRetrieveDataWithEmail:password:callback: + @brief Signs in using an email address and password. + @param email The user's email address. + @param password The user's password. + @param completion A block which is invoked when the sign in finishes (or is cancelled.) Invoked + asynchronously on the global auth work queue in the future. + @remarks This is the internal counterpart of this method, which uses a callback that does not + update the current user. + */ +- (void)internalSignInAndRetrieveDataWithEmail:(NSString *)email + password:(NSString *)password + completion:(FIRAuthDataResultCallback)completion { + FIREmailPasswordAuthCredential *credentail = + [[FIREmailPasswordAuthCredential alloc] initWithEmail:email password:password]; + [self internalSignInAndRetrieveDataWithCredential:credentail + isReauthentication:NO + callback:completion]; +} + - (void)signInWithCredential:(FIRAuthCredential *)credential completion:(FIRAuthResultCallback)completion { dispatch_async(FIRAuthGlobalWorkQueue(), ^{ @@ -595,9 +631,18 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; password:emailPasswordCredential.password callback:^(FIRUser *_Nullable user, NSError *_Nullable error) { if (callback) { - FIRAuthDataResult *result = user ? - [[FIRAuthDataResult alloc] initWithUser:user additionalUserInfo:nil] : nil; - callback(result, error); + if (error) { + callback(nil, error); + return; + } + FIRAdditionalUserInfo *additionalUserInfo = + [[FIRAdditionalUserInfo alloc] initWithProviderID:FIREmailAuthProviderID + profile:nil + username:nil + isNewUser:NO]; + FIRAuthDataResult *result = [[FIRAuthDataResult alloc] initWithUser:user + additionalUserInfo:additionalUserInfo]; + callback(result, nil); } }]; return; diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h index 1aa4bcf..9a115ee 100644 --- a/Firebase/Auth/Source/Public/FIRAuth.h +++ b/Firebase/Auth/Source/Public/FIRAuth.h @@ -329,6 +329,39 @@ FIR_SWIFT_NAME(Auth) password:(NSString *)password completion:(nullable FIRAuthResultCallback)completion; +/** @fn signInAndRetrieveDataWithEmail:password:completion: + @brief Signs in using an email address and password. + + @param email The user's email address. + @param password The user's password. + @param completion Optionally; a block which is invoked when the sign in flow finishes, or is + canceled. Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + + <ul> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates that email and password + accounts are not enabled. Enable them in the Auth section of the + Firebase console. + </li> + <li>@c FIRAuthErrorCodeUserDisabled - Indicates the user's account is disabled. + </li> + <li>@c FIRAuthErrorCodeWrongPassword - Indicates the user attempted + sign in with an incorrect password. + </li> + <li>@c FIRAuthErrorCodeInvalidEmail - Indicates the email address is malformed. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + @remarks This method will only exist until the next major Firebase release following 4.x.x. + After the next major release the method @c signInWithEmail:password:completion: will support + the @c FIRAuthDataResultCallback. + */ +- (void)signInAndRetrieveDataWithEmail:(NSString *)email + password:(NSString *)password + completion:(nullable FIRAuthDataResultCallback)completion; + /** @fn signInWithCredential:completion: @brief Convenience method for @c signInAndRetrieveDataWithCredential:completion: This method doesn't return additional identity provider data. |