Skip to content

Commit

Permalink
Generic IDP support (#2405)
Browse files Browse the repository at this point in the history
  • Loading branch information
Yue-Wang-Google committed Feb 23, 2019
1 parent f76ae21 commit 2e77efd
Show file tree
Hide file tree
Showing 79 changed files with 2,338 additions and 232 deletions.
125 changes: 123 additions & 2 deletions Example/Auth/Sample/MainViewController.m
Expand Up @@ -114,6 +114,11 @@
*/
static NSString *const kSignInGoogleButtonText = @"Sign in with Google";

/** @var kSignInGoogleProviderButtonText
@brief The text of the Google provider version of "Sign In With Provider" button.
*/
static NSString *const kSignInGoogleProviderButtonText = @"Sign in with provider:(Google)";

/** @var kSignInWithEmailLink
@brief The text of the "Sign in with Email Link" button.
*/
Expand All @@ -135,6 +140,16 @@
static NSString *const kSignInGoogleAndRetrieveDataButtonText =
@"Sign in with Google and retrieve data";

/** @var kSignInGoogleHeadfulLite
@brief The text of the "Sign in with Google (headful lite)" button.
*/
static NSString *const kSignInGoogleHeadfulLite = @"Sign in with Google (headful lite)";

/** @var kSignInMicrosoftHeadfulLite
@brief The text of the "Sign in with Microsoft (headful lite)" button.
*/
static NSString *const kSignInMicrosoftHeadfulLite = @"Sign in with Microsoft (headful lite)";

/** @var kSignInFacebookButtonText
@brief The text of the "Facebook SignIn" button.
*/
Expand Down Expand Up @@ -819,6 +834,8 @@ - (void)updateTable {
action:^{ [weakSelf createUserAuthDataResult]; }],
[StaticContentTableViewCell cellWithTitle:kSignInGoogleButtonText
action:^{ [weakSelf signInGoogle]; }],
[StaticContentTableViewCell cellWithTitle:kSignInGoogleProviderButtonText
action:^{ [weakSelf signInGoogleProvider]; }],
[StaticContentTableViewCell cellWithTitle:kSignInWithEmailLink
action:^{ [weakSelf signInWithEmailLink]; }],
[StaticContentTableViewCell cellWithTitle:kVerifyEmailLinkAccount
Expand All @@ -827,6 +844,10 @@ - (void)updateTable {
action:^{ [weakSelf sendEmailSignInLink]; }],
[StaticContentTableViewCell cellWithTitle:kSignInGoogleAndRetrieveDataButtonText
action:^{ [weakSelf signInGoogleAndRetrieveData]; }],
[StaticContentTableViewCell cellWithTitle:kSignInGoogleHeadfulLite
action:^{ [weakSelf signInGoogleHeadfulLite]; }],
[StaticContentTableViewCell cellWithTitle:kSignInMicrosoftHeadfulLite
action:^{ [weakSelf signInMicrosoftHeadfulLite]; }],
[StaticContentTableViewCell cellWithTitle:kSignInFacebookButtonText
action:^{ [weakSelf signInFacebook]; }],
[StaticContentTableViewCell cellWithTitle:kSignInFacebookAndRetrieveDataButtonText
Expand Down Expand Up @@ -1760,6 +1781,106 @@ - (void)signInGoogleAndRetrieveData {
[self signinWithProvider:[AuthProviders google] retrieveData:YES];
}

- (void)signInGoogleProvider {
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRGoogleAuthProviderID];
provider.customParameters = @{
@"prompt" : @"consent",
};
provider.scopes = @[ @"profile", @"email", @"https://www.googleapis.com/auth/plus.me" ];
[self showSpinner:^{
[[AppManager auth] signInWithProvider:provider
UIDelegate:nil
completion:^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
[self hideSpinner:^{
if (error) {
[self logFailure:@"sign-in with provider (Google) failed" error:error];
} else if (authResult.additionalUserInfo) {
[self logSuccess:[self stringWithAdditionalUserInfo:authResult.additionalUserInfo]];
if (_isNewUserToggleOn) {
NSString *newUserString = authResult.additionalUserInfo.newUser ?
@"New user" : @"Existing user";
[self showMessagePromptWithTitle:@"New or Existing"
message:newUserString
showCancelButton:NO
completion:nil];
}
}
[self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
}];
}];
}];
}

/** @fn signInGoogleHeadfulLite
@brief Invoked when "Sign in with Google (headful-lite)" row is pressed.
*/
- (void)signInGoogleHeadfulLite {
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRGoogleAuthProviderID];
provider.customParameters = @{
@"prompt" : @"consent",
};
provider.scopes = @[ @"profile", @"email", @"https://www.googleapis.com/auth/plus.me" ];
[self showSpinner:^{
[provider getCredentialWithUIDelegate:nil completion:^(FIRAuthCredential *_Nullable credential,
NSError *_Nullable error) {
if (error) {
[self logFailure:@"sign-in with Google failed" error:error];
return;
}
[[AppManager auth] signInAndRetrieveDataWithCredential:credential
completion:^(FIRAuthDataResult *_Nullable
authResult,
NSError *_Nullable error) {
[self hideSpinner:^{
if (error) {
[self logFailure:@"sign-in with Google failed" error:error];
return;
} else {
[self logSuccess:@"sign-in with Google (headful-lite) succeeded."];
}
[self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
}];
}];
}];
}];
}

/** @fn signInMicrosoftHeadfulLite
@brief Invoked when "Sign in with Microsoft (headful-lite)" row is pressed.
*/
- (void)signInMicrosoftHeadfulLite {
FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:FIRMicrosoftAuthProviderID];
provider.customParameters = @{
@"prompt" : @"consent",
@"login_hint" : @"tu8731@gmail.com",
};
provider.scopes = @[ @"user.readwrite,calendars.read" ];
[self showSpinner:^{
[provider getCredentialWithUIDelegate:nil completion:^(FIRAuthCredential *_Nullable credential,
NSError *_Nullable error) {
if (error) {
[self logFailure:@"sign-in with Microsoft failed" error:error];
return;
}
[[AppManager auth] signInAndRetrieveDataWithCredential:credential
completion:^(FIRAuthDataResult *_Nullable
authResult,
NSError *_Nullable error) {
[self hideSpinner:^{
if (error) {
[self logFailure:@"sign-in with Microsoft failed" error:error];
return;
} else {
[self logSuccess:@"sign-in with Microsoft (headful-lite) succeeded."];
}
[self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
}];
}];
}];
}];
}

/** @fn signInFacebook
@brief Invoked when "Sign in with Facebook" row is pressed.
*/
Expand Down Expand Up @@ -3262,7 +3383,7 @@ - (void)linkPhoneNumber:(NSString *_Nullable)phoneNumber
// provided credential.
[self showSpinner:^{
FIRPhoneAuthCredential *credential =
error.userInfo[FIRAuthUpdatedCredentialKey];
error.userInfo[FIRAuthErrorUserInfoUpdatedCredentialKey];
[[AppManager auth] signInWithCredential:credential
completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
Expand Down Expand Up @@ -3356,7 +3477,7 @@ - (void)signInWithGitHub {
if (!userPressedOK || !accessToken.length) {
return;
}
FIRAuthCredential *credential =
FIROAuthCredential *credential =
[FIROAuthProvider credentialWithProviderID:FIRGitHubAuthProviderID accessToken:accessToken];
if (credential) {
[[AppManager auth] signInWithCredential:credential
Expand Down
167 changes: 166 additions & 1 deletion Example/Auth/Tests/FIRAuthTests.m
Expand Up @@ -20,6 +20,11 @@

#import <FirebaseAuthInterop/FIRAuthInterop.h>
#import <FirebaseCore/FIRAppInternal.h>

#import <FirebaseAuth/FIREmailAuthProvider.h>
#import <FirebaseAuth/FIRGoogleAuthProvider.h>
#import <FirebaseAuth/FIRAdditionalUserInfo.h>

#import <FirebaseCore/FIRComponent.h>
#import <FirebaseCore/FIRLibrary.h>

Expand All @@ -41,7 +46,7 @@
#import "FIRGetAccountInfoResponse.h"
#import "FIRGetOOBConfirmationCodeRequest.h"
#import "FIRGetOOBConfirmationCodeResponse.h"
#import "FIRGoogleAuthProvider.h"
#import "FIROAuthProvider.h"
#import "FIRSecureTokenRequest.h"
#import "FIRSecureTokenResponse.h"
#import "FIRResetPasswordRequest.h"
Expand All @@ -59,11 +64,13 @@
#import "FIRVerifyPhoneNumberRequest.h"
#import "FIRVerifyPhoneNumberResponse.h"
#import "FIRApp+FIRAuthUnitTests.h"
#import "OAuth/FIROAuthCredential_Internal.h"
#import "OCMStubRecorder+FIRAuthUnitTests.h"
#import <OCMock/OCMock.h>
#import "FIRActionCodeSettings.h"

#if TARGET_OS_IOS
#import "FIRAuthUIDelegate.h"
#import "FIRPhoneAuthCredential.h"
#import "FIRPhoneAuthProvider.h"
#endif
Expand Down Expand Up @@ -163,6 +170,21 @@
*/
static NSString *const kVerificationID = @"55432";

/** @var kOAuthRequestURI
@brief Fake OAuthRequest URI for testing.
*/
static NSString *const kOAuthRequestURI = @"requestURI";

/** @var kOAuthSessionID
@brief Fake session ID for testing.
*/
static NSString *const kOAuthSessionID = @"sessionID";

/** @var kFakeWebSignInUserInteractionFailureReason
@brief Fake reason for FIRAuthErrorCodeWebSignInUserInteractionFailure error while testing.
*/
static NSString *const kFakeWebSignInUserInteractionFailureReason = @"fake_reason";

/** @var kContinueURL
@brief Fake string value of continue url.
*/
Expand Down Expand Up @@ -1116,6 +1138,91 @@ - (void)testSignInWithEmailCredentialEmptyPassword {
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}

#if TARGET_OS_IOS
/** @fn testSignInWithProviderSuccess
@brief Tests a successful @c signInWithProvider:UIDelegate:completion: call with an OAuth
provider configured for Google.
*/
- (void)testSignInWithProviderSuccess {
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVerifyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
OCMStub([mockVerifyAssertionResponse federatedID]).andReturn(kGoogleID);
OCMStub([mockVerifyAssertionResponse providerID]).andReturn(FIRGoogleAuthProviderID);
OCMStub([mockVerifyAssertionResponse localID]).andReturn(kLocalID);
OCMStub([mockVerifyAssertionResponse displayName]).andReturn(kGoogleDisplayName);
[self stubTokensWithMockResponse:mockVerifyAssertionResponse];
callback(mockVerifyAssertionResponse, nil);
});
});
[self expectGetAccountInfoGoogle];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
id mockProvider = OCMClassMock([FIROAuthProvider class]);
OCMExpect([mockProvider getCredentialWithUIDelegate:[OCMArg any] completion:[OCMArg any]])
.andCallBlock2(^(id<FIRAuthUIDelegate> delegate, FIRAuthCredentialCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^(){
FIROAuthCredential *credential =
[[FIROAuthCredential alloc] initWithProviderID:FIRGoogleAuthProviderID
sessionID:kOAuthSessionID
OAuthResponseURLString:kOAuthRequestURI];
callback(credential, nil);
});
});
[[FIRAuth auth] signInWithProvider:mockProvider
UIDelegate:nil
completion:^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUserGoogle:authResult.user];
XCTAssertNil(error);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}

/** @fn testSignInWithProviderFailure
@brief Tests a failed @c signInWithProvider:UIDelegate:completion: call with the error code
FIRAuthErrorCodeWebSignInUserInteractionFailure.
*/
- (void)testSignInWithProviderFailure {
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andDispatchError2([FIRAuthErrorUtils webSignInUserInteractionFailureWithReason:
kFakeWebSignInUserInteractionFailureReason]);
[[FIRAuth auth] signOut:NULL];
id mockProvider = OCMClassMock([FIROAuthProvider class]);
OCMExpect([mockProvider getCredentialWithUIDelegate:[OCMArg any] completion:[OCMArg any]])
.andCallBlock2(^(id<FIRAuthUIDelegate> delegate, FIRAuthCredentialCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^(){
FIROAuthCredential *credential =
[[FIROAuthCredential alloc] initWithProviderID:FIRGoogleAuthProviderID
sessionID:kOAuthSessionID
OAuthResponseURLString:kOAuthRequestURI];
callback(credential, nil);
});
});
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signInWithProvider:mockProvider
UIDelegate:nil
completion:^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
XCTAssertNil(authResult);
XCTAssertEqual(error.code, FIRAuthErrorCodeWebSignInUserInteractionFailure);
XCTAssertEqualObjects(error.userInfo[NSLocalizedFailureReasonErrorKey],
kFakeWebSignInUserInteractionFailureReason);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
OCMVerifyAll(_mockBackend);
}

/** @fn testSignInWithGoogleAccountExistsError
@brief Tests the flow of a failed @c signInWithCredential:completion: with a Google credential
where the backend returns a needs @needConfirmation equal to true. An
Expand Down Expand Up @@ -1195,6 +1302,64 @@ - (void)testSignInWithGoogleCredentialSuccess {
OCMVerifyAll(_mockBackend);
}

/** @fn testSignInWithOAuthCredentialSuccess
@brief Tests the flow of a successful @c signInWithCredential:completion: call with a generic
OAuth credential (In this case, configured for the Google IDP).
*/
- (void)testSignInWithOAuthCredentialSuccess {
OCMExpect([_mockBackend verifyAssertion:[OCMArg any] callback:[OCMArg any]])
.andCallBlock2(^(FIRVerifyAssertionRequest *_Nullable request,
FIRVerifyAssertionResponseCallback callback) {
XCTAssertEqualObjects(request.APIKey, kAPIKey);
XCTAssertEqualObjects(request.providerID, FIRGoogleAuthProviderID);
XCTAssertEqualObjects(request.requestURI, kOAuthRequestURI);
XCTAssertEqualObjects(request.sessionID, kOAuthSessionID);
XCTAssertTrue(request.returnSecureToken);
dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
id mockVeriyAssertionResponse = OCMClassMock([FIRVerifyAssertionResponse class]);
OCMStub([mockVeriyAssertionResponse federatedID]).andReturn(kGoogleID);
OCMStub([mockVeriyAssertionResponse providerID]).andReturn(FIRGoogleAuthProviderID);
OCMStub([mockVeriyAssertionResponse localID]).andReturn(kLocalID);
OCMStub([mockVeriyAssertionResponse displayName]).andReturn(kGoogleDisplayName);
[self stubTokensWithMockResponse:mockVeriyAssertionResponse];
callback(mockVeriyAssertionResponse, nil);
});
});
[self expectGetAccountInfoGoogle];
XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
[[FIRAuth auth] signOut:NULL];
id mockProvider = OCMClassMock([FIROAuthProvider class]);
OCMExpect([mockProvider getCredentialWithUIDelegate:[OCMArg any] completion:[OCMArg any]])
.andCallBlock2(^(id<FIRAuthUIDelegate> delegate, FIRAuthCredentialCallback callback) {
dispatch_async(FIRAuthGlobalWorkQueue(), ^(){
FIROAuthCredential *credential =
[[FIROAuthCredential alloc] initWithProviderID:FIRGoogleAuthProviderID
sessionID:kOAuthSessionID
OAuthResponseURLString:kOAuthRequestURI];
callback(credential, nil);
});
});
[mockProvider getCredentialWithUIDelegate:nil
completion:^(FIRAuthCredential *_Nullable credential,
NSError *_Nullable error) {
XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]);
FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential;
XCTAssertEqualObjects(OAuthCredential.OAuthResponseURLString, kOAuthRequestURI);
XCTAssertEqualObjects(OAuthCredential.sessionID, kOAuthSessionID);
[[FIRAuth auth] signInWithCredential:OAuthCredential completion:^(FIRUser *_Nullable user,
NSError *_Nullable error) {
XCTAssertTrue([NSThread isMainThread]);
[self assertUserGoogle:user];
XCTAssertNil(error);
[expectation fulfill];
}];
}];
[self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
[self assertUserGoogle:[FIRAuth auth].currentUser];
OCMVerifyAll(_mockBackend);
}
#endif // TARGET_OS_IOS

/** @fn testSignInAndRetrieveDataWithCredentialSuccess
@brief Tests the flow of a successful @c signInAndRetrieveDataWithCredential:completion: call
with an Google Sign-In credential.
Expand Down

0 comments on commit 2e77efd

Please sign in to comment.