From fbe9f9c30c025842a3657055f8dfabbc77f65bf2 Mon Sep 17 00:00:00 2001 From: Zsika Phillip Date: Wed, 4 Apr 2018 07:14:36 -0700 Subject: Adds support for deep link to Email/Link sign-In (#1023) --- Example/Auth/Tests/FIRAuthTests.m | 52 +++++++++++++++++++++++++++++++++++++-- Firebase/Auth/Source/FIRAuth.m | 32 ++++++++++++++++++++---- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/Example/Auth/Tests/FIRAuthTests.m b/Example/Auth/Tests/FIRAuthTests.m index 2e2d2c5..507fdf2 100644 --- a/Example/Auth/Tests/FIRAuthTests.m +++ b/Example/Auth/Tests/FIRAuthTests.m @@ -196,11 +196,20 @@ static NSString *const kFIRFacebookAuthSignInMethod = @"facebook.com"; */ static NSString *const kBadSignInEmailLink = @"http://www.facebook.com"; +/** @var kFakeEmailSignInDeeplink + @brief Fake email sign-in link + */ +static NSString *const kFakeEmailSignInDeeplink = + @"https://example.domain.com/?apiKey=testAPIKey&oobCode=testoobcode&mode=signIn"; + /** @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"; +static NSString *const kFakeEmailSignInlink = @"https://test.app.goo.gl/?link=https://test.firebase" + "app.com/__/auth/action?apiKey%3DtestAPIKey%26mode%3DsignIn%26oobCode%3Dtestoobcode%26continueU" + "rl%3Dhttps://test.apps.com&ibi=com.test.com&ifl=https://test.firebaseapp.com/__/auth/action?ap" + "iKey%3DtestAPIKey%26mode%3DsignIn%26oobCode%3Dtestoobcode%26continueUrl%3Dhttps://test.apps.co" + "m"; /** @var kExpectationTimeout @brief The maximum time waiting for expectations to fulfill. @@ -625,6 +634,44 @@ static const NSTimeInterval kWaitInterval = .5; OCMVerifyAll(_mockBackend); } +/** @fn testSignInWithEmailLinkSuccessDeeplink + @brief Tests the flow of a successful @c signInWithEmail:link:completion: call using a deep + link. + */ +- (void)testSignInWithEmailLinkSuccessDeeplink { + 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:kFakeEmailSignInDeeplink + 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. */ @@ -1894,6 +1941,7 @@ static const NSTimeInterval kWaitInterval = .5; */ - (void)testIsSignInWithEmailLink { XCTAssertTrue([[FIRAuth auth] isSignInWithEmailLink:kFakeEmailSignInlink]); + XCTAssertTrue([[FIRAuth auth] isSignInWithEmailLink:kFakeEmailSignInDeeplink]); XCTAssertFalse([[FIRAuth auth] isSignInWithEmailLink:kBadSignInEmailLink]); XCTAssertFalse([[FIRAuth auth] isSignInWithEmailLink:@""]); } diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m index d4ced9c..1930957 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/FIRAuth.m @@ -112,6 +112,10 @@ static NSString *const kHandleCodeInAppFalseExceptionReason = @"You must set handleCodeInApp in your ActionCodeSettings to true for Email-link " "Authentication."; +static NSString *const kInvalidEmailSignInLinkExceptionMessage = + @"The link provided is not valid for email/link sign-in. Please check the link by calling " + "isSignInWithEmailLink:link: on Auth before attempting to use it for email/link sign-in."; + /** @var kPasswordResetRequestType @brief The action code type value for resetting password in the check action code response. */ @@ -655,8 +659,16 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; - (void)internalSignInWithEmail:(nonnull NSString *)email link:(nonnull NSString *)link callback:(nullable FIRAuthResultCallback)callback { - NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link]; - NSDictionary *queryItems = FIRAuthParseURL(urlComponents.query); + if (![self isSignInWithEmailLink:link]) { + [FIRAuthExceptionUtils raiseInvalidParameterExceptionWithReason: + kInvalidEmailSignInLinkExceptionMessage]; + return; + } + NSDictionary *queryItems = FIRAuthParseURL(link); + if (![queryItems count]) { + NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link]; + queryItems = FIRAuthParseURL(urlComponents.query); + } NSString *actionCode = queryItems[@"oobCode"]; FIREmailLinkSignInRequest *request = @@ -1206,11 +1218,18 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; if (link.length == 0) { return NO; } - NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link]; - if (!urlComponents.query) { + NSDictionary *queryItems = FIRAuthParseURL(link); + if (![queryItems count]) { + NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link]; + if (!urlComponents.query) { + return NO; + } + queryItems = FIRAuthParseURL(urlComponents.query); + } + + if (![queryItems count]) { return NO; } - NSDictionary *queryItems = FIRAuthParseURL(urlComponents.query); NSString *actionCode = queryItems[@"oobCode"]; NSString *mode = queryItems[@"mode"]; @@ -1228,6 +1247,9 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; */ static NSDictionary *FIRAuthParseURL(NSString *urlString) { NSString *linkURL = [NSURLComponents componentsWithString:urlString].query; + if (!linkURL) { + return @{}; + } NSArray *URLComponents = [linkURL componentsSeparatedByString:@"&"]; NSMutableDictionary *queryItems = [[NSMutableDictionary alloc] initWithCapacity:URLComponents.count]; -- cgit v1.2.3