Skip to content

Commit 944416b

Browse files
authored
Add multi-tenancy support to generic OAuth providers. (#9100)
* Add multi-tenancy support to generic OAuth providers. * Update unit tests. * Update changelog. * Link issue instead of PR in changelog.
1 parent ea6bf3a commit 944416b

File tree

3 files changed

+124
-1
lines changed

3 files changed

+124
-1
lines changed

FirebaseAuth/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
2-
- [changed] Added a `X-Firebase-GMPID` header to network requests.
2+
- [changed] Added a `X-Firebase-GMPID` header to network requests. (#9046)
3+
- [fixed] Added multi-tenancy support to generic OAuth providers. (#7990)
34

45
# 8.9.0
56
- [changed] Improved error logging. (#8704)

FirebaseAuth/Sources/AuthProvider/OAuth/FIROAuthProvider.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ - (void)getHeadFulLiteURLWithEventID:(NSString *)eventID
308308
NSString *appID = strongSelf->_auth.app.options.googleAppID;
309309
NSString *apiKey =
310310
strongSelf->_auth.requestConfiguration.APIKey;
311+
NSString *tenantID = strongSelf->_auth.tenantID;
311312
NSMutableDictionary *urlArguments = [@{
312313
@"apiKey" : apiKey,
313314
@"authType" : kAuthTypeSignInWithRedirect,
@@ -322,6 +323,9 @@ - (void)getHeadFulLiteURLWithEventID:(NSString *)eventID
322323
} else {
323324
urlArguments[@"appId"] = appID;
324325
}
326+
if (tenantID) {
327+
urlArguments[@"tid"] = tenantID;
328+
}
325329
if (strongSelf.scopes.count) {
326330
urlArguments[@"scopes"] =
327331
[strongSelf.scopes componentsJoinedByString:@","];

FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@
102102
*/
103103
static NSString *const kFakeEncodedFirebaseAppID = @"app-1-123456789-ios-123abc456def";
104104

105+
/** @var kFakeTenantID
106+
@brief A fake tenant ID.
107+
*/
108+
static NSString *const kFakeTenantID = @"tenantID";
109+
105110
/** @var kFakeOAuthResponseURL
106111
@brief A fake OAuth response URL used in test.
107112
*/
@@ -313,6 +318,111 @@ - (void)testGetCredentialWithUIDelegateWithClientID {
313318
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
314319
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
315320
XCTAssertNotNil(params[@"v"]);
321+
XCTAssertNil(params[@"tid"]);
322+
// `callbackMatcher` is at index 4
323+
[invocation getArgument:&unretainedArgument atIndex:4];
324+
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
325+
NSMutableString *redirectURL = [NSMutableString
326+
stringWithString:[kFakeReverseClientID
327+
stringByAppendingString:kFakeRedirectURLResponseURL]];
328+
// Add fake OAuthResponse to callback.
329+
[redirectURL appendString:kFakeOAuthResponseURL];
330+
// Verify that the URL is rejected by the callback matcher without the event ID.
331+
XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
332+
[redirectURL appendString:@"%26eventId%3D"];
333+
[redirectURL appendString:params[@"eventId"]];
334+
NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
335+
// Verify that the URL is accepted by the callback matcher with the matching event ID.
336+
XCTAssertTrue(callbackMatcher([originalComponents URL]));
337+
NSURLComponents *components = [originalComponents copy];
338+
components.query = @"https";
339+
XCTAssertFalse(callbackMatcher([components URL]));
340+
components = [originalComponents copy];
341+
components.host = @"badhost";
342+
XCTAssertFalse(callbackMatcher([components URL]));
343+
components = [originalComponents copy];
344+
components.path = @"badpath";
345+
XCTAssertFalse(callbackMatcher([components URL]));
346+
components = [originalComponents copy];
347+
components.query = @"badquery";
348+
XCTAssertFalse(callbackMatcher([components URL]));
349+
350+
// `completion` is at index 5
351+
[invocation getArgument:&unretainedArgument atIndex:5];
352+
FIRAuthURLPresentationCompletion completion = unretainedArgument;
353+
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
354+
completion(originalComponents.URL, nil);
355+
});
356+
});
357+
358+
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
359+
[_provider
360+
getCredentialWithUIDelegate:mockUIDelegate
361+
completion:^(FIRAuthCredential *_Nullable credential,
362+
NSError *_Nullable error) {
363+
XCTAssertTrue([NSThread isMainThread]);
364+
XCTAssertNil(error);
365+
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
366+
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
367+
XCTAssertEqualObjects(kFakeOAuthResponseURL,
368+
OAuthCredential.OAuthResponseURLString);
369+
[expectation fulfill];
370+
}];
371+
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
372+
OCMVerifyAll(_mockBackend);
373+
}
374+
375+
/** @fn testGetCredentialWithUIDelegateWithTenantID
376+
@brief Tests a successful invocation of @c getCredentialWithUIDelegte:completion:
377+
*/
378+
- (void)testGetCredentialWithUIDelegateWithTenantID {
379+
id mockBundle = OCMClassMock([NSBundle class]);
380+
OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
381+
OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]).andReturn(@[
382+
@{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]}
383+
]);
384+
OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
385+
OCMStub([_mockAuth tenantID]).andReturn(kFakeTenantID);
386+
387+
OCMStub([_mockOptions clientID]).andReturn(kFakeClientID);
388+
_provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:_mockAuth];
389+
390+
OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
391+
.andCallBlock2(
392+
^(FIRGetProjectConfigRequest *request, FIRGetProjectConfigResponseCallback callback) {
393+
XCTAssertNotNil(request);
394+
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
395+
id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
396+
OCMStub([mockGetProjectConfigResponse authorizedDomains]).andReturn(@[
397+
kFakeAuthorizedDomain
398+
]);
399+
callback(mockGetProjectConfigResponse, nil);
400+
});
401+
});
402+
403+
id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
404+
405+
// Expect view controller presentation by UIDelegate.
406+
OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
407+
UIDelegate:mockUIDelegate
408+
callbackMatcher:OCMOCK_ANY
409+
completion:OCMOCK_ANY])
410+
.andDo(^(NSInvocation *invocation) {
411+
__unsafe_unretained id unretainedArgument;
412+
// Indices 0 and 1 indicate the hidden arguments self and _cmd.
413+
// `presentURL` is at index 2.
414+
[invocation getArgument:&unretainedArgument atIndex:2];
415+
NSURL *presentURL = unretainedArgument;
416+
XCTAssertEqualObjects(presentURL.scheme, @"https");
417+
XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
418+
XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
419+
NSDictionary *params = [FIRAuthWebUtils dictionaryWithHttpArgumentsString:presentURL.query];
420+
XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
421+
XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
422+
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
423+
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
424+
XCTAssertEqualObjects(params[@"tid"], kFakeTenantID);
425+
XCTAssertNotNil(params[@"v"]);
316426
// `callbackMatcher` is at index 4
317427
[invocation getArgument:&unretainedArgument atIndex:4];
318428
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
@@ -416,6 +526,7 @@ - (void)testGetCredentialWithUIDelegateUserCancellationWithClientID {
416526
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
417527
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
418528
XCTAssertNotNil(params[@"v"]);
529+
XCTAssertNil(params[@"tid"]);
419530
// `callbackMatcher` is at index 4
420531
[invocation getArgument:&unretainedArgument atIndex:4];
421532
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
@@ -516,6 +627,7 @@ - (void)testGetCredentialWithUIDelegateNetworkRequestFailedWithClientID {
516627
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
517628
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
518629
XCTAssertNotNil(params[@"v"]);
630+
XCTAssertNil(params[@"tid"]);
519631
// `callbackMatcher` is at index 4
520632
[invocation getArgument:&unretainedArgument atIndex:4];
521633
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
@@ -614,6 +726,7 @@ - (void)testGetCredentialWithUIDelegateInternalErrorWithClientID {
614726
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
615727
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
616728
XCTAssertNotNil(params[@"v"]);
729+
XCTAssertNil(params[@"tid"]);
617730
// `callbackMatcher` is at index 4
618731
[invocation getArgument:&unretainedArgument atIndex:4];
619732
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
@@ -713,6 +826,7 @@ - (void)testGetCredentialWithUIDelegateInvalidClientID {
713826
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
714827
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
715828
XCTAssertNotNil(params[@"v"]);
829+
XCTAssertNil(params[@"tid"]);
716830
// `callbackMatcher` is at index 4
717831
[invocation getArgument:&unretainedArgument atIndex:4];
718832
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
@@ -812,6 +926,7 @@ - (void)testGetCredentialWithUIDelegateUnknownErrorWithClientID {
812926
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
813927
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
814928
XCTAssertNotNil(params[@"v"]);
929+
XCTAssertNil(params[@"tid"]);
815930
// `callbackMatcher` is at index 4
816931
[invocation getArgument:&unretainedArgument atIndex:4];
817932
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
@@ -910,6 +1025,7 @@ - (void)testGetCredentialWithUIDelegateWithFirebaseAppID {
9101025
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
9111026
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
9121027
XCTAssertNotNil(params[@"v"]);
1028+
XCTAssertNil(params[@"tid"]);
9131029
// `callbackMatcher` is at index 4
9141030
[invocation getArgument:&unretainedArgument atIndex:4];
9151031
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
@@ -1014,6 +1130,7 @@ - (void)testGetCredentialWithUIDelegateWithFirebaseAppIDWhileClientIdPresent {
10141130
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
10151131
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
10161132
XCTAssertNotNil(params[@"v"]);
1133+
XCTAssertNil(params[@"tid"]);
10171134
// `callbackMatcher` is at index 4
10181135
[invocation getArgument:&unretainedArgument atIndex:4];
10191136
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
@@ -1108,6 +1225,7 @@ - (void)testGetCredentialWithUIDelegateUseEmulator {
11081225
XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
11091226
XCTAssertEqualObjects(params[@"authType"], @"signInWithRedirect");
11101227
XCTAssertNotNil(params[@"v"]);
1228+
XCTAssertNil(params[@"tid"]);
11111229
// `callbackMatcher` is at index 4
11121230
[invocation getArgument:&unretainedArgument atIndex:4];
11131231
FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;

0 commit comments

Comments
 (0)