Skip to content

Commit 2e77efd

Browse files
Generic IDP support (firebase#2405)
1 parent f76ae21 commit 2e77efd

File tree

79 files changed

+2338
-232
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+2338
-232
lines changed

Example/Auth/Sample/MainViewController.m

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@
114114
*/
115115
static NSString *const kSignInGoogleButtonText = @"Sign in with Google";
116116

117+
/** @var kSignInGoogleProviderButtonText
118+
@brief The text of the Google provider version of "Sign In With Provider" button.
119+
*/
120+
static NSString *const kSignInGoogleProviderButtonText = @"Sign in with provider:(Google)";
121+
117122
/** @var kSignInWithEmailLink
118123
@brief The text of the "Sign in with Email Link" button.
119124
*/
@@ -135,6 +140,16 @@
135140
static NSString *const kSignInGoogleAndRetrieveDataButtonText =
136141
@"Sign in with Google and retrieve data";
137142

143+
/** @var kSignInGoogleHeadfulLite
144+
@brief The text of the "Sign in with Google (headful lite)" button.
145+
*/
146+
static NSString *const kSignInGoogleHeadfulLite = @"Sign in with Google (headful lite)";
147+
148+
/** @var kSignInMicrosoftHeadfulLite
149+
@brief The text of the "Sign in with Microsoft (headful lite)" button.
150+
*/
151+
static NSString *const kSignInMicrosoftHeadfulLite = @"Sign in with Microsoft (headful lite)";
152+
138153
/** @var kSignInFacebookButtonText
139154
@brief The text of the "Facebook SignIn" button.
140155
*/
@@ -819,6 +834,8 @@ - (void)updateTable {
819834
action:^{ [weakSelf createUserAuthDataResult]; }],
820835
[StaticContentTableViewCell cellWithTitle:kSignInGoogleButtonText
821836
action:^{ [weakSelf signInGoogle]; }],
837+
[StaticContentTableViewCell cellWithTitle:kSignInGoogleProviderButtonText
838+
action:^{ [weakSelf signInGoogleProvider]; }],
822839
[StaticContentTableViewCell cellWithTitle:kSignInWithEmailLink
823840
action:^{ [weakSelf signInWithEmailLink]; }],
824841
[StaticContentTableViewCell cellWithTitle:kVerifyEmailLinkAccount
@@ -827,6 +844,10 @@ - (void)updateTable {
827844
action:^{ [weakSelf sendEmailSignInLink]; }],
828845
[StaticContentTableViewCell cellWithTitle:kSignInGoogleAndRetrieveDataButtonText
829846
action:^{ [weakSelf signInGoogleAndRetrieveData]; }],
847+
[StaticContentTableViewCell cellWithTitle:kSignInGoogleHeadfulLite
848+
action:^{ [weakSelf signInGoogleHeadfulLite]; }],
849+
[StaticContentTableViewCell cellWithTitle:kSignInMicrosoftHeadfulLite
850+
action:^{ [weakSelf signInMicrosoftHeadfulLite]; }],
830851
[StaticContentTableViewCell cellWithTitle:kSignInFacebookButtonText
831852
action:^{ [weakSelf signInFacebook]; }],
832853
[StaticContentTableViewCell cellWithTitle:kSignInFacebookAndRetrieveDataButtonText
@@ -1760,6 +1781,106 @@ - (void)signInGoogleAndRetrieveData {
17601781
[self signinWithProvider:[AuthProviders google] retrieveData:YES];
17611782
}
17621783

1784+
- (void)signInGoogleProvider {
1785+
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRGoogleAuthProviderID];
1786+
provider.customParameters = @{
1787+
@"prompt" : @"consent",
1788+
};
1789+
provider.scopes = @[ @"profile", @"email", @"https://www.googleapis.com/auth/plus.me" ];
1790+
[self showSpinner:^{
1791+
[[AppManager auth] signInWithProvider:provider
1792+
UIDelegate:nil
1793+
completion:^(FIRAuthDataResult *_Nullable authResult,
1794+
NSError *_Nullable error) {
1795+
[self hideSpinner:^{
1796+
if (error) {
1797+
[self logFailure:@"sign-in with provider (Google) failed" error:error];
1798+
} else if (authResult.additionalUserInfo) {
1799+
[self logSuccess:[self stringWithAdditionalUserInfo:authResult.additionalUserInfo]];
1800+
if (_isNewUserToggleOn) {
1801+
NSString *newUserString = authResult.additionalUserInfo.newUser ?
1802+
@"New user" : @"Existing user";
1803+
[self showMessagePromptWithTitle:@"New or Existing"
1804+
message:newUserString
1805+
showCancelButton:NO
1806+
completion:nil];
1807+
}
1808+
}
1809+
[self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
1810+
}];
1811+
}];
1812+
}];
1813+
}
1814+
1815+
/** @fn signInGoogleHeadfulLite
1816+
@brief Invoked when "Sign in with Google (headful-lite)" row is pressed.
1817+
*/
1818+
- (void)signInGoogleHeadfulLite {
1819+
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRGoogleAuthProviderID];
1820+
provider.customParameters = @{
1821+
@"prompt" : @"consent",
1822+
};
1823+
provider.scopes = @[ @"profile", @"email", @"https://www.googleapis.com/auth/plus.me" ];
1824+
[self showSpinner:^{
1825+
[provider getCredentialWithUIDelegate:nil completion:^(FIRAuthCredential *_Nullable credential,
1826+
NSError *_Nullable error) {
1827+
if (error) {
1828+
[self logFailure:@"sign-in with Google failed" error:error];
1829+
return;
1830+
}
1831+
[[AppManager auth] signInAndRetrieveDataWithCredential:credential
1832+
completion:^(FIRAuthDataResult *_Nullable
1833+
authResult,
1834+
NSError *_Nullable error) {
1835+
[self hideSpinner:^{
1836+
if (error) {
1837+
[self logFailure:@"sign-in with Google failed" error:error];
1838+
return;
1839+
} else {
1840+
[self logSuccess:@"sign-in with Google (headful-lite) succeeded."];
1841+
}
1842+
[self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
1843+
}];
1844+
}];
1845+
}];
1846+
}];
1847+
}
1848+
1849+
/** @fn signInMicrosoftHeadfulLite
1850+
@brief Invoked when "Sign in with Microsoft (headful-lite)" row is pressed.
1851+
*/
1852+
- (void)signInMicrosoftHeadfulLite {
1853+
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRMicrosoftAuthProviderID];
1854+
provider.customParameters = @{
1855+
@"prompt" : @"consent",
1856+
@"login_hint" : @"[email protected]",
1857+
};
1858+
provider.scopes = @[ @"user.readwrite,calendars.read" ];
1859+
[self showSpinner:^{
1860+
[provider getCredentialWithUIDelegate:nil completion:^(FIRAuthCredential *_Nullable credential,
1861+
NSError *_Nullable error) {
1862+
if (error) {
1863+
[self logFailure:@"sign-in with Microsoft failed" error:error];
1864+
return;
1865+
}
1866+
[[AppManager auth] signInAndRetrieveDataWithCredential:credential
1867+
completion:^(FIRAuthDataResult *_Nullable
1868+
authResult,
1869+
NSError *_Nullable error) {
1870+
[self hideSpinner:^{
1871+
if (error) {
1872+
[self logFailure:@"sign-in with Microsoft failed" error:error];
1873+
return;
1874+
} else {
1875+
[self logSuccess:@"sign-in with Microsoft (headful-lite) succeeded."];
1876+
}
1877+
[self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
1878+
}];
1879+
}];
1880+
}];
1881+
}];
1882+
}
1883+
17631884
/** @fn signInFacebook
17641885
@brief Invoked when "Sign in with Facebook" row is pressed.
17651886
*/
@@ -3262,7 +3383,7 @@ - (void)linkPhoneNumber:(NSString *_Nullable)phoneNumber
32623383
// provided credential.
32633384
[self showSpinner:^{
32643385
FIRPhoneAuthCredential *credential =
3265-
error.userInfo[FIRAuthUpdatedCredentialKey];
3386+
error.userInfo[FIRAuthErrorUserInfoUpdatedCredentialKey];
32663387
[[AppManager auth] signInWithCredential:credential
32673388
completion:^(FIRUser *_Nullable user,
32683389
NSError *_Nullable error) {
@@ -3356,7 +3477,7 @@ - (void)signInWithGitHub {
33563477
if (!userPressedOK || !accessToken.length) {
33573478
return;
33583479
}
3359-
FIRAuthCredential *credential =
3480+
FIROAuthCredential *credential =
33603481
[FIROAuthProvider credentialWithProviderID:FIRGitHubAuthProviderID accessToken:accessToken];
33613482
if (credential) {
33623483
[[AppManager auth] signInWithCredential:credential

Example/Auth/Tests/FIRAuthTests.m

Lines changed: 166 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020

2121
#import <FirebaseAuthInterop/FIRAuthInterop.h>
2222
#import <FirebaseCore/FIRAppInternal.h>
23+
24+
#import <FirebaseAuth/FIREmailAuthProvider.h>
25+
#import <FirebaseAuth/FIRGoogleAuthProvider.h>
26+
#import <FirebaseAuth/FIRAdditionalUserInfo.h>
27+
2328
#import <FirebaseCore/FIRComponent.h>
2429
#import <FirebaseCore/FIRLibrary.h>
2530

@@ -41,7 +46,7 @@
4146
#import "FIRGetAccountInfoResponse.h"
4247
#import "FIRGetOOBConfirmationCodeRequest.h"
4348
#import "FIRGetOOBConfirmationCodeResponse.h"
44-
#import "FIRGoogleAuthProvider.h"
49+
#import "FIROAuthProvider.h"
4550
#import "FIRSecureTokenRequest.h"
4651
#import "FIRSecureTokenResponse.h"
4752
#import "FIRResetPasswordRequest.h"
@@ -59,11 +64,13 @@
5964
#import "FIRVerifyPhoneNumberRequest.h"
6065
#import "FIRVerifyPhoneNumberResponse.h"
6166
#import "FIRApp+FIRAuthUnitTests.h"
67+
#import "OAuth/FIROAuthCredential_Internal.h"
6268
#import "OCMStubRecorder+FIRAuthUnitTests.h"
6369
#import <OCMock/OCMock.h>
6470
#import "FIRActionCodeSettings.h"
6571

6672
#if TARGET_OS_IOS
73+
#import "FIRAuthUIDelegate.h"
6774
#import "FIRPhoneAuthCredential.h"
6875
#import "FIRPhoneAuthProvider.h"
6976
#endif
@@ -163,6 +170,21 @@
163170
*/
164171
static NSString *const kVerificationID = @"55432";
165172

173+
/** @var kOAuthRequestURI
174+
@brief Fake OAuthRequest URI for testing.
175+
*/
176+
static NSString *const kOAuthRequestURI = @"requestURI";
177+
178+
/** @var kOAuthSessionID
179+
@brief Fake session ID for testing.
180+
*/
181+
static NSString *const kOAuthSessionID = @"sessionID";
182+
183+
/** @var kFakeWebSignInUserInteractionFailureReason
184+
@brief Fake reason for FIRAuthErrorCodeWebSignInUserInteractionFailure error while testing.
185+
*/
186+
static NSString *const kFakeWebSignInUserInteractionFailureReason = @"fake_reason";
187+
166188
/** @var kContinueURL
167189
@brief Fake string value of continue url.
168190
*/
@@ -1116,6 +1138,91 @@ - (void)testSignInWithEmailCredentialEmptyPassword {
11161138
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
11171139
}
11181140

1141+
#if TARGET_OS_IOS
1142+
/** @fn testSignInWithProviderSuccess
1143+
@brief Tests a successful @c signInWithProvider:UIDelegate:completion: call with an OAuth
1144+
provider configured for Google.
1145+
*/
1146+
- (void)testSignInWithProviderSuccess {
1147+
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
1148+
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
1149+
FIRVerifyAssertionResponseCallback callback) {
1150+
XCTAssertEqualObjects(request.APIKey, kAPIKey);
1151+
XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
1152+
XCTAssertTrue(request.returnSecureToken);
1153+
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
1154+
id mockVerifyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
1155+
OCMStub([mockVerifyAssertionResponse federatedID]).andReturn(kGoogleID);
1156+
OCMStub([mockVerifyAssertionResponse providerID]).andReturn(FIRGoogleAuthProviderID);
1157+
OCMStub([mockVerifyAssertionResponse localID]).andReturn(kLocalID);
1158+
OCMStub([mockVerifyAssertionResponse displayName]).andReturn(kGoogleDisplayName);
1159+
[self stubTokensWithMockResponse:mockVerifyAssertionResponse];
1160+
callback(mockVerifyAssertionResponse, nil);
1161+
});
1162+
});
1163+
[self expectGetAccountInfoGoogle];
1164+
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
1165+
[[FIRAuth auth] signOut:NULL];
1166+
id mockProvider = OCMClassMock([FIROAuthProvider class]);
1167+
OCMExpect([mockProvider getCredentialWithUIDelegate:[OCMArg any] completion:[OCMArg any]])
1168+
.andCallBlock2(^(id<FIRAuthUIDelegate> delegate, FIRAuthCredentialCallback callback) {
1169+
dispatch_async(FIRAuthGlobalWorkQueue(), ^(){
1170+
FIROAuthCredential *credential =
1171+
[[FIROAuthCredential alloc] initWithProviderID:FIRGoogleAuthProviderID
1172+
sessionID:kOAuthSessionID
1173+
OAuthResponseURLString:kOAuthRequestURI];
1174+
callback(credential, nil);
1175+
});
1176+
});
1177+
[[FIRAuth auth] signInWithProvider:mockProvider
1178+
UIDelegate:nil
1179+
completion:^(FIRAuthDataResult *_Nullable authResult,
1180+
NSError *_Nullable error) {
1181+
XCTAssertTrue([NSThread isMainThread]);
1182+
[self assertUserGoogle:authResult.user];
1183+
XCTAssertNil(error);
1184+
[expectation fulfill];
1185+
}];
1186+
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
1187+
OCMVerifyAll(_mockBackend);
1188+
}
1189+
1190+
/** @fn testSignInWithProviderFailure
1191+
@brief Tests a failed @c signInWithProvider:UIDelegate:completion: call with the error code
1192+
FIRAuthErrorCodeWebSignInUserInteractionFailure.
1193+
*/
1194+
- (void)testSignInWithProviderFailure {
1195+
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
1196+
.andDispatchError2([FIRAuthErrorUtils webSignInUserInteractionFailureWithReason:
1197+
kFakeWebSignInUserInteractionFailureReason]);
1198+
[[FIRAuth auth] signOut:NULL];
1199+
id mockProvider = OCMClassMock([FIROAuthProvider class]);
1200+
OCMExpect([mockProvider getCredentialWithUIDelegate:[OCMArg any] completion:[OCMArg any]])
1201+
.andCallBlock2(^(id<FIRAuthUIDelegate> delegate, FIRAuthCredentialCallback callback) {
1202+
dispatch_async(FIRAuthGlobalWorkQueue(), ^(){
1203+
FIROAuthCredential *credential =
1204+
[[FIROAuthCredential alloc] initWithProviderID:FIRGoogleAuthProviderID
1205+
sessionID:kOAuthSessionID
1206+
OAuthResponseURLString:kOAuthRequestURI];
1207+
callback(credential, nil);
1208+
});
1209+
});
1210+
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
1211+
[[FIRAuth auth] signInWithProvider:mockProvider
1212+
UIDelegate:nil
1213+
completion:^(FIRAuthDataResult *_Nullable authResult,
1214+
NSError *_Nullable error) {
1215+
XCTAssertTrue([NSThread isMainThread]);
1216+
XCTAssertNil(authResult);
1217+
XCTAssertEqual(error.code, FIRAuthErrorCodeWebSignInUserInteractionFailure);
1218+
XCTAssertEqualObjects(error.userInfo[NSLocalizedFailureReasonErrorKey],
1219+
kFakeWebSignInUserInteractionFailureReason);
1220+
[expectation fulfill];
1221+
}];
1222+
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
1223+
OCMVerifyAll(_mockBackend);
1224+
}
1225+
11191226
/** @fn testSignInWithGoogleAccountExistsError
11201227
@brief Tests the flow of a failed @c signInWithCredential:completion: with a Google credential
11211228
where the backend returns a needs @needConfirmation equal to true. An
@@ -1195,6 +1302,64 @@ - (void)testSignInWithGoogleCredentialSuccess {
11951302
OCMVerifyAll(_mockBackend);
11961303
}
11971304

1305+
/** @fn testSignInWithOAuthCredentialSuccess
1306+
@brief Tests the flow of a successful @c signInWithCredential:completion: call with a generic
1307+
OAuth credential (In this case, configured for the Google IDP).
1308+
*/
1309+
- (void)testSignInWithOAuthCredentialSuccess {
1310+
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
1311+
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
1312+
FIRVerifyAssertionResponseCallback callback) {
1313+
XCTAssertEqualObjects(request.APIKey, kAPIKey);
1314+
XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
1315+
XCTAssertEqualObjects(request.requestURI, kOAuthRequestURI);
1316+
XCTAssertEqualObjects(request.sessionID, kOAuthSessionID);
1317+
XCTAssertTrue(request.returnSecureToken);
1318+
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
1319+
id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
1320+
OCMStub([mockVeriyAssertionResponse federatedID]).andReturn(kGoogleID);
1321+
OCMStub([mockVeriyAssertionResponse providerID]).andReturn(FIRGoogleAuthProviderID);
1322+
OCMStub([mockVeriyAssertionResponse localID]).andReturn(kLocalID);
1323+
OCMStub([mockVeriyAssertionResponse displayName]).andReturn(kGoogleDisplayName);
1324+
[self stubTokensWithMockResponse:mockVeriyAssertionResponse];
1325+
callback(mockVeriyAssertionResponse, nil);
1326+
});
1327+
});
1328+
[self expectGetAccountInfoGoogle];
1329+
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
1330+
[[FIRAuth auth] signOut:NULL];
1331+
id mockProvider = OCMClassMock([FIROAuthProvider class]);
1332+
OCMExpect([mockProvider getCredentialWithUIDelegate:[OCMArg any] completion:[OCMArg any]])
1333+
.andCallBlock2(^(id<FIRAuthUIDelegate> delegate, FIRAuthCredentialCallback callback) {
1334+
dispatch_async(FIRAuthGlobalWorkQueue(), ^(){
1335+
FIROAuthCredential *credential =
1336+
[[FIROAuthCredential alloc] initWithProviderID:FIRGoogleAuthProviderID
1337+
sessionID:kOAuthSessionID
1338+
OAuthResponseURLString:kOAuthRequestURI];
1339+
callback(credential, nil);
1340+
});
1341+
});
1342+
[mockProvider getCredentialWithUIDelegate:nil
1343+
completion:^(FIRAuthCredential *_Nullable credential,
1344+
NSError *_Nullable error) {
1345+
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
1346+
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
1347+
XCTAssertEqualObjects(OAuthCredential.OAuthResponseURLString, kOAuthRequestURI);
1348+
XCTAssertEqualObjects(OAuthCredential.sessionID, kOAuthSessionID);
1349+
[[FIRAuth auth] signInWithCredential:OAuthCredential completion:^(FIRUser *_Nullable user,
1350+
NSError *_Nullable error) {
1351+
XCTAssertTrue([NSThread isMainThread]);
1352+
[self assertUserGoogle:user];
1353+
XCTAssertNil(error);
1354+
[expectation fulfill];
1355+
}];
1356+
}];
1357+
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
1358+
[self assertUserGoogle:[FIRAuth auth].currentUser];
1359+
OCMVerifyAll(_mockBackend);
1360+
}
1361+
#endif // TARGET_OS_IOS
1362+
11981363
/** @fn testSignInAndRetrieveDataWithCredentialSuccess
11991364
@brief Tests the flow of a successful @c signInAndRetrieveDataWithCredential:completion: call
12001365
with an Google Sign-In credential.

0 commit comments

Comments
 (0)