aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Example/Auth/Sample/MainViewController.m127
-rw-r--r--Example/Auth/Tests/FIRAuthTests.m286
-rw-r--r--Example/Auth/Tests/FIREmailLinkRequestTests.m137
-rw-r--r--Example/Auth/Tests/FIREmailLinkSignInResponseTests.m195
-rw-r--r--Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m43
-rw-r--r--Example/Firebase.xcodeproj/project.pbxproj10
-rw-r--r--Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m4
-rw-r--r--Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h13
-rw-r--r--Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m9
-rw-r--r--Firebase/Auth/Source/FIRAuth.m176
-rw-r--r--Firebase/Auth/Source/Public/FIRAuth.h81
-rw-r--r--Firebase/Auth/Source/Public/FIREmailAuthProvider.h9
-rw-r--r--Firebase/Auth/Source/RPCs/FIRAuthBackend.h32
-rw-r--r--Firebase/Auth/Source/RPCs/FIRAuthBackend.m19
-rw-r--r--Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h5
-rw-r--r--Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m1
-rw-r--r--Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h66
-rw-r--r--Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m70
-rw-r--r--Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h54
-rw-r--r--Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m32
-rw-r--r--Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h22
-rw-r--r--Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m24
22 files changed, 1397 insertions, 18 deletions
diff --git a/Example/Auth/Sample/MainViewController.m b/Example/Auth/Sample/MainViewController.m
index 31c103a..36ef92d 100644
--- a/Example/Auth/Sample/MainViewController.m
+++ b/Example/Auth/Sample/MainViewController.m
@@ -91,6 +91,16 @@ static NSString *const kSetPhotoURLText = @"Set Photo url";
*/
static NSString *const kSignInGoogleButtonText = @"Sign in with Google";
+/** @var kSignInWithEmailLink
+ @brief The text of the "Sign in with Email Link" button.
+ */
+static NSString *const kSignInWithEmailLink = @"Sign in with Email Link";
+
+/** @var kSendEmailSignInLink
+ @brief The text of the "Send Email SignIn link" button
+*/
+static NSString *const kSendEmailSignInLink = @"Send Email Sign in Link";
+
/** @var kSignInAndRetrieveGoogleButtonText
@brief The text of the "Sign in with Google and retrieve data" button.
*/
@@ -279,6 +289,11 @@ static NSString *const kUnlinkFromEmailPassword = @"Unlink from Email/Password";
*/
static NSString *const kGetProvidersForEmail = @"Get Provider IDs for Email";
+/** @var kGetAllSignInMethodsForEmail
+ @brief The text of the "Get sign-in methods for Email" button.
+ */
+static NSString *const kGetAllSignInMethodsForEmail = @"Get Sign-in methods for Email";
+
/** @var kActionCodeTypeDescription
@brief The description of the "Action Type" entry.
*/
@@ -722,6 +737,10 @@ typedef enum {
action:^{ [weakSelf createUserAuthDataResult]; }],
[StaticContentTableViewCell cellWithTitle:kSignInGoogleButtonText
action:^{ [weakSelf signInGoogle]; }],
+ [StaticContentTableViewCell cellWithTitle:kSignInWithEmailLink
+ action:^{ [weakSelf signInWithEmailLink]; }],
+ [StaticContentTableViewCell cellWithTitle:kSendEmailSignInLink
+ action:^{ [weakSelf sendEmailSignInLink]; }],
[StaticContentTableViewCell cellWithTitle:kSignInGoogleAndRetrieveDataButtonText
action:^{ [weakSelf signInGoogleAndRetrieveData]; }],
[StaticContentTableViewCell cellWithTitle:kSignInFacebookButtonText
@@ -754,6 +773,8 @@ typedef enum {
action:^{ [weakSelf reloadUser]; }],
[StaticContentTableViewCell cellWithTitle:kGetProvidersForEmail
action:^{ [weakSelf getProvidersForEmail]; }],
+ [StaticContentTableViewCell cellWithTitle:kGetAllSignInMethodsForEmail
+ action:^{ [weakSelf getAllSignInMethodsForEmail]; }],
[StaticContentTableViewCell cellWithTitle:kUpdateEmailText
action:^{ [weakSelf updateEmail]; }],
[StaticContentTableViewCell cellWithTitle:kUpdatePasswordText
@@ -1657,7 +1678,7 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) {
}
/** @fn signInEmailPassword
- @brief Invoked when "sign in with Email/Password" row is pressed.
+ @brief Invoked when "Sign in with Email/Password" row is pressed.
*/
- (void)signInEmailPassword {
[self showTextInputPromptWithMessage:@"Email Address:"
@@ -1724,6 +1745,75 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) {
}];
}
+/** @fn signInWithEmailLink
+ @brief Invoked when "Sign in with email link" row is pressed.
+ */
+- (void)signInWithEmailLink {
+ [self showTextInputPromptWithMessage:@"Email Address:"
+ keyboardType:UIKeyboardTypeEmailAddress
+ completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) {
+ if (!userPressedOK || !email.length) {
+ return;
+ }
+ [self showTextInputPromptWithMessage:@"Email Sign-In Link:"
+ completionBlock:^(BOOL userPressedOK, NSString *_Nullable link) {
+ if (!userPressedOK) {
+ return;
+ }
+ if ([[FIRAuth auth] isSignInWithEmailLink:link]) {
+ [self showSpinner:^{
+ [[AppManager auth] signInWithEmail:email
+ link:link
+ completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ [self hideSpinner:^{
+ if (error) {
+ [self logFailure:@"sign-in with Email/Sign-In failed" error:error];
+ } else {
+ [self logSuccess:@"sign-in with Email/Sign-In link succeeded."];
+ [self log:[NSString stringWithFormat:@"UID: %@",authResult.user.uid]];
+ }
+ [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
+ }];
+ }];
+ }];
+ } else {
+ [self log:@"The sign-in link is invalid"];
+ }
+ }];
+ }];
+}
+
+/** @fn sendEmailSignInLink
+ @brief Invoked when "Send email sign-in link" row is pressed.
+ */
+- (void)sendEmailSignInLink {
+ [self showTextInputPromptWithMessage:@"Email:"
+ completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
+ if (!userPressedOK) {
+ return;
+ }
+ [self showSpinner:^{
+ void (^requestEmailSignInLink)(void (^)(NSError *)) = ^(void (^completion)(NSError *)) {
+ [[AppManager auth] sendSignInLinkToEmail:userInput
+ actionCodeSettings:[self actionCodeSettings]
+ completion:completion];
+ };
+ requestEmailSignInLink(^(NSError *_Nullable error) {
+ [self hideSpinner:^{
+ if (error) {
+ [self logFailure:@"Email Link request failed" error:error];
+ [self showMessagePrompt:error.localizedDescription];
+ return;
+ }
+ [self logSuccess:@"Email Link request succeeded."];
+ [self showMessagePrompt:@"Sent"];
+ }];
+ });
+ }];
+ }];
+}
+
/** @fn signUpNewEmail
@brief Invoked if sign-in is attempted with new email/password.
@remarks Should only be called if @c FIRAuthErrorCodeInvalidEmail is encountered on attepmt to
@@ -2245,6 +2335,39 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) {
}];
}
+/** @fn getAllSignInMethodsForEmail
+ @brief Prompts user for an email address, calls @c FIRAuth.getAllSignInMethodsForEmail:callback:
+ and displays the result.
+ */
+- (void)getAllSignInMethodsForEmail {
+ [self showTextInputPromptWithMessage:@"Email:"
+ completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
+ if (!userPressedOK || !userInput.length) {
+ return;
+ }
+
+ [self showSpinner:^{
+ [[AppManager auth] fetchSignInMethodsForEmail:userInput
+ completion:^(NSArray<NSString *> *_Nullable signInMethods,
+ NSError *_Nullable error) {
+ if (error) {
+ [self logFailure:@"get sign-in methods for email failed" error:error];
+ } else {
+ [self logSuccess:@"get sign-in methods for email succeeded."];
+ }
+ [self hideSpinner:^{
+ if (error) {
+ [self showMessagePrompt:error.localizedDescription];
+ return;
+ }
+ [self showMessagePrompt:[signInMethods componentsJoinedByString:@", "]];
+ }];
+ }];
+ }];
+ }];
+}
+
+
/** @fn actionCodeRequestTypeString
@brief Returns a string description for the type of the next action code request.
*/
@@ -2486,6 +2609,8 @@ static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) {
return @"Recover Email";
case FIRActionCodeOperationPasswordReset:
return @"Password Reset";
+ case FIRActionCodeOperationEmailLink:
+ return @"Email Sign-In Link";
case FIRActionCodeOperationUnknown:
return @"Unknown action";
}
diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m
index b22c600..914c58b 100644
--- a/Example/Auth/Tests/FIRAuthTests.m
+++ b/Example/Auth/Tests/FIRAuthTests.m
@@ -33,6 +33,8 @@
#import "FIRAuthBackend.h"
#import "FIRCreateAuthURIRequest.h"
#import "FIRCreateAuthURIResponse.h"
+#import "FIREmailLinkSignInRequest.h"
+#import "FIREmailLinkSignInResponse.h"
#import "FIRGetAccountInfoRequest.h"
#import "FIRGetAccountInfoResponse.h"
#import "FIRGetOOBConfirmationCodeRequest.h"
@@ -56,6 +58,7 @@
#import "FIRApp+FIRAuthUnitTests.h"
#import "OCMStubRecorder+FIRAuthUnitTests.h"
#import <OCMock/OCMock.h>
+#import "FIRActionCodeSettings.h"
#if TARGET_OS_IOS
#import "FIRPhoneAuthCredential.h"
@@ -167,6 +170,38 @@ static NSString *const kVerificationCode = @"12345678";
*/
static NSString *const kVerificationID = @"55432";
+/** @var kContinueURL
+ @brief Fake string value of continue url.
+ */
+static NSString *const kContinueURL = @"continueURL";
+
+/** @var kCanHandleCodeInAppKey
+ @brief The key for the request parameter indicating whether the action code can be handled in
+ the app or not.
+ */
+static NSString *const kCanHandleCodeInAppKey = @"canHandleCodeInApp";
+
+/** @var kFIREmailLinkAuthSignInMethod
+ @brief Fake email link sign-in method for testing.
+ */
+static NSString *const kFIREmailLinkAuthSignInMethod = @"emailLink";
+
+/** @var kFIRFacebookAuthSignInMethod
+ @brief Fake Facebook sign-in method for testing.
+ */
+static NSString *const kFIRFacebookAuthSignInMethod = @"facebook.com";
+
+/** @var kBadSignInEmailLink
+ @brief Bad sign-in link to test email link sign-in
+ */
+static NSString *const kBadSignInEmailLink = @"http://www.facebook.com";
+
+/** @var kFakeEmailSignInlink
+ @brief Fake email sign-in link
+ */
+static NSString *const kFakeEmailSignInlink = @"https://fex9s.app.goo.gl?link="
+ "https://fb-sa-upgraded.firebaseapp.com/_?mode%3DsignIn%26oobCode%3Dtestoobcode";
+
/** @var kExpectationTimeout
@brief The maximum time waiting for expectations to fulfill.
*/
@@ -360,6 +395,39 @@ static const NSTimeInterval kWaitInterval = .5;
OCMVerifyAll(_mockBackend);
}
+/** @fn testFetchSignInMethodsForEmailSuccess
+ @brief Tests the flow of a successful @c fetchSignInMethodsForEmail:completion: call.
+ */
+- (void)testFetchSignInMethodsForEmailSuccess {
+ NSArray<NSString *> *allSignInMethods =
+ @[ kFIREmailLinkAuthSignInMethod, kFIRFacebookAuthSignInMethod ];
+ OCMExpect([_mockBackend createAuthURI:[OCMArg any]
+ callback:[OCMArg any]])
+ .andCallBlock2(^(FIRCreateAuthURIRequest *_Nullable request,
+ FIRCreateAuthURIResponseCallback callback) {
+ XCTAssertEqualObjects(request.identifier, kEmail);
+ XCTAssertNotNil(request.endpoint);
+ XCTAssertEqualObjects(request.APIKey, kAPIKey);
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ id mockCreateAuthURIResponse = OCMClassMock([FIRCreateAuthURIResponse class]);
+ OCMStub([mockCreateAuthURIResponse signinMethods]).andReturn(allSignInMethods);
+ callback(mockCreateAuthURIResponse, nil);
+ });
+ });
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] fetchSignInMethodsForEmail:kEmail
+ completion:^(NSArray<NSString *> *_Nullable signInMethods,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqualObjects(signInMethods, allSignInMethods);
+ XCTAssertTrue([allSignInMethods isKindOfClass:[NSArray class]]);
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
/** @fn testFetchProvidersForEmailSuccessDeprecatedProviderID
@brief Tests the flow of a successful @c fetchProvidersForEmail:completion: call using the
deprecated FIREmailPasswordAuthProviderID.
@@ -416,6 +484,25 @@ static const NSTimeInterval kWaitInterval = .5;
OCMVerifyAll(_mockBackend);
}
+/** @fn testFetchSignInMethodsForEmailFailure
+ @brief Tests the flow of a failed @c fetchSignInMethodsForEmail:completion: call.
+ */
+- (void)testFetchSignInMethodsForEmailFailure {
+ OCMExpect([_mockBackend createAuthURI:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils tooManyRequestsErrorWithMessage:nil]);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] fetchSignInMethodsForEmail:kEmail
+ completion:^(NSArray<NSString *> *_Nullable signInMethods,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(signInMethods);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeTooManyRequests);
+ XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
+ [expectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
#if TARGET_OS_IOS
/** @fn testPhoneAuthSuccess
@brief Tests the flow of a successful @c signInWithCredential:completion for phone auth.
@@ -501,6 +588,63 @@ static const NSTimeInterval kWaitInterval = .5;
}
#endif
+/** @fn testSignInWithEmailLinkSuccess
+ @brief Tests the flow of a successful @c signInWithEmail:link:completion: call.
+ */
+- (void)testSignInWithEmailLinkSuccess {
+ NSString *fakeCode = @"testoobcode";
+ OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIREmailLinkSignInRequest *_Nullable request,
+ FIREmailLinkSigninResponseCallback callback) {
+ XCTAssertEqualObjects(request.email, kEmail);
+ XCTAssertEqualObjects(request.oobCode, fakeCode);
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ id mockEmailLinkSignInResponse = OCMClassMock([FIREmailLinkSignInResponse class]);
+ [self stubTokensWithMockResponse:mockEmailLinkSignInResponse];
+ callback(mockEmailLinkSignInResponse, nil);
+ OCMStub([mockEmailLinkSignInResponse refreshToken]).andReturn(kRefreshToken);
+ });
+ });
+ [self expectGetAccountInfo];
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] signOut:NULL];
+ [[FIRAuth auth] signInWithEmail:kEmail
+ link:kFakeEmailSignInlink
+ completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNotNil(authResult.user);
+ XCTAssertEqualObjects(authResult.user.refreshToken, kRefreshToken);
+ XCTAssertFalse(authResult.user.anonymous);
+ XCTAssertEqualObjects(authResult.user.email, kEmail);
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ [self assertUser:[FIRAuth auth].currentUser];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testSignInWithEmailLinkFailure
+ @brief Tests the flow of a failed @c signInWithEmail:link:completion: call.
+ */
+- (void)testSignInWithEmailLinkFailure {
+ OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]])
+ ._andDispatchError2([FIRAuthErrorUtils invalidActionCodeErrorWithMessage:nil]);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] signOut:NULL];
+ [[FIRAuth auth] signInWithEmail:kEmail
+ link:kFakeEmailSignInlink
+ completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidActionCode);
+ [expectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
/** @fn testSignInWithEmailPasswordSuccess
@brief Tests the flow of a successful @c signInWithEmail:password:completion: call.
*/
@@ -521,8 +665,10 @@ static const NSTimeInterval kWaitInterval = .5;
[self expectGetAccountInfo];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
- [[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user,
- NSError *_Nullable error) {
+ [[FIRAuth auth] signInWithEmail:kEmail
+ password:kFakePassword
+ completion:^(FIRUser *_Nullable user,
+ NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUser:user];
XCTAssertNil(error);
@@ -541,8 +687,10 @@ static const NSTimeInterval kWaitInterval = .5;
.andDispatchError2([FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]);
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
- [[FIRAuth auth] signInWithEmail:kEmail password:kFakePassword completion:^(FIRUser *_Nullable user,
- NSError *_Nullable error) {
+ [[FIRAuth auth] signInWithEmail:kEmail
+ password:kFakePassword
+ completion:^(FIRUser *_Nullable user,
+ NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(user);
XCTAssertEqual(error.code, FIRAuthErrorCodeWrongPassword);
@@ -829,6 +977,68 @@ static const NSTimeInterval kWaitInterval = .5;
OCMVerifyAll(_mockBackend);
}
+/** @fn testSignInWithEmailLinkCredentialSuccess
+ @brief Tests the flow of a successfully @c signInWithCredential:completion: call with an
+ email sign-in link credential using FIREmailAuthProvider.
+ */
+- (void)testSignInWithEmailLinkCredentialSuccess {
+ NSString *fakeCode = @"testoobcode";
+ OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIREmailLinkSignInRequest *_Nullable request,
+ FIREmailLinkSigninResponseCallback callback) {
+ XCTAssertEqualObjects(request.email, kEmail);
+ XCTAssertEqualObjects(request.oobCode, fakeCode);
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ id mockEmailLinkSigninResponse = OCMClassMock([FIREmailLinkSignInResponse class]);
+ [self stubTokensWithMockResponse:mockEmailLinkSigninResponse];
+ callback(mockEmailLinkSigninResponse, nil);
+ });
+ });
+ [self expectGetAccountInfo];
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] signOut:NULL];
+ FIRAuthCredential *emailCredential =
+ [FIREmailAuthProvider credentialWithEmail:kEmail link:kFakeEmailSignInlink];
+ [[FIRAuth auth] signInAndRetrieveDataWithCredential:emailCredential
+ completion:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNotNil(authResult.user);
+ XCTAssertEqualObjects(authResult.user.refreshToken, kRefreshToken);
+ XCTAssertFalse(authResult.user.anonymous);
+ XCTAssertEqualObjects(authResult.user.email, kEmail);
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ [self assertUser:[FIRAuth auth].currentUser];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testSignInWithEmailLinkCredentialFailure
+ @brief Tests the flow of a failed @c signInWithCredential:completion: call with an
+ email-email sign-in link credential using FIREmailAuthProvider.
+ */
+- (void)testSignInWithEmailLinkCredentialFailure {
+ OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils userDisabledErrorWithMessage:nil]);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] signOut:NULL];
+ FIRAuthCredential *emailCredential =
+ [FIREmailAuthProvider credentialWithEmail:kEmail link:kFakeEmailSignInlink];
+ [[FIRAuth auth] signInWithCredential:emailCredential completion:^(FIRUser *_Nullable user,
+ NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(user);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeUserDisabled);
+ XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
+ [expectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ XCTAssertNil([FIRAuth auth].currentUser);
+ OCMVerifyAll(_mockBackend);
+}
+
/** @fn testSignInWithEmailCredentialSuccess
@brief Tests the flow of a successfully @c signInWithCredential:completion: call with an
email-password credential.
@@ -1504,6 +1714,65 @@ static const NSTimeInterval kWaitInterval = .5;
OCMVerifyAll(_mockBackend);
}
+/** @fn testSendSignInLinkToEmailSuccess
+ @brief Tests the flow of a successful @c sendSignInLinkToEmail:actionCodeSettings:completion:
+ call.
+ */
+- (void)testSendSignInLinkToEmailSuccess {
+ OCMExpect([_mockBackend getOOBConfirmationCode:[OCMArg any] callback:[OCMArg any]])
+ .andCallBlock2(^(FIRGetOOBConfirmationCodeRequest *_Nullable request,
+ FIRGetOOBConfirmationCodeResponseCallback callback) {
+ XCTAssertEqualObjects(request.APIKey, kAPIKey);
+ XCTAssertEqualObjects(request.email, kEmail);
+ XCTAssertEqualObjects(request.continueURL, kContinueURL);
+ XCTAssertTrue(request.handleCodeInApp);
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
+ callback([[FIRGetOOBConfirmationCodeResponse alloc] init], nil);
+ });
+ });
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] sendSignInLinkToEmail:kEmail
+ actionCodeSettings:[self fakeActionCodeSettings]
+ completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn testSendSignInLinkToEmailFailure
+ @brief Tests the flow of a failed @c sendSignInLinkToEmail:actionCodeSettings:completion:
+ call.
+ */
+- (void)testSendSignInLinkToEmailFailure {
+ OCMExpect([_mockBackend getOOBConfirmationCode:[OCMArg any] callback:[OCMArg any]])
+ .andDispatchError2([FIRAuthErrorUtils appNotAuthorizedError]);
+ XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
+ [[FIRAuth auth] sendSignInLinkToEmail:kEmail
+ actionCodeSettings:[self fakeActionCodeSettings]
+ completion:^(NSError *_Nullable error) {
+ XCTAssertTrue([NSThread isMainThread]);
+ XCTAssertEqual(error.code, FIRAuthErrorCodeAppNotAuthorized);
+ XCTAssertNotNil(error.userInfo[NSLocalizedDescriptionKey]);
+ [expectation fulfill];
+ }];
+ [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
+ OCMVerifyAll(_mockBackend);
+}
+
+/** @fn fakeActionCodeSettings
+ @brief Constructs and returns a fake instance of @c FIRActionCodeSettings for testing.
+ @return An instance of @c FIRActionCodeSettings for testing.
+ */
+- (FIRActionCodeSettings *)fakeActionCodeSettings {
+ FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc]init];
+ actionCodeSettings.URL = [NSURL URLWithString:kContinueURL];
+ actionCodeSettings.handleCodeInApp = YES;
+ return actionCodeSettings;
+}
+
/** @fn testSignOut
@brief Tests the @c signOut: method.
*/
@@ -1515,6 +1784,15 @@ static const NSTimeInterval kWaitInterval = .5;
XCTAssertNil([FIRAuth auth].currentUser);
}
+/** @fn testIsSignInWithEmailLink
+ @brief Tests the @c isSignInWithEmailLink: method.
+*/
+- (void)testIsSignInWithEmailLink {
+ XCTAssertTrue([[FIRAuth auth] isSignInWithEmailLink:kFakeEmailSignInlink]);
+ XCTAssertFalse([[FIRAuth auth] isSignInWithEmailLink:kBadSignInEmailLink]);
+ XCTAssertFalse([[FIRAuth auth] isSignInWithEmailLink:@""]);
+}
+
/** @fn testAuthStateChanges
@brief Tests @c addAuthStateDidChangeListener: and @c removeAuthStateDidChangeListener: methods.
*/
diff --git a/Example/Auth/Tests/FIREmailLinkRequestTests.m b/Example/Auth/Tests/FIREmailLinkRequestTests.m
new file mode 100644
index 0000000..90d7c18
--- /dev/null
+++ b/Example/Auth/Tests/FIREmailLinkRequestTests.m
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <XCTest/XCTest.h>
+
+#import "FIRAuthErrors.h"
+#import "FIRAuthBackend.h"
+#import "FIREmailLinkSignInRequest.h"
+#import "FIREmailLinkSignInResponse.h"
+#import "FIRFakeBackendRPCIssuer.h"
+
+/** @var kTestAPIKey
+ @brief Fake API key used for testing.
+ */
+static NSString *const kTestAPIKey = @"APIKey";
+
+/** @var kTestEmail
+ @brief The key for the "email" value in the request.
+ */
+static NSString *const kTestEmail = @"TestEmail@email.com";
+
+/** @var kTestOOBCode
+ @brief The test value for the "oobCode" in the request.
+ */
+static NSString *const kTestOOBCode = @"TestOOBCode";
+
+/** @var kTestIDToken
+ @brief The test value for "idToken" in the request.
+ */
+static NSString *const kTestIDToken = @"testIDToken";
+
+/** @var kEmailKey
+ @brief The key for the "identifier" value in the request.
+ */
+static NSString *const kEmailKey = @"email";
+
+/** @var kEmailLinkKey
+ @brief The key for the "oobCode" value in the request.
+ */
+static NSString *const kOOBCodeKey = @"oobCode";
+
+/** @var kIDTokenKey
+ @brief The key for the "IDToken" value in the request.
+ */
+static NSString *const kIDTokenKey = @"idToken";
+
+/** @var kExpectedAPIURL
+ @brief The value of the expected URL (including the backend endpoint) in the request.
+ */
+static NSString *const kExpectedAPIURL =
+ @"https://www.googleapis.com/identitytoolkit/v3/relyingparty/emailLinkSignin?key=APIKey";
+
+/** @class FIREmailLinkRequestTests
+ @brief Tests for @c FIREmailLinkRequests.
+ */
+@interface FIREmailLinkRequestTests : XCTestCase
+@end
+
+@implementation FIREmailLinkRequestTests {
+ /** @var _RPCIssuer
+ @brief This backend RPC issuer is used to fake network responses for each test in the suite.
+ In the @c setUp method we initialize this and set @c FIRAuthBackend's RPC issuer to it.
+ */
+ FIRFakeBackendRPCIssuer *_RPCIssuer;
+
+ /** @var _requestConfiguration
+ @brief This is the request configuration used for testing.
+ */
+ FIRAuthRequestConfiguration *_requestConfiguration;
+}
+
+- (void)setUp {
+ [super setUp];
+ FIRFakeBackendRPCIssuer *RPCIssuer = [[FIRFakeBackendRPCIssuer alloc] init];
+ [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:RPCIssuer];
+ _RPCIssuer = RPCIssuer;
+ _requestConfiguration = [[FIRAuthRequestConfiguration alloc] initWithAPIKey:kTestAPIKey];
+}
+
+- (void)tearDown {
+ _RPCIssuer = nil;
+ _requestConfiguration = nil;
+ [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:nil];
+ [super tearDown];
+}
+
+/** @fn testEmailLinkRequestCreation
+ @brief Tests the email link sign-in request with mandatory parameters.
+ */
+- (void)testEmailLinkRequest {
+ FIREmailLinkSignInRequest *request =
+ [[FIREmailLinkSignInRequest alloc] initWithEmail:kTestEmail
+ oobCode:kTestOOBCode
+ requestConfiguration:_requestConfiguration];
+ [FIRAuthBackend emailLinkSignin:request callback:^(FIREmailLinkSignInResponse *_Nullable response,
+ NSError *_Nullable error) {
+ }];
+ XCTAssertEqualObjects(_RPCIssuer.requestURL.absoluteString, kExpectedAPIURL);
+ XCTAssertNotNil(_RPCIssuer.decodedRequest);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kEmailKey], kTestEmail);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kOOBCodeKey], kTestOOBCode);
+ XCTAssertNil(_RPCIssuer.decodedRequest[kIDTokenKey]);
+}
+
+/** @fn testEmailLinkRequestCreationOptional
+ @brief Tests the email link sign-in request with mandatory parameters and optional ID token.
+ */
+- (void)testEmailLinkRequestCreationOptional {
+ FIREmailLinkSignInRequest *request =
+ [[FIREmailLinkSignInRequest alloc] initWithEmail:kTestEmail
+ oobCode:kTestOOBCode
+ requestConfiguration:_requestConfiguration];
+ request.IDToken = kTestIDToken;
+ [FIRAuthBackend emailLinkSignin:request callback:^(FIREmailLinkSignInResponse *_Nullable response,
+ NSError *_Nullable error) {
+ }];
+ XCTAssertEqualObjects(_RPCIssuer.requestURL.absoluteString, kExpectedAPIURL);
+ XCTAssertNotNil(_RPCIssuer.decodedRequest);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kEmailKey], kTestEmail);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kOOBCodeKey], kTestOOBCode);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kIDTokenKey], kTestIDToken);
+}
+
+@end
diff --git a/Example/Auth/Tests/FIREmailLinkSignInResponseTests.m b/Example/Auth/Tests/FIREmailLinkSignInResponseTests.m
new file mode 100644
index 0000000..cc2c544
--- /dev/null
+++ b/Example/Auth/Tests/FIREmailLinkSignInResponseTests.m
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <XCTest/XCTest.h>
+
+#import "FIRAuthErrors.h"
+#import "FIRAuthErrorUtils.h"
+#import "FIRAuthBackend.h"
+#import "FIREmailLinkSignInRequest.h"
+#import "FIREmailLinkSignInResponse.h"
+#import "FIRFakeBackendRPCIssuer.h"
+
+/** @var kTestAPIKey
+ @brief Fake API key used for testing.
+ */
+static NSString *const kTestAPIKey = @"APIKey";
+
+/** @var kTestEmail
+ @brief The key for the "email" value in the request.
+ */
+static NSString *const kTestEmail = @"TestEmail@email.com";
+
+/** @var kTestOOBCode
+ @brief The test value for the "oobCode" in the request.
+ */
+static NSString *const kTestOOBCode = @"TestOOBCode";
+
+/** @var kTestIDToken
+ @brief The test value for "idToken" in the request.
+ */
+static NSString *const kTestIDToken = @"testIDToken";
+
+/** @var kEmailKey
+ @brief The key for the "identifier" value in the request.
+ */
+static NSString *const kEmailKey = @"email";
+
+/** @var kEmailLinkKey
+ @brief The key for the "emailLink" value in the request.
+ */
+static NSString *const kOOBCodeKey = @"oobCode";
+
+/** @var kIDTokenKey
+ @brief The key for the "IDToken" value in the request.
+ */
+static NSString *const kIDTokenKey = @"idToken";
+
+/** @var kTestIDTokenResponse
+ @brief A fake ID Token in the server response.
+ */
+static NSString *const kTestIDTokenResponse = @"fakeToken";
+
+/** @var kTestEmailResponse
+ @brief A fake email in the server response.
+ */
+static NSString *const kTestEmailResponse = @"fake email";
+
+/** @var kTestRefreshToken
+ @brief A fake refresh token in the server response.
+ */
+static NSString *const kTestRefreshToken = @"testRefreshToken";
+
+/** @var kInvalidEmailErrorMessage
+ @brief The error returned by the server if the email is invalid.
+ */
+static NSString *const kInvalidEmailErrorMessage = @"INVALID_EMAIL";
+
+/** @var kTestTokenExpirationTimeInterval
+ @brief The fake time interval that it takes a token to expire.
+ */
+static const NSTimeInterval kTestTokenExpirationTimeInterval = 55 * 60;
+
+/** @var kMaxDifferenceBetweenDates
+ @brief The maximum difference between time two dates (in seconds), after which they will be
+ considered different.
+ */
+static const NSTimeInterval kMaxDifferenceBetweenDates = 0.0001;
+
+/** @var kFakeIsNewUSerFlag
+ @brief The fake fake isNewUser flag in the response.
+ */
+static const BOOL kFakeIsNewUSerFlag = YES;
+
+/** @class FIREmailLinkRequestTests
+ @brief Tests for @c FIREmailLinkRequests.
+ */
+@interface FIREmailLinkSignInResponseTests : XCTestCase
+@end
+
+@implementation FIREmailLinkSignInResponseTests {
+ /** @var _RPCIssuer
+ @brief This backend RPC issuer is used to fake network responses for each test in the suite.
+ In the @c setUp method we initialize this and set @c FIRAuthBackend's RPC issuer to it.
+ */
+ FIRFakeBackendRPCIssuer *_RPCIssuer;
+
+ /** @var _requestConfiguration
+ @brief This is the request configuration used for testing.
+ */
+ FIRAuthRequestConfiguration *_requestConfiguration;
+}
+
+- (void)setUp {
+ [super setUp];
+ FIRFakeBackendRPCIssuer *RPCIssuer = [[FIRFakeBackendRPCIssuer alloc] init];
+ [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:RPCIssuer];
+ _RPCIssuer = RPCIssuer;
+ _requestConfiguration = [[FIRAuthRequestConfiguration alloc] initWithAPIKey:kTestAPIKey];
+}
+
+/** @fn testFailedEmailLinkSignInResponse
+ @brief Tests a failed email link sign-in response.
+ */
+- (void)testFailedEmailLinkSignInResponse {
+ FIREmailLinkSignInRequest *request =
+ [[FIREmailLinkSignInRequest alloc] initWithEmail:kTestEmail
+ oobCode:kTestOOBCode
+ requestConfiguration:_requestConfiguration];
+
+ __block BOOL callbackInvoked = NO;
+ __block FIREmailLinkSignInResponse *RPCResponse;
+ __block NSError *RPCError;
+ [FIRAuthBackend emailLinkSignin:request
+ callback:^(FIREmailLinkSignInResponse *_Nullable response,
+ NSError *_Nullable error) {
+ callbackInvoked = YES;
+ RPCResponse = response;
+ RPCError = error;
+ }];
+
+ [_RPCIssuer respondWithServerErrorMessage:kInvalidEmailErrorMessage];
+
+ XCTAssert(callbackInvoked);
+ XCTAssertNil(RPCResponse);
+ XCTAssertEqual(RPCError.code, FIRAuthErrorCodeInvalidEmail);
+}
+
+/** @fn testSuccessfulEmailLinkSignInResponse
+ @brief Tests a succesful email link sign-in response.
+ */
+- (void)testSuccessfulEmailLinkSignInResponse {
+ FIREmailLinkSignInRequest *request =
+ [[FIREmailLinkSignInRequest alloc] initWithEmail:kTestEmail
+ oobCode:kTestOOBCode
+ requestConfiguration:_requestConfiguration];
+
+ __block BOOL callbackInvoked = NO;
+ __block FIREmailLinkSignInResponse *RPCResponse;
+ __block NSError *RPCError;
+ [FIRAuthBackend emailLinkSignin:request
+ callback:^(FIREmailLinkSignInResponse *_Nullable response,
+ NSError *_Nullable error) {
+ callbackInvoked = YES;
+ RPCResponse = response;
+ RPCError = error;
+ }];
+
+ [_RPCIssuer respondWithJSON:@{
+ @"idToken" : kTestIDTokenResponse,
+ @"email" : kTestEmailResponse,
+ @"isNewUser" : kFakeIsNewUSerFlag ? @YES : @NO,
+ @"expiresIn" : [NSString stringWithFormat:@"%f",kTestTokenExpirationTimeInterval],
+ @"refreshToken" : kTestRefreshToken,
+ }];
+
+ XCTAssert(callbackInvoked);
+ XCTAssertNil(RPCError);
+ XCTAssertNotNil(RPCResponse);
+ XCTAssertEqualObjects(RPCResponse.IDToken, kTestIDTokenResponse);
+ XCTAssertEqualObjects(RPCResponse.email, kTestEmailResponse);
+ XCTAssertEqualObjects(RPCResponse.refreshToken, kTestRefreshToken);
+ XCTAssertTrue(RPCResponse.isNewUser);
+ NSTimeInterval expirationTimeInterval =
+ [RPCResponse.approximateExpirationDate timeIntervalSinceNow];
+ NSTimeInterval testTimeInterval =
+ [[NSDate dateWithTimeIntervalSinceNow:kTestTokenExpirationTimeInterval] timeIntervalSinceNow];
+ NSTimeInterval timeIntervalDifference =
+ fabs(expirationTimeInterval - testTimeInterval);
+ XCTAssert(timeIntervalDifference < kMaxDifferenceBetweenDates);
+}
+
+@end
diff --git a/Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m b/Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m
index 965af8a..b11c759 100644
--- a/Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m
+++ b/Example/Auth/Tests/FIRGetOOBConfirmationCodeRequestTests.m
@@ -49,6 +49,11 @@ static NSString *const kPasswordResetRequestTypeValue = @"PASSWORD_RESET";
*/
static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL";
+/** @var kEmailLinkSignInTypeValue
+ @brief The value for the "EMAIL_SIGNIN" request type.
+ */
+static NSString *const kEmailLinkSignInTypeValue = @"EMAIL_SIGNIN";
+
/** @var kEmailKey
@brief The name of the "email" property in the request.
*/
@@ -124,6 +129,7 @@ static NSString *const kCanHandleCodeInAppKey = @"canHandleCodeInApp";
/** @class FIRGetOOBConfirmationCodeRequestTests
@brief Tests for @c FIRGetOOBConfirmationCodeRequest.
*/
+
@interface FIRGetOOBConfirmationCodeRequestTests : XCTestCase
@end
@implementation FIRGetOOBConfirmationCodeRequestTests {
@@ -190,6 +196,43 @@ static NSString *const kCanHandleCodeInAppKey = @"canHandleCodeInApp";
[NSNumber numberWithBool:YES]);
}
+/** @fn testSignInWithEmailLinkRequest
+ @brief Tests the encoding of a email sign-in link request.
+ */
+- (void)testSignInWithEmailLinkRequest {
+ FIRGetOOBConfirmationCodeRequest *request =
+ [FIRGetOOBConfirmationCodeRequest signInWithEmailLinkRequest:kTestEmail
+ actionCodeSettings:[self fakeActionCodeSettings]
+ requestConfiguration:_requestConfiguration];
+
+ __block BOOL callbackInvoked;
+ __block FIRGetOOBConfirmationCodeResponse *RPCResponse;
+ __block NSError *RPCError;
+ [FIRAuthBackend getOOBConfirmationCode:request
+ callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response,
+ NSError *_Nullable error) {
+ callbackInvoked = YES;
+ RPCResponse = response;
+ RPCError = error;
+ }];
+
+ XCTAssertEqualObjects(_RPCIssuer.requestURL.absoluteString, kExpectedAPIURL);
+ XCTAssertNotNil(_RPCIssuer.decodedRequest);
+ XCTAssert([_RPCIssuer.decodedRequest isKindOfClass:[NSDictionary class]]);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kEmailKey], kTestEmail);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kRequestTypeKey], kEmailLinkSignInTypeValue);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kContinueURLKey], kContinueURL);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kIosBundleIDKey], kIosBundleID);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kAndroidPackageNameKey], kAndroidPackageName);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kAndroidMinimumVersionKey],
+ kAndroidMinimumVersion);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kAndroidInstallAppKey],
+ [NSNumber numberWithBool:YES]);
+ XCTAssertEqualObjects(_RPCIssuer.decodedRequest[kCanHandleCodeInAppKey],
+ [NSNumber numberWithBool:YES]);
+}
+
+
/** @fn testEmailVerificationRequest
@brief Tests the encoding of an email verification request.
*/
diff --git a/Example/Firebase.xcodeproj/project.pbxproj b/Example/Firebase.xcodeproj/project.pbxproj
index c8400d6..7c00d1c 100644
--- a/Example/Firebase.xcodeproj/project.pbxproj
+++ b/Example/Firebase.xcodeproj/project.pbxproj
@@ -135,6 +135,8 @@
7E26CF28514D041D284F00A5 /* Pods_Database_Tests_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5318F3AE32EEBCC9FF608813 /* Pods_Database_Tests_tvOS.framework */; };
7E5BD38D202BFB8CD9CCEB53 /* Pods_Auth_EarlGreyTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FAA82401DA4259800B142EA /* Pods_Auth_EarlGreyTests.framework */; };
7E9485421F578AC4005A3939 /* FIRAuthURLPresenterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */; };
+ 7EE21F7A1FE89193009B1370 /* FIREmailLinkRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */; };
+ 7EE21F7C1FE8919E009B1370 /* FIREmailLinkSignInResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EE21F7B1FE8919D009B1370 /* FIREmailLinkSignInResponseTests.m */; };
7EFA2E041F71C93300DD354F /* FIRUserMetadataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7EFA2E031F71C93300DD354F /* FIRUserMetadataTests.m */; };
7F41B0EFBDDA90CB9CE6CE5B /* Pods_Database_Tests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61377AC9FE132A8D7BF71881 /* Pods_Database_Tests_macOS.framework */; };
822CE316AE9827F7F0889B30 /* Pods_Auth_Example_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9878B57CF73D2F865992E6EA /* Pods_Auth_Example_macOS.framework */; };
@@ -1037,6 +1039,8 @@
7981511F571E13DECA09B4B1 /* Pods-Core_Example_macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Core_Example_macOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Core_Example_macOS/Pods-Core_Example_macOS.release.xcconfig"; sourceTree = "<group>"; };
7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRAuthURLPresenterTests.m; sourceTree = "<group>"; };
7ED0DF69C095C21EFC81F672 /* Pods-Database_Example_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Database_Example_tvOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Database_Example_tvOS/Pods-Database_Example_tvOS.release.xcconfig"; sourceTree = "<group>"; };
+ 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIREmailLinkRequestTests.m; sourceTree = "<group>"; };
+ 7EE21F7B1FE8919D009B1370 /* FIREmailLinkSignInResponseTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIREmailLinkSignInResponseTests.m; sourceTree = "<group>"; };
7EFA2E031F71C93300DD354F /* FIRUserMetadataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRUserMetadataTests.m; sourceTree = "<group>"; };
8496034D8156555C5FCF8F14 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
84EC7975F05977AE75E90A12 /* Pods_Database_Example_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Database_Example_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -2371,9 +2375,11 @@
DE9315001E86C6FF0083EDBF /* FIRAuthGlobalWorkQueueTests.m */,
DE9315011E86C6FF0083EDBF /* FIRAuthKeychainTests.m */,
DE750DB81EB3DD4000A75E47 /* FIRAuthNotificationManagerTests.m */,
+ 7EE21F791FE89193009B1370 /* FIREmailLinkRequestTests.m */,
DE9315021E86C6FF0083EDBF /* FIRAuthSerialTaskQueueTests.m */,
DE9315031E86C6FF0083EDBF /* FIRAuthTests.m */,
7E94853F1F578A9D005A3939 /* FIRAuthURLPresenterTests.m */,
+ 7EE21F7B1FE8919D009B1370 /* FIREmailLinkSignInResponseTests.m */,
DE9315041E86C6FF0083EDBF /* FIRAuthUserDefaultsStorageTests.m */,
DE9315051E86C6FF0083EDBF /* FIRCreateAuthURIRequestTests.m */,
DE9315061E86C6FF0083EDBF /* FIRCreateAuthURIResponseTests.m */,
@@ -4662,6 +4668,7 @@
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Database_Tests_tvOS-checkManifestLockResult.txt",
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -5425,6 +5432,7 @@
);
name = "[CP] Copy Pods Resources";
outputPaths = (
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -6188,6 +6196,7 @@
DE9315571E86C71C0083EDBF /* FIRAdditionalUserInfoTests.m in Sources */,
DE750DBF1EB3DD6C00A75E47 /* FIRAuthAppCredentialManagerTests.m in Sources */,
DE93157B1E86C71C0083EDBF /* FIRVerifyPasswordResponseTests.m in Sources */,
+ 7EE21F7A1FE89193009B1370 /* FIREmailLinkRequestTests.m in Sources */,
DE93155B1E86C71C0083EDBF /* FIRAuthDispatcherTests.m in Sources */,
DE9315791E86C71C0083EDBF /* FIRVerifyCustomTokenResponseTests.m in Sources */,
DE9315601E86C71C0083EDBF /* FIRAuthUserDefaultsStorageTests.m in Sources */,
@@ -6216,6 +6225,7 @@
DE93155A1E86C71C0083EDBF /* FIRAuthBackendRPCImplementationTests.m in Sources */,
DE93157D1E86C71C0083EDBF /* FIRVerifyPhoneNumberResponseTests.m in Sources */,
DE93157E1E86C71C0083EDBF /* OCMStubRecorder+FIRAuthUnitTests.m in Sources */,
+ 7EE21F7C1FE8919E009B1370 /* FIREmailLinkSignInResponseTests.m in Sources */,
DE9315771E86C71C0083EDBF /* FIRVerifyAssertionResponseTests.m in Sources */,
DE9315721E86C71C0083EDBF /* FIRSignUpNewUserRequestTests.m in Sources */,
DE9315671E86C71C0083EDBF /* FIRGetAccountInfoResponseTests.m in Sources */,
diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m
index d27611e..7a871e2 100644
--- a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m
+++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m
@@ -32,4 +32,8 @@
return [[FIREmailPasswordAuthCredential alloc] initWithEmail:email password:password];
}
++ (FIRAuthCredential *)credentialWithEmail:(NSString *)email link:(NSString *)link {
+ return [[FIREmailPasswordAuthCredential alloc] initWithEmail:email link:link];
+}
+
@end
diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h
index d50bf17..7625685 100644
--- a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h
+++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h
@@ -35,6 +35,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property(nonatomic, readonly) NSString *password;
+/** @property link
+ @brief The email sign-in link.
+ */
+@property(nonatomic, readonly) NSString *link;
+
/** @fn initWithEmail:password:
@brief Designated initializer.
@param email The user's email address.
@@ -43,6 +48,14 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithEmail:(NSString *)email password:(NSString *)password
NS_DESIGNATED_INITIALIZER;
+/** @fn initWithEmail:link:
+ @brief Designated initializer.
+ @param email The user's email address.
+ @param link The email sign-in link.
+ */
+- (nullable instancetype)initWithEmail:(NSString *)email link:(NSString *)link
+ NS_DESIGNATED_INITIALIZER;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m
index 4361366..71cc330 100644
--- a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m
+++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m
@@ -43,6 +43,15 @@
return self;
}
+- (nullable instancetype)initWithEmail:(NSString *)email link:(NSString *)link {
+ self = [super initWithProvider:FIREmailAuthProviderID];
+ if (self) {
+ _email = [email copy];
+ _link = [link copy];
+ }
+ return self;
+}
+
- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request {
[FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason:
@"Attempt to call prepareVerifyAssertionRequest: on a FIREmailPasswordAuthCredential."];
diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m
index 0f3705f..387fab7 100644
--- a/Firebase/Auth/Source/FIRAuth.m
+++ b/Firebase/Auth/Source/FIRAuth.m
@@ -39,6 +39,8 @@
#import "FIRAuthRequestConfiguration.h"
#import "FIRCreateAuthURIRequest.h"
#import "FIRCreateAuthURIResponse.h"
+#import "FIREmailLinkSignInRequest.h"
+#import "FIREmailLinkSignInResponse.h"
#import "FIRGetOOBConfirmationCodeRequest.h"
#import "FIRGetOOBConfirmationCodeResponse.h"
#import "FIRResetPasswordRequest.h"
@@ -117,6 +119,11 @@ static NSString *const kVerifyEmailRequestType = @"VERIFY_EMAIL";
*/
static NSString *const kRecoverEmailRequestType = @"RECOVER_EMAIL";
+/** @var kEmailLinkSignInRequestType
+ @brief The action code type value for an email sign-in link in the check action code response.
+*/
+static NSString *const kEmailLinkSignInRequestType = @"EMAIL_SIGNIN";
+
/** @var kMissingPasswordReason
@brief The reason why the @c FIRAuthErrorCodeWeakPassword error is thrown.
@remarks This error message will be localized in the future.
@@ -186,6 +193,9 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
if ([requestType isEqualToString:kRecoverEmailRequestType]) {
return FIRActionCodeOperationRecoverEmail;
}
+ if ([requestType isEqualToString:kEmailLinkSignInRequestType]) {
+ return FIRActionCodeOperationEmailLink;
+ }
return FIRActionCodeOperationUnknown;
}
@@ -509,6 +519,24 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
});
}
+- (void)fetchSignInMethodsForEmail:(nonnull NSString *)email
+ completion:(nullable FIRSignInMethodQueryCallback)completion {
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^{
+ FIRCreateAuthURIRequest *request =
+ [[FIRCreateAuthURIRequest alloc] initWithIdentifier:email
+ continueURI:@"http://www.google.com/"
+ requestConfiguration:_requestConfiguration];
+ [FIRAuthBackend createAuthURI:request callback:^(FIRCreateAuthURIResponse *_Nullable response,
+ NSError *_Nullable error) {
+ if (completion) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(response.signinMethods, error);
+ });
+ }
+ }];
+ });
+}
+
- (void)signInWithEmail:(NSString *)email
password:(NSString *)password
completion:(FIRAuthResultCallback)completion {
@@ -524,6 +552,23 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
});
}
+- (void)signInWithEmail:(NSString *)email
+ link:(NSString *)link
+ completion:(FIRAuthDataResultCallback)completion {
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^{
+ FIRAuthDataResultCallback decoratedCallback =
+ [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion];
+ FIREmailPasswordAuthCredential *credential =
+ [[FIREmailPasswordAuthCredential alloc] initWithEmail:email link:link];
+ [self internalSignInAndRetrieveDataWithCredential:credential
+ isReauthentication:NO
+ callback:^(FIRAuthDataResult *_Nullable authResult,
+ NSError *_Nullable error) {
+ decoratedCallback(authResult, error);
+ }];
+ });
+}
+
/** @fn signInWithEmail:password:callback:
@brief Signs in using an email address and password.
@param email The user's email address.
@@ -536,6 +581,7 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
- (void)signInWithEmail:(NSString *)email
password:(NSString *)password
callback:(FIRAuthResultCallback)callback {
+
FIRVerifyPasswordRequest *request =
[[FIRVerifyPasswordRequest alloc] initWithEmail:email
password:password
@@ -591,6 +637,40 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
callback:completion];
}
+/** @fn internalSignInWithEmail:link:completion:
+ @brief Signs in using an email and email sign-in link.
+ @param email The user's email address.
+ @param link The email sign-in link.
+ @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked
+ asynchronously on the global auth work queue in the future.
+ */
+- (void)internalSignInWithEmail:(nonnull NSString *)email
+ link:(nonnull NSString *)link
+ callback:(nullable FIRAuthResultCallback)callback {
+ NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link];
+ NSDictionary<NSString *, NSString *> *queryItems = FIRAuthParseURL(urlComponents.query);
+ NSString *actionCode = queryItems[@"oobCode"];
+
+ FIREmailLinkSignInRequest *request =
+ [[FIREmailLinkSignInRequest alloc] initWithEmail:email
+ oobCode:actionCode
+ requestConfiguration:_requestConfiguration];
+
+ [FIRAuthBackend emailLinkSignin:request
+ callback:^(FIREmailLinkSignInResponse *_Nullable response,
+ NSError *_Nullable error) {
+ if (error) {
+ callback(nil, error);
+ return;
+ }
+ [self completeSignInWithAccessToken:response.IDToken
+ accessTokenExpirationDate:response.approximateExpirationDate
+ refreshToken:response.refreshToken
+ anonymous:NO
+ callback:callback];
+ }];
+}
+
- (void)signInWithCredential:(FIRAuthCredential *)credential
completion:(FIRAuthResultCallback)completion {
dispatch_async(FIRAuthGlobalWorkQueue(), ^{
@@ -628,24 +708,31 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
// Special case for email/password credentials
FIREmailPasswordAuthCredential *emailPasswordCredential =
(FIREmailPasswordAuthCredential *)credential;
- [self signInWithEmail:emailPasswordCredential.email
- password:emailPasswordCredential.password
- callback:^(FIRUser *_Nullable user, NSError *_Nullable error) {
+ FIRAuthResultCallback completeEmailSignIn = ^(FIRUser *user, NSError *error) {
if (callback) {
if (error) {
callback(nil, error);
return;
}
- FIRAdditionalUserInfo *additionalUserInfo =
- [[FIRAdditionalUserInfo alloc] initWithProviderID:FIREmailAuthProviderID
- profile:nil
- username:nil
- isNewUser:NO];
+ FIRAdditionalUserInfo *additionalUserInfo =
+ [[FIRAdditionalUserInfo alloc] initWithProviderID:FIREmailAuthProviderID
+ profile:nil
+ username:nil
+ isNewUser:NO];
FIRAuthDataResult *result = [[FIRAuthDataResult alloc] initWithUser:user
additionalUserInfo:additionalUserInfo];
- callback(result, nil);
+ callback(result, error);
}
- }];
+ };
+ if (emailPasswordCredential.link) {
+ [self internalSignInWithEmail:emailPasswordCredential.email
+ link:emailPasswordCredential.link
+ callback:completeEmailSignIn];
+ } else {
+ [self signInWithEmail:emailPasswordCredential.email
+ password:emailPasswordCredential.password
+ callback:completeEmailSignIn];
+ }
return;
}
@@ -1013,6 +1100,31 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
});
}
+- (void)sendSignInLinkToEmail:(nonnull NSString *)email
+ actionCodeSettings:(nonnull FIRActionCodeSettings *)actionCodeSettings
+ completion:(nullable FIRSendSignInLinkToEmailCallback)completion {
+ dispatch_async(FIRAuthGlobalWorkQueue(), ^{
+ if (!email) {
+ [FIRAuthExceptionUtils raiseInvalidParameterExceptionWithReason:
+ kMissingEmailInvalidParameterExceptionReason];
+ }
+ FIRGetOOBConfirmationCodeRequest *request =
+ [FIRGetOOBConfirmationCodeRequest signInWithEmailLinkRequest:email
+ actionCodeSettings:actionCodeSettings
+ requestConfiguration:_requestConfiguration];
+ [FIRAuthBackend getOOBConfirmationCode:request
+ callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response,
+ NSError *_Nullable error) {
+ if (completion) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(error);
+ });
+ }
+ }];
+ });
+}
+
+
- (BOOL)signOut:(NSError *_Nullable __autoreleasing *_Nullable)error {
__block BOOL result = YES;
dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
@@ -1031,6 +1143,50 @@ static NSMutableDictionary *gKeychainServiceNameForAppName;
return [self updateCurrentUser:nil byForce:YES savingToDisk:YES error:error];
}
+- (BOOL)isSignInWithEmailLink:(NSString *)link {
+ if (link.length == 0) {
+ return NO;
+ }
+ NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link];
+ if (!urlComponents.query) {
+ return NO;
+ }
+ NSDictionary<NSString *, NSString *> *queryItems = FIRAuthParseURL(urlComponents.query);
+
+ NSString *actionCode = queryItems[@"oobCode"];
+ NSString *mode = queryItems[@"mode"];
+
+ if (actionCode && [mode isEqualToString:@"signIn"]) {
+ return YES;
+ }
+ return NO;
+}
+
+/** @fn FIRAuthParseURL:NSString
+ @brief Parses an incoming URL into all available query items.
+ @param urlString The url to be parsed.
+ @return A dictionary of available query items in the target URL.
+ */
+static NSDictionary<NSString *, NSString *> *FIRAuthParseURL(NSString *urlString) {
+ NSString *linkURL = [NSURLComponents componentsWithString:urlString].query;
+ NSArray<NSString *> *URLComponents = [linkURL componentsSeparatedByString:@"&"];
+ NSMutableDictionary<NSString *, NSString *> *queryItems =
+ [[NSMutableDictionary alloc] initWithCapacity:URLComponents.count];
+ for (NSString *component in URLComponents) {
+ NSRange equalRange = [component rangeOfString:@"="];
+ if (equalRange.location != NSNotFound) {
+ NSString *queryItemKey =
+ [[component substringToIndex:equalRange.location] stringByRemovingPercentEncoding];
+ NSString *queryItemValue =
+ [[component substringFromIndex:equalRange.location + 1] stringByRemovingPercentEncoding];
+ if (queryItemKey && queryItemValue) {
+ queryItems[queryItemKey] = queryItemValue;
+ }
+ }
+ }
+ return queryItems;
+}
+
- (FIRAuthStateDidChangeListenerHandle)addAuthStateDidChangeListener:
(FIRAuthStateDidChangeListenerBlock)listener {
__block BOOL firstInvocation = YES;
diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h
index f18a3d0..c985658 100644
--- a/Firebase/Auth/Source/Public/FIRAuth.h
+++ b/Firebase/Auth/Source/Public/FIRAuth.h
@@ -114,6 +114,14 @@ typedef void (^FIRProviderQueryCallback)(NSArray<NSString *> *_Nullable provider
NSError *_Nullable error)
NS_SWIFT_NAME(ProviderQueryCallback);
+/** @typedef FIRSignInMethodQueryCallback
+ @brief The type of block invoked when a list of sign-in methods for a given email address is
+ requested.
+ */
+typedef void (^FIRSignInMethodQueryCallback)(NSArray<NSString *> *_Nullable,
+ NSError *_Nullable)
+ NS_SWIFT_NAME(SignInMethodQueryCallback);
+
/** @typedef FIRSendPasswordResetCallback
@brief The type of block invoked when sending a password reset email.
@@ -123,6 +131,12 @@ typedef void (^FIRProviderQueryCallback)(NSArray<NSString *> *_Nullable provider
typedef void (^FIRSendPasswordResetCallback)(NSError *_Nullable error)
NS_SWIFT_NAME(SendPasswordResetCallback);
+/** @typedef FIRSendSignInLinkToEmailCallback
+ @brief The type of block invoked when sending an email sign-in link email.
+ */
+typedef void (^FIRSendSignInLinkToEmailCallback)(NSError *_Nullable error)
+ NS_SWIFT_NAME(SendSignInLinkToEmailCallback);
+
/** @typedef FIRConfirmPasswordResetCallback
@brief The type of block invoked when performing a password reset.
@@ -190,6 +204,10 @@ typedef NS_ENUM(NSInteger, FIRActionCodeOperation) {
/** Action code for recover email operation. */
FIRActionCodeOperationRecoverEmail = 3,
+ /** Action code for email link operation. */
+ FIRActionCodeOperationEmailLink = 4,
+
+
} NS_SWIFT_NAME(ActionCodeOperation);
/**
@@ -297,6 +315,24 @@ NS_SWIFT_NAME(Auth)
- (void)fetchProvidersForEmail:(NSString *)email
completion:(nullable FIRProviderQueryCallback)completion;
+/** @fn fetchSignInMethodsForEmail:completion:
+ @brief Fetches the list of all sign-in methods previously used for the provided email address.
+
+ @param email The email address for which to obtain a list of sign-in methods.
+ @param completion Optionally; a block which is invoked when the list of sign in methods for the
+ specified email address is ready or an error was encountered. Invoked asynchronously on the
+ main thread in the future.
+
+ @remarks Possible error codes:
+
+ + `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is malformed.
+
+ @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods.
+ */
+
+- (void)fetchSignInMethodsForEmail:(NSString *)email
+ completion:(nullable FIRSignInMethodQueryCallback)completion;
+
/** @fn signInWithEmail:password:completion:
@brief Signs in using an email address and password.
@@ -322,6 +358,30 @@ NS_SWIFT_NAME(Auth)
password:(NSString *)password
completion:(nullable FIRAuthResultCallback)completion;
+/** @fn signInWithEmail:link:completion:
+ @brief Signs in using an email address and email sign-in link.
+
+ @param email The user's email address.
+ @param link The email sign-in link.
+ @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:
+
+ + `FIRAuthErrorCodeOperationNotAllowed` - Indicates that email and email sign-in link
+ accounts are not enabled. Enable them in the Auth section of the
+ Firebase console.
+ + `FIRAuthErrorCodeUserDisabled` - Indicates the user's account is disabled.
+ + `FIRAuthErrorCodeInvalidEmail` - Indicates the email address is invalid.
+
+
+ @remarks See `FIRAuthErrors` for a list of error codes that are common to all API methods.
+ */
+
+- (void)signInWithEmail:(NSString *)email
+ link:(NSString *)link
+ completion:(nullable FIRAuthDataResultCallback)completion;
+
/** @fn signInAndRetrieveDataWithEmail:password:completion:
@brief Signs in using an email address and password.
@@ -654,6 +714,19 @@ NS_SWIFT_NAME(Auth)
actionCodeSettings:(FIRActionCodeSettings *)actionCodeSettings
completion:(nullable FIRSendPasswordResetCallback)completion;
+/** @fn sendSignInLinkToEmail:actionCodeSettings:completion:
+ @brief Sends a sign in with email link to provided email address.
+
+ @param email The email address of the user.
+ @param actionCodeSettings An @c FIRActionCodeSettings object containing settings related to
+ handling action codes.
+ @param completion Optionally; a block which is invoked when the request finishes. Invoked
+ asynchronously on the main thread in the future.
+ */
+- (void)sendSignInLinkToEmail:(NSString *)email
+ actionCodeSettings:(FIRActionCodeSettings *)actionCodeSettings
+ completion:(nullable FIRSendSignInLinkToEmailCallback)completion;
+
/** @fn signOut:
@brief Signs out the current user.
@@ -672,6 +745,14 @@ NS_SWIFT_NAME(Auth)
*/
- (BOOL)signOut:(NSError *_Nullable *_Nullable)error;
+/** @fn isSignInWithEmailLink
+ @brief Checks if link is an email sign-in link.
+
+ @param link The email sign-in link.
+ @return @YES when the link passed matches the expected format of an email sign-in link.
+ */
+- (BOOL)isSignInWithEmailLink:(NSString *)link;
+
/** @fn addAuthStateDidChangeListener:
@brief Registers a block as an "auth state did change" listener. To be invoked when:
diff --git a/Firebase/Auth/Source/Public/FIREmailAuthProvider.h b/Firebase/Auth/Source/Public/FIREmailAuthProvider.h
index 99cd018..5823911 100644
--- a/Firebase/Auth/Source/Public/FIREmailAuthProvider.h
+++ b/Firebase/Auth/Source/Public/FIREmailAuthProvider.h
@@ -51,6 +51,15 @@ typedef FIREmailAuthProvider FIREmailPasswordAuthProvider __attribute__((depreca
*/
+ (FIRAuthCredential *)credentialWithEmail:(NSString *)email password:(NSString *)password;
+/** @fn credentialWithEmail:Link:
+ @brief Creates an `FIRAuthCredential` for an email & link sign in.
+
+ @param email The user's email address.
+ @param link The email sign-in link.
+ @return A FIRAuthCredential containing the email & link credential.
+ */
++ (FIRAuthCredential *)credentialWithEmail:(NSString *)email link:(NSString *)link;
+
/** @fn init
@brief This class is not meant to be initialized.
*/
diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.h b/Firebase/Auth/Source/RPCs/FIRAuthBackend.h
index a82c3a7..5928e71 100644
--- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.h
+++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.h
@@ -19,6 +19,8 @@
@class FIRAuthRequestConfiguration;
@class FIRCreateAuthURIRequest;
@class FIRCreateAuthURIResponse;
+@class FIREmailLinkSignInRequest;
+@class FIREmailLinkSignInResponse;
@class FIRGetAccountInfoRequest;
@class FIRGetAccountInfoResponse;
@class FIRGetProjectConfigRequest;
@@ -130,6 +132,16 @@ typedef void (^FIRVerifyAssertionResponseCallback)
typedef void (^FIRVerifyPasswordResponseCallback)
(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error);
+/** @typedef FIREmailLinkSigninResponseCallback
+ @brief The type of block used to return the result of a call to the emailLinkSignin
+ endpoint.
+ @param response The received response, if any.
+ @param error The error which occurred, if any.
+ @remarks One of response or error will be non-nil.
+ */
+typedef void (^FIREmailLinkSigninResponseCallback)
+ (FIREmailLinkSignInResponse *_Nullable response, NSError *_Nullable error);
+
/** @typedef FIRVerifyCustomTokenResponseCallback
@brief The type of block used to return the result of a call to the verifyCustomToken
endpoint.
@@ -296,6 +308,15 @@ typedef void (^FIRVerifyClientResponseCallback)
+ (void)verifyPassword:(FIRVerifyPasswordRequest *)request
callback:(FIRVerifyPasswordResponseCallback)callback;
+/** @fn emailLinkSignin:callback:
+ @brief Calls the emailLinkSignin endpoint, which is responsible for authenticating a
+ user through passwordless sign-in.
+ @param request The request parameters.
+ @param callback The callback.
+ */
++ (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
+ callback:(FIREmailLinkSigninResponseCallback)callback;
+
/** @fn secureToken:callback:
@brief Calls the token endpoint, which is responsible for performing STS token exchanges and
token refreshes.
@@ -461,6 +482,15 @@ typedef void (^FIRVerifyClientResponseCallback)
- (void)verifyPassword:(FIRVerifyPasswordRequest *)request
callback:(FIRVerifyPasswordResponseCallback)callback;
+/** @fn emailLinkSignin:callback:
+ @brief Calls the emailLinkSignin endpoint, which is responsible for authenticating a
+ user through passwordless sign-in.
+ @param request The request parameters.
+ @param callback The callback.
+ */
+- (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
+ callback:(FIREmailLinkSigninResponseCallback)callback;
+
/** @fn secureToken:callback:
@brief Calls the token endpoint, which is responsible for performing STS token exchanges and
token refreshes.
@@ -472,7 +502,7 @@ typedef void (^FIRVerifyClientResponseCallback)
/** @fn getOOBConfirmationCode:callback:
@brief Calls the getOOBConfirmationCode endpoint, which is responsible for sending email change
- request emails, and password reset emails.
+ request emails, email sign-in link emails, and password reset emails.
@param request The request parameters.
@param callback The callback.
*/
diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m
index 6b5232b..e380e34 100644
--- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m
+++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m
@@ -51,6 +51,8 @@
#import "FIRVerifyCustomTokenResponse.h"
#import "FIRVerifyPasswordRequest.h"
#import "FIRVerifyPasswordResponse.h"
+#import "FIREmailLinkSignInRequest.h"
+#import "FIREmailLinkSignInResponse.h"
#import "FIRVerifyPhoneNumberRequest.h"
#import "FIRVerifyPhoneNumberResponse.h"
#import <GTMSessionFetcher/GTMSessionFetcher.h>
@@ -430,6 +432,11 @@ static id<FIRAuthBackendImplementation> gBackendImplementation;
[[self implementation] verifyPassword:request callback:callback];
}
++ (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
+ callback:(FIREmailLinkSigninResponseCallback)callback {
+ [[self implementation] emailLinkSignin:request callback:callback];
+}
+
+ (void)secureToken:(FIRSecureTokenRequest *)request
callback:(FIRSecureTokenResponseCallback)callback {
[[self implementation] secureToken:request callback:callback];
@@ -623,6 +630,18 @@ static id<FIRAuthBackendImplementation> gBackendImplementation;
}];
}
+- (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
+ callback:(FIREmailLinkSigninResponseCallback)callback {
+ FIREmailLinkSignInResponse *response = [[FIREmailLinkSignInResponse alloc] init];
+ [self postWithRequest:request response:response callback:^(NSError *error) {
+ if (error) {
+ callback(nil, error);
+ } else {
+ callback(response, nil);
+ }
+ }];
+}
+
- (void)secureToken:(FIRSecureTokenRequest *)request
callback:(FIRSecureTokenResponseCallback)callback {
FIRSecureTokenResponse *response = [[FIRSecureTokenResponse alloc] init];
diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h
index 9f6cbae..8e8f7b0 100644
--- a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h
+++ b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h
@@ -51,6 +51,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property(nonatomic, copy, readonly, nullable) NSArray<NSString *> *allProviders;
+/** @property signinMethods
+ @brief A list of sign-in methods available for the passed @c identifier.
+ */
+@property(nonatomic, copy, readonly, nullable) NSArray<NSString *> *signinMethods;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m
index 12ef97c..6f2937f 100644
--- a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m
+++ b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m
@@ -25,6 +25,7 @@
_registered = [dictionary[@"registered"] boolValue];
_forExistingProvider = [dictionary[@"forExistingProvider"] boolValue];
_allProviders = [dictionary[@"allProviders"] copy];
+ _signinMethods = [dictionary[@"signinMethods"] copy];
return YES;
}
diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h
new file mode 100644
index 0000000..e1b10d8
--- /dev/null
+++ b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "FIRAuthRPCRequest.h"
+#import "FIRIdentityToolkitRequest.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** @class FIREmailLinkSignInRequest
+ @brief Represents the parameters for the emailLinkSignin endpoint.
+ */
+@interface FIREmailLinkSignInRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest>
+
+#pragma mark - Components of "postBody"
+
+/** @property email
+ @brief The email identifier used to complete the email link sign-in.
+ */
+@property(nonatomic, copy, readonly) NSString *email;
+
+/** @property oobCode
+ @brief The OOB code used to complete the email link sign-in flow.
+ */
+@property(nonatomic, copy, readonly) NSString *oobCode;
+
+/** @property idToken
+ @brief The ID Token code potentially used to complete the email link sign-in flow.
+ */
+@property(nonatomic, copy) NSString *IDToken;
+
+/** @fn initWithEndpoint:requestConfiguration:
+ @brief Please use initWithProviderID:requestConfifuration instead.
+ */
+- (instancetype)initWithEndpoint:(NSString *)endpoint
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration NS_UNAVAILABLE;
+
+/** @fn initWithProviderID:requestConfifuration
+ @brief Designated initializer.
+ @param email The email identifier used to complete hte email link sign-in flow.
+ @param oobCode The OOB code used to complete the email link sign-in flow.
+ @param requestConfiguration An object containing configurations to be added to the request.
+
+ */
+- (instancetype)initWithEmail:(NSString *)email
+ oobCode:(NSString *)oobCode
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration
+ NS_DESIGNATED_INITIALIZER;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m
new file mode 100644
index 0000000..9787e8e
--- /dev/null
+++ b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInRequest.m
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FIREmailLinkSignInRequest.h"
+
+/** @var kEmailLinkSigninEndpoint
+ @brief The "EmailLinkSignin" endpoint.
+ */
+static NSString *const kEmailLinkSigninEndpoint = @"emailLinkSignin";
+
+/** @var kEmailKey
+ @brief The key for the "identifier" value in the request.
+ */
+static NSString *const kEmailKey = @"email";
+
+/** @var kEmailLinkKey
+ @brief The key for the "emailLink" value in the request.
+ */
+static NSString *const kOOBCodeKey = @"oobCode";
+
+/** @var kIDTokenKey
+ @brief The key for the "IDToken" value in the request.
+ */
+static NSString *const kIDTokenKey = @"idToken";
+
+/** @var kPostBodyKey
+ @brief The key for the "postBody" value in the request.
+ */
+static NSString *const kPostBodyKey = @"postBody";
+
+@implementation FIREmailLinkSignInRequest
+
+- (instancetype)initWithEmail:(NSString *)email
+ oobCode:(NSString *)oobCode
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration {
+ self = [super initWithEndpoint:kEmailLinkSigninEndpoint
+ requestConfiguration:requestConfiguration];
+ if (self) {
+ _email = email;
+ _oobCode = oobCode;
+ }
+ return self;
+}
+
+- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error {
+ NSMutableDictionary *postBody = [@{
+ kEmailKey : _email,
+ kOOBCodeKey : _oobCode,
+ } mutableCopy];
+
+ if (_IDToken) {
+ postBody[kIDTokenKey] = _IDToken;
+ }
+ return postBody;
+}
+
+@end
diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h
new file mode 100644
index 0000000..df0a127
--- /dev/null
+++ b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#import <Foundation/Foundation.h>
+
+#import "FIRAuthRPCResponse.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** @class FIRVerifyAssertionResponse
+ @brief Represents the response from the emailLinkSignin endpoint.
+ */
+@interface FIREmailLinkSignInResponse : NSObject<FIRAuthRPCResponse>
+
+/** @property IDToken
+ @brief The ID token in the email link sign-in response.
+ */
+@property(nonatomic, copy, readonly) NSString *IDToken;
+
+/** @property email
+ @brief The email returned by the IdP.
+ */
+@property(nonatomic, strong, readonly, nullable) NSString *email;
+
+/** @property refreshToken
+ @brief The refreshToken returned by the server.
+ */
+@property(nonatomic, strong, readonly, nullable) NSString *refreshToken;
+
+/** @property approximateExpirationDate
+ @brief The approximate expiration date of the access token.
+ */
+@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate;
+
+/** @property isNewUser
+ @brief Flag indicating that the user signing in is a new user and not a returning user.
+ */
+@property(nonatomic, assign) BOOL isNewUser;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m
new file mode 100644
index 0000000..cd36d41
--- /dev/null
+++ b/Firebase/Auth/Source/RPCs/FIREmailLinkSignInResponse.m
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "FIREmailLinkSignInResponse.h"
+
+@implementation FIREmailLinkSignInResponse
+
+- (BOOL)setWithDictionary:(NSDictionary *)dictionary
+ error:(NSError *_Nullable *_Nullable)error {
+ _email = [dictionary[@"email"] copy];
+ _IDToken = [dictionary[@"idToken"] copy];
+ _isNewUser = [dictionary[@"isNewUser"] boolValue];
+ _refreshToken = [dictionary[@"refreshToken"] copy];
+ _approximateExpirationDate = [dictionary[@"expiresIn"] isKindOfClass:[NSString class]] ?
+ [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"expiresIn"] doubleValue]] : nil;
+ return YES;
+}
+
+@end
diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h
index abd59b4..751cfe7 100644
--- a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h
+++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h
@@ -36,6 +36,11 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) {
@brief Requests an email verification code.
*/
FIRGetOOBConfirmationCodeRequestTypeVerifyEmail,
+
+ /** @var FIRGetOOBConfirmationCodeRequestTypeEmailLink
+ @brief Requests an email sign-in link.
+ */
+ FIRGetOOBConfirmationCodeRequestTypeEmailLink,
};
/** @enum FIRGetOOBConfirmationCodeRequest
@@ -91,7 +96,7 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) {
*/
@property(assign, nonatomic) BOOL handleCodeInApp;
-/** @fn passwordResetRequestWithEmail:APIKey:
+/** @fn passwordResetRequestWithEmail:actionCodeSettings:requestConfiguration:
@brief Creates a password reset request.
@param email The user's email address.
@param actionCodeSettings An object of FIRActionCodeSettings which specifies action code
@@ -104,7 +109,7 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) {
actionCodeSettings:(nullable FIRActionCodeSettings *)actionCodeSettings
requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration;
-/** @fn verifyEmailRequestWithAccessToken:APIKey:
+/** @fn verifyEmailRequestWithAccessToken:actionCodeSettings:requestConfiguration:
@brief Creates a password reset request.
@param accessToken The user's STS Access Token.
@param actionCodeSettings An object of FIRActionCodeSettings which specifies action code
@@ -117,6 +122,19 @@ typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) {
actionCodeSettings:(nullable FIRActionCodeSettings *)actionCodeSettings
requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration;
+/** @fn signInWithEmailLinkRequest:actionCodeSettings:requestConfiguration:
+ @brief Creates a sign-in with email link.
+ @param email The user's email address.
+ @param actionCodeSettings An object of FIRActionCodeSettings which specifies action code
+ settings to be applied to the email sign-in link.
+ @param requestConfiguration An object containing configurations to be added to the request.
+ @return An email sign-in link request.
+ */
++ (nullable FIRGetOOBConfirmationCodeRequest *)
+ signInWithEmailLinkRequest:(NSString *)email
+ actionCodeSettings:(nullable FIRActionCodeSettings *)actionCodeSettings
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration;
+
/** @fn init
@brief Please use a factory method.
*/
diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m
index 653eddd..438f24b 100644
--- a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m
+++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m
@@ -79,6 +79,11 @@ static NSString *const kCanHandleCodeInAppKey = @"canHandleCodeInApp";
*/
static NSString *const kPasswordResetRequestTypeValue = @"PASSWORD_RESET";
+/** @var kEmailLinkSignInTypeValue
+ @brief The value for the "EMAIL_SIGNIN" request type.
+ */
+static NSString *const kEmailLinkSignInTypeValue= @"EMAIL_SIGNIN";
+
/** @var kVerifyEmailRequestTypeValue
@brief The value for the "VERIFY_EMAIL" request type.
*/
@@ -116,6 +121,8 @@ static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL";
return kPasswordResetRequestTypeValue;
case FIRGetOOBConfirmationCodeRequestTypeVerifyEmail:
return kVerifyEmailRequestTypeValue;
+ case FIRGetOOBConfirmationCodeRequestTypeEmailLink:
+ return kEmailLinkSignInTypeValue;
// No default case so that we get a compiler warning if a new value was added to the enum.
}
}
@@ -142,6 +149,17 @@ static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL";
requestConfiguration:requestConfiguration];
}
++ (FIRGetOOBConfirmationCodeRequest *)
+ signInWithEmailLinkRequest:(NSString *)email
+ actionCodeSettings:(nullable FIRActionCodeSettings *)actionCodeSettings
+ requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration {
+ return [[self alloc] initWithRequestType:FIRGetOOBConfirmationCodeRequestTypeEmailLink
+ email:email
+ accessToken:nil
+ actionCodeSettings:actionCodeSettings
+ requestConfiguration:requestConfiguration];
+}
+
- (nullable instancetype)initWithRequestType:(FIRGetOOBConfirmationCodeRequestType)requestType
email:(nullable NSString *)email
accessToken:(nullable NSString *)accessToken
@@ -180,6 +198,12 @@ static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL";
body[kIDTokenKey] = _accessToken;
}
+ // For email sign-in link requests, we only need an email address in addition to the already
+ // required fields.
+ if (_requestType == FIRGetOOBConfirmationCodeRequestTypeEmailLink) {
+ body[kEmailKey] = _email;
+ }
+
if (_continueURL) {
body[kContinueURLKey] = _continueURL;
}