mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
* Check if reverse client ID is registered as a custom URL scheme before setting it as the callback scheme. * Fix existing tests. * Add tests. * Update changelog.
1167 lines
57 KiB
Objective-C
1167 lines
57 KiB
Objective-C
/*
|
|
* 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 <TargetConditionals.h>
|
|
#if TARGET_OS_IOS
|
|
|
|
#import <XCTest/XCTest.h>
|
|
#import "OCMock.h"
|
|
|
|
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuthErrors.h"
|
|
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuthUIDelegate.h"
|
|
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIROAuthProvider.h"
|
|
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
|
|
|
|
#import "FirebaseAuth/Sources/Auth/FIRAuthGlobalWorkQueue.h"
|
|
#import "FirebaseAuth/Sources/Auth/FIRAuth_Internal.h"
|
|
#import "FirebaseAuth/Sources/AuthProvider/OAuth/FIROAuthCredential_Internal.h"
|
|
#import "FirebaseAuth/Sources/Backend/FIRAuthBackend.h"
|
|
#import "FirebaseAuth/Sources/Backend/FIRAuthRequestConfiguration.h"
|
|
#import "FirebaseAuth/Sources/Backend/RPC/FIRGetProjectConfigRequest.h"
|
|
#import "FirebaseAuth/Sources/Backend/RPC/FIRGetProjectConfigResponse.h"
|
|
#import "FirebaseAuth/Sources/Utilities/FIRAuthErrorUtils.h"
|
|
#import "FirebaseAuth/Sources/Utilities/FIRAuthURLPresenter.h"
|
|
#import "FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.h"
|
|
#import "FirebaseAuth/Tests/Unit/OCMStubRecorder+FIRAuthUnitTests.h"
|
|
|
|
/** @var kExpectationTimeout
|
|
@brief The maximum time waiting for expectations to fulfill.
|
|
*/
|
|
static const NSTimeInterval kExpectationTimeout = 1;
|
|
|
|
/** @var kFakeAuthorizedDomain
|
|
@brief A fake authorized domain for the app.
|
|
*/
|
|
static NSString *const kFakeAuthorizedDomain = @"test.firebaseapp.com";
|
|
|
|
/** @var kFakeBundleID
|
|
@brief A fake bundle ID.
|
|
*/
|
|
static NSString *const kFakeBundleID = @"com.firebaseapp.example";
|
|
|
|
/** @var kFakeAccessToken
|
|
@brief A fake access token for testing.
|
|
*/
|
|
static NSString *const kFakeAccessToken = @"fakeAccessToken";
|
|
|
|
/** @var kFakeIDToken
|
|
@brief A fake ID token for testing.
|
|
*/
|
|
static NSString *const kFakeIDToken = @"fakeIDToken";
|
|
|
|
/** @var kFakeProviderID
|
|
@brief A fake provider ID for testing.
|
|
*/
|
|
static NSString *const kFakeProviderID = @"fakeProviderID";
|
|
|
|
/** @var kFakeAPIKey
|
|
@brief A fake API key.
|
|
*/
|
|
static NSString *const kFakeAPIKey = @"asdfghjkl";
|
|
|
|
/** @var kFakeEmulatorHost
|
|
@brief A fake emulator host.
|
|
*/
|
|
static NSString *const kFakeEmulatorHost = @"emulatorhost";
|
|
|
|
/** @var kFakeEmulatorPort
|
|
@brief A fake emulator port.
|
|
*/
|
|
static NSString *const kFakeEmulatorPort = @"12345";
|
|
|
|
/** @var kFakeClientID
|
|
@brief A fake client ID.
|
|
*/
|
|
static NSString *const kFakeClientID = @"123456.apps.googleusercontent.com";
|
|
|
|
/** @var kFakeReverseClientID
|
|
@brief The dot-reversed version of the fake client ID.
|
|
*/
|
|
static NSString *const kFakeReverseClientID = @"com.googleusercontent.apps.123456";
|
|
|
|
/** @var kFakeFirebaseAppID
|
|
@brief A fake Firebase app ID.
|
|
*/
|
|
static NSString *const kFakeFirebaseAppID = @"1:123456789:ios:123abc456def";
|
|
|
|
/** @var kFakeEncodedFirebaseAppID
|
|
@brief A fake encoded Firebase app ID to be used as a custom URL scheme.
|
|
*/
|
|
static NSString *const kFakeEncodedFirebaseAppID = @"app-1-123456789-ios-123abc456def";
|
|
|
|
/** @var kFakeOAuthResponseURL
|
|
@brief A fake OAuth response URL used in test.
|
|
*/
|
|
static NSString *const kFakeOAuthResponseURL = @"fakeOAuthResponseURL";
|
|
|
|
/** @var kFakeRedirectURLResponseURL
|
|
@brief A fake callback URL (minus the scheme) containing a fake response URL.
|
|
*/
|
|
static NSString *const kFakeRedirectURLResponseURL =
|
|
@"://firebaseauth/"
|
|
@"link?deep_link_id=https%3A%2F%2Fexample.firebaseapp.com%2F__%2Fauth%2Fcallback%3FauthType%"
|
|
@"3DsignInWithRedirect%26link%3D";
|
|
|
|
/** @var kFakeRedirectURLBaseErrorString
|
|
@brief The base for a fake redirect URL string that contains an error.
|
|
*/
|
|
static NSString *const kFakeRedirectURLBaseErrorString =
|
|
@"com.googleusercontent.apps.123456://fire"
|
|
"baseauth/link?deep_link_id=https%3A%2F%2Fexample.firebaseapp.com%2F__%2Fauth%2Fcallback%3f";
|
|
|
|
/** @var kNetworkRequestFailedErrorString
|
|
@brief The error message returned if a network request failure occurs within the web context.
|
|
*/
|
|
static NSString *const kNetworkRequestFailedErrorString =
|
|
@"firebaseError%3D%257B%2522code%2"
|
|
"522%253A%2522auth%252Fnetwork-request-failed%2522%252C%2522message%2522%253A%2522The%"
|
|
"2520netwo"
|
|
"rk%2520request%2520failed%2520.%2522%257D%26authType%3DsignInWithRedirect";
|
|
|
|
/** @var kInvalidClientIDString
|
|
@brief The error message returned if the client ID used is invalid.
|
|
*/
|
|
static NSString *const kInvalidClientIDString =
|
|
@"firebaseError%3D%257B%2522code%2522%253A%2522auth"
|
|
"%252Finvalid-oauth-client-id%2522%252C%2522message%2522%253A%2522The%2520OAuth%2520client%"
|
|
"2520"
|
|
"ID%2520provided%2520is%2520either%2520invalid%2520or%2520does%2520not%2520match%2520the%"
|
|
"2520sp"
|
|
"ecified%2520API%2520key.%2522%257D%26authType%3DsignInWithRedirect";
|
|
|
|
/** @var kInternalErrorString
|
|
@brief The error message returned if there is an internal error within the web context.
|
|
*/
|
|
static NSString *const kInternalErrorString =
|
|
@"firebaseError%3D%257B%2522code%2522%253"
|
|
"A%2522auth%252Finternal-error%2522%252C%2522message%2522%253A%2522Internal%2520error%2520.%"
|
|
"252"
|
|
"2%257D%26authType%3DsignInWithRedirect";
|
|
|
|
/** @var kUnknownErrorString
|
|
@brief The error message returned if an unknown error is returned from the web context.
|
|
*/
|
|
static NSString *const kUnknownErrorString =
|
|
@"firebaseError%3D%257B%2522code%2522%253A%2522auth%2"
|
|
"52Funknown-error-id%2522%252C%2522message%2522%253A%2522The%2520OAuth%2520client%2520ID%"
|
|
"2520pr"
|
|
"ovided%2520is%2520either%2520invalid%2520or%2520does%2520not%2520match%2520the%2520specified%"
|
|
"2"
|
|
"520API%2520key.%2522%257D%26authType%3DsignInWithRedirect";
|
|
|
|
@interface FIROAuthProviderTests : XCTestCase
|
|
|
|
@end
|
|
|
|
@implementation FIROAuthProviderTests {
|
|
/** @var _mockBackend
|
|
@brief The mock @c FIRAuthBackendImplementation.
|
|
*/
|
|
id _mockBackend;
|
|
|
|
/** @var _provider
|
|
@brief The @c FIROAuthProvider instance under test.
|
|
*/
|
|
FIROAuthProvider *_provider;
|
|
|
|
/** @var _mockAuth
|
|
@brief The mock @c FIRAuth instance associated with @c _provider.
|
|
*/
|
|
id _mockAuth;
|
|
|
|
/** @var _mockURLPresenter
|
|
@brief The mock @c FIRAuthURLPresenter instance associated with @c _mockAuth.
|
|
*/
|
|
id _mockURLPresenter;
|
|
|
|
/** @var _mockApp
|
|
@brief The mock @c FIRApp instance associated with @c _mockAuth.
|
|
*/
|
|
id _mockApp;
|
|
|
|
/** @var _mockOptions
|
|
@brief The mock @c FIROptions instance associated with @c _mockApp.
|
|
*/
|
|
id _mockOptions;
|
|
|
|
/** @var _mockRequestConfiguration
|
|
@brief The mock @c FIRAuthRequestConfiguration instance associated with @c _mockAuth.
|
|
*/
|
|
id _mockRequestConfiguration;
|
|
}
|
|
|
|
- (void)setUp {
|
|
[super setUp];
|
|
_mockBackend = OCMProtocolMock(@protocol(FIRAuthBackendImplementation));
|
|
[FIRAuthBackend setBackendImplementation:_mockBackend];
|
|
_mockAuth = OCMClassMock([FIRAuth class]);
|
|
_mockApp = OCMClassMock([FIRApp class]);
|
|
OCMStub([_mockAuth app]).andReturn(_mockApp);
|
|
_mockOptions = OCMClassMock([FIROptions class]);
|
|
OCMStub([(FIRApp *)_mockApp options]).andReturn(_mockOptions);
|
|
OCMStub([_mockOptions googleAppID]).andReturn(kFakeFirebaseAppID);
|
|
_mockURLPresenter = OCMClassMock([FIRAuthURLPresenter class]);
|
|
OCMStub([_mockAuth authURLPresenter]).andReturn(_mockURLPresenter);
|
|
_mockRequestConfiguration = OCMClassMock([FIRAuthRequestConfiguration class]);
|
|
OCMStub([_mockAuth requestConfiguration]).andReturn(_mockRequestConfiguration);
|
|
OCMStub([_mockRequestConfiguration APIKey]).andReturn(kFakeAPIKey);
|
|
}
|
|
|
|
/** @fn testObtainingOAuthCredentialNoIDToken
|
|
@brief Tests the correct creation of an OAuthCredential without an IDToken.
|
|
*/
|
|
- (void)testObtainingOAuthCredentialNoIDToken {
|
|
FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:kFakeProviderID
|
|
accessToken:kFakeAccessToken];
|
|
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
|
|
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
|
|
XCTAssertEqualObjects(OAuthCredential.accessToken, kFakeAccessToken);
|
|
XCTAssertEqualObjects(OAuthCredential.provider, kFakeProviderID);
|
|
XCTAssertNil(OAuthCredential.IDToken);
|
|
}
|
|
|
|
/** @fn testObtainingOAuthCredentialWithIDToken
|
|
@brief Tests the correct creation of an OAuthCredential with an IDToken
|
|
*/
|
|
- (void)testObtainingOAuthCredentialWithIDToken {
|
|
FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:kFakeProviderID
|
|
IDToken:kFakeIDToken
|
|
accessToken:kFakeAccessToken];
|
|
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
|
|
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
|
|
XCTAssertEqualObjects(OAuthCredential.accessToken, kFakeAccessToken);
|
|
XCTAssertEqualObjects(OAuthCredential.provider, kFakeProviderID);
|
|
XCTAssertEqualObjects(OAuthCredential.IDToken, kFakeIDToken);
|
|
}
|
|
|
|
/** @fn testObtainingOAuthProvider
|
|
@brief Tests the correct creation of an FIROAuthProvider instance.
|
|
*/
|
|
- (void)testObtainingOAuthProvider {
|
|
id mockAuth = OCMClassMock([FIRAuth class]);
|
|
id mockApp = OCMClassMock([FIRApp class]);
|
|
OCMStub([mockAuth app]).andReturn(mockApp);
|
|
id mockOptions = OCMClassMock([FIROptions class]);
|
|
OCMStub([mockOptions clientID]).andReturn(kFakeClientID);
|
|
OCMStub([mockOptions googleAppID]).andReturn(kFakeFirebaseAppID);
|
|
OCMStub([(FIRApp *)mockApp options]).andReturn(mockOptions);
|
|
FIROAuthProvider *OAuthProvider = [FIROAuthProvider providerWithProviderID:kFakeProviderID
|
|
auth:mockAuth];
|
|
XCTAssertTrue([OAuthProvider isKindOfClass:[FIROAuthProvider class]]);
|
|
XCTAssertEqualObjects(OAuthProvider.providerID, kFakeProviderID);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateWithClientID
|
|
@brief Tests a successful invocation of @c getCredentialWithUIDelegte:completion:
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateWithClientID {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
|
|
.andCallBlock2(
|
|
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
|
|
XCTAssertNotNil(request);
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
|
|
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
|
|
kFakeAuthorizedDomain
|
|
]);
|
|
callback(mockGetProjectConfigResponse, nil);
|
|
});
|
|
});
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"https");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
|
|
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL = [NSMutableString
|
|
stringWithString:[kFakeReverseClientID
|
|
stringByAppendingString:kFakeRedirectURLResponseURL]];
|
|
// Add fake OAuthResponse to callback.
|
|
[redirectURL appendString:kFakeOAuthResponseURL];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(originalComponents.URL, nil);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider
|
|
getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(error);
|
|
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
|
|
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
|
|
XCTAssertEqualObjects(kFakeOAuthResponseURL,
|
|
OAuthCredential.OAuthResponseURLString);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateUserCancellationWithClientID
|
|
@brief Tests an unsuccessful invocation of @c getCredentialWithUIDelegte:completion: due to user
|
|
cancelation.
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateUserCancellationWithClientID {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
|
|
.andCallBlock2(
|
|
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
|
|
XCTAssertNotNil(request);
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
|
|
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
|
|
kFakeAuthorizedDomain
|
|
]);
|
|
callback(mockGetProjectConfigResponse, nil);
|
|
});
|
|
});
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"https");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
|
|
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL = [NSMutableString
|
|
stringWithString:[kFakeReverseClientID
|
|
stringByAppendingString:kFakeRedirectURLResponseURL]];
|
|
// Add fake OAuthResponse to callback.
|
|
[redirectURL appendString:kFakeOAuthResponseURL];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(nil, [FIRAuthErrorUtils webContextCancelledErrorWithMessage:nil]);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(credential);
|
|
XCTAssertEqual(FIRAuthErrorCodeWebContextCancelled, error.code);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateNetworkRequestFailedWithClientID
|
|
@brief Tests an unsuccessful invocation of @c getCredentialWithUIDelegte:completion: due to a
|
|
failed network request within the web context.
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateNetworkRequestFailedWithClientID {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
|
|
.andCallBlock2(
|
|
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
|
|
XCTAssertNotNil(request);
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
|
|
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
|
|
kFakeAuthorizedDomain
|
|
]);
|
|
callback(mockGetProjectConfigResponse, nil);
|
|
});
|
|
});
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"https");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
|
|
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL =
|
|
[NSMutableString stringWithString:kFakeRedirectURLBaseErrorString];
|
|
[redirectURL appendString:kNetworkRequestFailedErrorString];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(originalComponents.URL, nil);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(credential);
|
|
XCTAssertEqual(FIRAuthErrorCodeWebNetworkRequestFailed, error.code);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateInternalErrorWithClientID
|
|
@brief Tests an unsuccessful invocation of @c getCredentialWithUIDelegte:completion: due to an
|
|
internal error within the web context.
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateInternalErrorWithClientID {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
|
|
.andCallBlock2(
|
|
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
|
|
XCTAssertNotNil(request);
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
|
|
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
|
|
kFakeAuthorizedDomain
|
|
]);
|
|
callback(mockGetProjectConfigResponse, nil);
|
|
});
|
|
});
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"https");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
|
|
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL =
|
|
[NSMutableString stringWithString:kFakeRedirectURLBaseErrorString];
|
|
// Add internal error string to redirect URL.
|
|
[redirectURL appendString:kInternalErrorString];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(originalComponents.URL, nil);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(credential);
|
|
XCTAssertEqual(FIRAuthErrorCodeWebInternalError, error.code);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateInvalidClientID
|
|
@brief Tests an unsuccessful invocation of @c getCredentialWithUIDelegte:completion: due to an
|
|
use of an invalid client ID.
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateInvalidClientID {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
|
|
.andCallBlock2(
|
|
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
|
|
XCTAssertNotNil(request);
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
|
|
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
|
|
kFakeAuthorizedDomain
|
|
]);
|
|
callback(mockGetProjectConfigResponse, nil);
|
|
});
|
|
});
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"https");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
|
|
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL =
|
|
[NSMutableString stringWithString:kFakeRedirectURLBaseErrorString];
|
|
// Add invalid client ID error to redirect URL.
|
|
[redirectURL appendString:kInvalidClientIDString];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(originalComponents.URL, nil);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(credential);
|
|
XCTAssertEqual(FIRAuthErrorCodeInvalidClientID, error.code);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateUnknownErrorWithClientID
|
|
@brief Tests an unsuccessful invocation of @c getCredentialWithUIDelegte:completion: due to an
|
|
unknown error.
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateUnknownErrorWithClientID {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
|
|
.andCallBlock2(
|
|
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
|
|
XCTAssertNotNil(request);
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
|
|
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
|
|
kFakeAuthorizedDomain
|
|
]);
|
|
callback(mockGetProjectConfigResponse, nil);
|
|
});
|
|
});
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"https");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
|
|
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL =
|
|
[NSMutableString stringWithString:kFakeRedirectURLBaseErrorString];
|
|
// Add unknown error to redirect URL.
|
|
[redirectURL appendString:kUnknownErrorString];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(originalComponents.URL, nil);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(credential);
|
|
XCTAssertEqual(FIRAuthErrorCodeWebSignInUserInteractionFailure,
|
|
error.code);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateWithFirebaseAppID
|
|
@brief Tests a successful invocation of @c getCredentialWithUIDelegte:completion:
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateWithFirebaseAppID {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeEncodedFirebaseAppID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
|
|
.andCallBlock2(
|
|
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
|
|
XCTAssertNotNil(request);
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
|
|
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
|
|
kFakeAuthorizedDomain
|
|
]);
|
|
callback(mockGetProjectConfigResponse, nil);
|
|
});
|
|
});
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"https");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
|
|
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"appId"], kFakeFirebaseAppID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL = [NSMutableString
|
|
stringWithString:[kFakeEncodedFirebaseAppID
|
|
stringByAppendingString:kFakeRedirectURLResponseURL]];
|
|
// Add fake OAuthResponse to callback.
|
|
[redirectURL appendString:kFakeOAuthResponseURL];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(originalComponents.URL, nil);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider
|
|
getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(error);
|
|
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
|
|
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
|
|
XCTAssertEqualObjects(kFakeOAuthResponseURL,
|
|
OAuthCredential.OAuthResponseURLString);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateWithFirebaseAppIDWhileClientIdPresent
|
|
@brief Tests a successful invocation of @c getCredentialWithUIDelegte:completion: when the
|
|
client ID is present in the plist file, but the encoded app ID is the registered custom URL
|
|
scheme.
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateWithFirebaseAppIDWhileClientIdPresent {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeEncodedFirebaseAppID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
|
|
.andCallBlock2(
|
|
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
|
|
XCTAssertNotNil(request);
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
|
|
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
|
|
kFakeAuthorizedDomain
|
|
]);
|
|
callback(mockGetProjectConfigResponse, nil);
|
|
});
|
|
});
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"https");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
|
|
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"appId"], kFakeFirebaseAppID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL = [NSMutableString
|
|
stringWithString:[kFakeEncodedFirebaseAppID
|
|
stringByAppendingString:kFakeRedirectURLResponseURL]];
|
|
// Add fake OAuthResponse to callback.
|
|
[redirectURL appendString:kFakeOAuthResponseURL];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(originalComponents.URL, nil);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider
|
|
getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(error);
|
|
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
|
|
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
|
|
XCTAssertEqualObjects(kFakeOAuthResponseURL,
|
|
OAuthCredential.OAuthResponseURLString);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
/** @fn testGetCredentialWithUIDelegateUseEmulator
|
|
@brief Tests a successful invocation of @c getCredentialWithUIDelegte:completion: when using the
|
|
emulator.
|
|
*/
|
|
- (void)testGetCredentialWithUIDelegateUseEmulator {
|
|
id mockBundle = OCMClassMock([NSBundle class]);
|
|
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
|
|
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
|
|
@{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]}
|
|
]);
|
|
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
|
|
|
|
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
|
|
NSString *emulatorHostAndPort =
|
|
[NSString stringWithFormat:@"%@:%@", kFakeEmulatorHost, kFakeEmulatorPort];
|
|
OCMStub([_mockRequestConfiguration emulatorHostAndPort]).andReturn(emulatorHostAndPort);
|
|
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
|
|
|
|
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
|
|
|
|
// Expect view controller presentation by UIDelegate.
|
|
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
|
|
UIDelegate:mockUIDelegate
|
|
callbackMatcher:OCMOCK_ANY
|
|
completion:OCMOCK_ANY])
|
|
.andDo(^(NSInvocation *invocation) {
|
|
__unsafe_unretained id unretainedArgument;
|
|
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
|
|
// `presentURL` is at index 2.
|
|
[invocation getArgument:&unretainedArgument atIndex:2];
|
|
NSURL *presentURL = unretainedArgument;
|
|
XCTAssertEqualObjects(presentURL.scheme, @"http");
|
|
XCTAssertEqualObjects(presentURL.host, kFakeEmulatorHost);
|
|
XCTAssertEqualObjects([presentURL.port stringValue], kFakeEmulatorPort);
|
|
XCTAssertEqualObjects(presentURL.path, @"/emulator/auth/handler");
|
|
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
|
|
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
|
|
XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
|
|
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
|
|
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
|
|
XCTAssertNotNil(params[@"v"]);
|
|
// `callbackMatcher` is at index 4
|
|
[invocation getArgument:&unretainedArgument atIndex:4];
|
|
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
|
|
NSMutableString *redirectURL = [NSMutableString
|
|
stringWithString:[kFakeReverseClientID
|
|
stringByAppendingString:kFakeRedirectURLResponseURL]];
|
|
// Add fake OAuthResponse to callback.
|
|
[redirectURL appendString:kFakeOAuthResponseURL];
|
|
// Verify that the URL is rejected by the callback matcher without the event ID.
|
|
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
|
|
[redirectURL appendString:@"%26eventId%3D"];
|
|
[redirectURL appendString:params[@"eventId"]];
|
|
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
|
|
// Verify that the URL is accepted by the callback matcher with the matching event ID.
|
|
XCTAssertTrue(callbackMatcher([originalComponents URL]));
|
|
NSURLComponents *components = [originalComponents copy];
|
|
components.query = @"https";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.host = @"badhost";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.path = @"badpath";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
components = [originalComponents copy];
|
|
components.query = @"badquery";
|
|
XCTAssertFalse(callbackMatcher([components URL]));
|
|
|
|
// `completion` is at index 5
|
|
[invocation getArgument:&unretainedArgument atIndex:5];
|
|
FIRAuthURLPresentationCompletion completion = unretainedArgument;
|
|
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
|
|
completion(originalComponents.URL, nil);
|
|
});
|
|
});
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
|
|
[_provider
|
|
getCredentialWithUIDelegate:mockUIDelegate
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
XCTAssertTrue([NSThread isMainThread]);
|
|
XCTAssertNil(error);
|
|
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
|
|
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
|
|
XCTAssertEqualObjects(kFakeOAuthResponseURL,
|
|
OAuthCredential.OAuthResponseURLString);
|
|
[expectation fulfill];
|
|
}];
|
|
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
|
|
OCMVerifyAll(_mockBackend);
|
|
}
|
|
|
|
@end
|
|
|
|
#endif
|