From 067f3fe08fb038586c5c494b0cd9aab3c005f3d1 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Thu, 26 Oct 2023 15:40:05 -0500 Subject: [PATCH 1/9] upgrade Google Sign In to 7.0 for iOS --- .../ios/RunnerTests/GoogleSignInTests.m | 274 ++++++++++-------- .../ios/Classes/FLTGoogleSignInPlugin.h | 8 + .../ios/Classes/FLTGoogleSignInPlugin.m | 125 ++++---- .../ios/google_sign_in_ios.podspec | 2 +- 4 files changed, 233 insertions(+), 176 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index 8e27c39a841..f0c9c6a72d0 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -44,7 +44,7 @@ - (void)testSignOut { } - (void)testDisconnect { - [[self.mockSignIn stub] disconnectWithCallback:[OCMArg invokeBlockWithArgs:[NSNull null], nil]]; + [[self.mockSignIn stub] disconnectWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin disconnectWithCompletion:^(FlutterError *error) { @@ -58,7 +58,7 @@ - (void)testDisconnectIgnoresError { NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeHasNoAuthInKeychain userInfo:nil]; - [[self.mockSignIn stub] disconnectWithCallback:[OCMArg invokeBlockWithArgs:error, nil]]; + [[self.mockSignIn stub] disconnectWithCompletion:[OCMArg invokeBlockWithArgs:error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin disconnectWithCompletion:^(FlutterError *error) { @@ -70,7 +70,7 @@ - (void)testDisconnectIgnoresError { #pragma mark - Init -- (void)testInitNoClientIdError { +- (void)testInitNoClientIdNoError { // Init plugin without GoogleService-Info.plist. self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn withGoogleServiceProperties:nil]; @@ -83,7 +83,7 @@ - (void)testInitNoClientIdError { FlutterError *error; [self.plugin initializeSignInWithParameters:params error:&error]; - XCTAssertEqualObjects(error.code, @"missing-config"); + XCTAssertNil(error); } - (void)testInitGoogleServiceInfoPlist { @@ -100,19 +100,16 @@ - (void)testInitGoogleServiceInfoPlist { [self.plugin signInWithCompletion:^(FSIUserData *user, FlutterError *error){ }]; OCMVerify([self.mockSignIn - signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { - // Set in example app GoogleService-Info.plist. - return - [configuration.hostedDomain isEqualToString:@"example.com"] && - [configuration.clientID - isEqualToString: - @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"] && - [configuration.serverClientID isEqualToString:@"YOUR_SERVER_CLIENT_ID"]; - }] - presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] - hint:nil - additionalScopes:OCMOCK_ANY - callback:OCMOCK_ANY]); + signInWithPresentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:OCMOCK_ANY + completion:OCMOCK_ANY]); + + XCTAssertEqualObjects(self.plugin.configuration.hostedDomain, @"example.com"); + XCTAssertEqualObjects( + self.plugin.configuration.clientID, + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); + XCTAssertEqualObjects(self.plugin.configuration.serverClientID, @"YOUR_SERVER_CLIENT_ID"); } - (void)testInitDynamicClientIdNullDomain { @@ -133,14 +130,14 @@ - (void)testInitDynamicClientIdNullDomain { [self.plugin signInWithCompletion:^(FSIUserData *user, FlutterError *error){ }]; OCMVerify([self.mockSignIn - signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { - return configuration.hostedDomain == nil && - [configuration.clientID isEqualToString:@"mockClientId"]; - }] - presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] - hint:nil - additionalScopes:OCMOCK_ANY - callback:OCMOCK_ANY]); + signInWithPresentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:OCMOCK_ANY + completion:OCMOCK_ANY]); + + XCTAssertEqualObjects(self.plugin.configuration.hostedDomain, nil); + XCTAssertEqualObjects(self.plugin.configuration.clientID, @"mockClientId"); + XCTAssertEqualObjects(self.plugin.configuration.serverClientID, nil); } - (void)testInitDynamicServerClientIdNullDomain { @@ -156,14 +153,16 @@ - (void)testInitDynamicServerClientIdNullDomain { [self.plugin signInWithCompletion:^(FSIUserData *user, FlutterError *error){ }]; OCMVerify([self.mockSignIn - signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { - return configuration.hostedDomain == nil && - [configuration.serverClientID isEqualToString:@"mockServerClientId"]; - }] - presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] - hint:nil - additionalScopes:OCMOCK_ANY - callback:OCMOCK_ANY]); + signInWithPresentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:OCMOCK_ANY + completion:OCMOCK_ANY]); + + XCTAssertEqualObjects(self.plugin.configuration.hostedDomain, nil); + XCTAssertEqualObjects( + self.plugin.configuration.clientID, + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); + XCTAssertEqualObjects(self.plugin.configuration.serverClientID, @"mockServerClientId"); } #pragma mark - Is signed in @@ -193,7 +192,8 @@ - (void)testSignInSilently { OCMStub([mockUser userID]).andReturn(@"mockID"); [[self.mockSignIn stub] - restorePreviousSignInWithCallback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + restorePreviousSignInWithCompletion:[OCMArg + invokeBlockWithArgs:mockUser, [NSNull null], nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInSilentlyWithCompletion:^(FSIUserData *user, FlutterError *error) { @@ -215,7 +215,7 @@ - (void)testSignInSilentlyWithError { userInfo:nil]; [[self.mockSignIn stub] - restorePreviousSignInWithCallback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + restorePreviousSignInWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInSilentlyWithCompletion:^(FSIUserData *user, FlutterError *error) { @@ -239,18 +239,17 @@ - (void)testSignIn { OCMStub([mockUser profile]).andReturn(mockUserProfile); OCMStub([mockUser userID]).andReturn(@"mockID"); - OCMStub([mockUser serverAuthCode]).andReturn(@"mockAuthCode"); + + id mockSignInResult = OCMClassMock([GIDSignInResult class]); + OCMStub([mockSignInResult user]).andReturn(mockUser); + OCMStub([mockSignInResult serverAuthCode]).andReturn(@"mockAuthCode"); [[self.mockSignIn expect] - signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { - return [configuration.clientID - isEqualToString: - @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"]; - }] - presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] - hint:nil - additionalScopes:@[] - callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + signInWithPresentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:@[] + completion:[OCMArg invokeBlockWithArgs:mockSignInResult, + [NSNull null], nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithCompletion:^(FSIUserData *user, FlutterError *error) { @@ -264,6 +263,10 @@ - (void)testSignIn { }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; + XCTAssertEqualObjects( + self.plugin.configuration.clientID, + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); + OCMVerifyAll(self.mockSignIn); } @@ -278,16 +281,18 @@ - (void)testSignInWithInitializedScopes { id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([mockUser userID]).andReturn(@"mockID"); + id mockSignInResult = OCMClassMock([GIDSignInResult class]); + OCMStub([mockSignInResult user]).andReturn(mockUser); [[self.mockSignIn expect] - signInWithConfiguration:OCMOCK_ANY - presentingViewController:OCMOCK_ANY - hint:nil - additionalScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { - return [[NSSet setWithArray:scopes] - isEqualToSet:[NSSet setWithObjects:@"initial1", @"initial2", nil]]; - }] - callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + signInWithPresentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { + return [[NSSet setWithArray:scopes] + isEqualToSet:[NSSet setWithObjects:@"initial1", @"initial2", nil]]; + }] + completion:[OCMArg invokeBlockWithArgs:mockSignInResult, + [NSNull null], nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithCompletion:^(FSIUserData *user, FlutterError *error) { @@ -303,20 +308,22 @@ - (void)testSignInWithInitializedScopes { - (void)testSignInAlreadyGranted { id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([mockUser userID]).andReturn(@"mockID"); + id mockSignInResult = OCMClassMock([GIDSignInResult class]); + OCMStub([mockSignInResult user]).andReturn(mockUser); [[self.mockSignIn stub] - signInWithConfiguration:OCMOCK_ANY - presentingViewController:OCMOCK_ANY - hint:nil - additionalScopes:OCMOCK_ANY - callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + signInWithPresentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:OCMOCK_ANY + completion:[OCMArg invokeBlockWithArgs:mockSignInResult, + [NSNull null], nil]]; NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeScopesAlreadyGranted userInfo:nil]; - [[self.mockSignIn stub] addScopes:OCMOCK_ANY - presentingViewController:OCMOCK_ANY - callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + [[self.mockSignIn currentUser] addScopes:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + completion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithCompletion:^(FSIUserData *user, FlutterError *error) { @@ -332,11 +339,10 @@ - (void)testSignInError { code:kGIDSignInErrorCodeCanceled userInfo:nil]; [[self.mockSignIn stub] - signInWithConfiguration:OCMOCK_ANY - presentingViewController:OCMOCK_ANY - hint:nil - additionalScopes:OCMOCK_ANY - callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + signInWithPresentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:OCMOCK_ANY + completion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin signInWithCompletion:^(FSIUserData *user, FlutterError *error) { @@ -348,11 +354,10 @@ - (void)testSignInError { } - (void)testSignInException { - OCMExpect([self.mockSignIn signInWithConfiguration:OCMOCK_ANY - presentingViewController:OCMOCK_ANY - hint:OCMOCK_ANY - additionalScopes:OCMOCK_ANY - callback:OCMOCK_ANY]) + OCMExpect([self.mockSignIn signInWithPresentingViewController:OCMOCK_ANY + hint:OCMOCK_ANY + additionalScopes:OCMOCK_ANY + completion:OCMOCK_ANY]) .andThrow([NSException exceptionWithName:@"MockName" reason:@"MockReason" userInfo:nil]); __block FlutterError *error; @@ -371,14 +376,20 @@ - (void)testSignInException { - (void)testGetTokens { id mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); + id mockUserResponse = OCMClassMock([GIDGoogleUser class]); + + id mockIdToken = OCMClassMock([GIDToken class]); + OCMStub([mockIdToken tokenString]).andReturn(@"mockIdToken"); + OCMStub([mockUserResponse idToken]).andReturn(mockIdToken); + + id mockAccessToken = OCMClassMock([GIDToken class]); + OCMStub([mockAccessToken tokenString]).andReturn(@"mockAccessToken"); + OCMStub([mockUserResponse accessToken]).andReturn(mockAccessToken); - id mockAuthentication = OCMClassMock([GIDAuthentication class]); - OCMStub([mockAuthentication idToken]).andReturn(@"mockIdToken"); - OCMStub([mockAuthentication accessToken]).andReturn(@"mockAccessToken"); - [[mockAuthentication stub] - doWithFreshTokens:[OCMArg invokeBlockWithArgs:mockAuthentication, [NSNull null], nil]]; - OCMStub([mockUser authentication]).andReturn(mockAuthentication); + [[mockUser stub] + refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:mockUserResponse, + [NSNull null], nil]]; + OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin getAccessTokenWithCompletion:^(FSITokenData *token, FlutterError *error) { @@ -394,13 +405,11 @@ - (void)testGetTokensNoAuthKeychainError { id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - id mockAuthentication = OCMClassMock([GIDAuthentication class]); NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeHasNoAuthInKeychain userInfo:nil]; - [[mockAuthentication stub] - doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; - OCMStub([mockUser authentication]).andReturn(mockAuthentication); + [[mockUser stub] + refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin getAccessTokenWithCompletion:^(FSITokenData *token, FlutterError *error) { @@ -416,13 +425,11 @@ - (void)testGetTokensCancelledError { id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - id mockAuthentication = OCMClassMock([GIDAuthentication class]); NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeCanceled userInfo:nil]; - [[mockAuthentication stub] - doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; - OCMStub([mockUser authentication]).andReturn(mockAuthentication); + [[mockUser stub] + refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin getAccessTokenWithCompletion:^(FSITokenData *token, FlutterError *error) { @@ -438,11 +445,9 @@ - (void)testGetTokensURLError { id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - id mockAuthentication = OCMClassMock([GIDAuthentication class]); NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:nil]; - [[mockAuthentication stub] - doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; - OCMStub([mockUser authentication]).andReturn(mockAuthentication); + [[mockUser stub] + refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin getAccessTokenWithCompletion:^(FSITokenData *token, FlutterError *error) { @@ -458,11 +463,9 @@ - (void)testGetTokensUnknownError { id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - id mockAuthentication = OCMClassMock([GIDAuthentication class]); NSError *error = [NSError errorWithDomain:@"BogusDomain" code:42 userInfo:nil]; - [[mockAuthentication stub] - doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; - OCMStub([mockUser authentication]).andReturn(mockAuthentication); + [[mockUser stub] + refreshTokensIfNeededWithCompletion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin getAccessTokenWithCompletion:^(FSITokenData *token, FlutterError *error) { @@ -477,13 +480,6 @@ - (void)testGetTokensUnknownError { #pragma mark - Request scopes - (void)testRequestScopesResultErrorIfNotSignedIn { - NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain - code:kGIDSignInErrorCodeNoCurrentUser - userInfo:nil]; - [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] - presentingViewController:OCMOCK_ANY - callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; - XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin requestScopes:@[ @"mockScope1" ] completion:^(NSNumber *success, FlutterError *error) { @@ -495,12 +491,15 @@ - (void)testRequestScopesResultErrorIfNotSignedIn { } - (void)testRequestScopesIfNoMissingScope { + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain code:kGIDSignInErrorCodeScopesAlreadyGranted userInfo:nil]; - [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] - presentingViewController:OCMOCK_ANY - callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + [[mockUser stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + completion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin requestScopes:@[ @"mockScope1" ] @@ -512,11 +511,35 @@ - (void)testRequestScopesIfNoMissingScope { [self waitForExpectationsWithTimeout:5.0 handler:nil]; } +- (void)testRequestScopesResultErrorIfMismatchingUser { + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); + + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeMismatchWithCurrentUser + userInfo:nil]; + [[mockUser stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + completion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; + [self.plugin requestScopes:@[ @"mockScope1" ] + completion:^(NSNumber *success, FlutterError *error) { + XCTAssertNil(success); + XCTAssertEqualObjects(error.code, @"request_scopes"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + - (void)testRequestScopesWithUnknownError { + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); + NSError *error = [NSError errorWithDomain:@"BogusDomain" code:42 userInfo:nil]; - [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] - presentingViewController:OCMOCK_ANY - callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + [[mockUser stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + completion:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin requestScopes:@[ @"mockScope1" ] @@ -529,7 +552,10 @@ - (void)testRequestScopesWithUnknownError { } - (void)testRequestScopesException { - OCMExpect([self.mockSignIn addScopes:@[] presentingViewController:OCMOCK_ANY callback:OCMOCK_ANY]) + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); + + OCMExpect([mockUser addScopes:@[] presentingViewController:OCMOCK_ANY completion:OCMOCK_ANY]) .andThrow([NSException exceptionWithName:@"MockName" reason:@"MockReason" userInfo:nil]); [self.plugin requestScopes:@[] @@ -542,16 +568,18 @@ - (void)testRequestScopesException { } - (void)testRequestScopesReturnsFalseIfOnlySubsetGranted { - GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); + id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); NSArray *requestedScopes = @[ @"mockScope1", @"mockScope2" ]; // Only grant one of the two requested scopes. - OCMStub(mockUser.grantedScopes).andReturn(@[ @"mockScope1" ]); + id mockSignInResult = OCMClassMock([GIDSignInResult class]); + OCMStub([mockUser grantedScopes]).andReturn(@[ @"mockScope1" ]); + OCMStub([mockSignInResult user]).andReturn(mockUser); - [[self.mockSignIn stub] addScopes:requestedScopes - presentingViewController:OCMOCK_ANY - callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + [[mockUser stub] addScopes:requestedScopes + presentingViewController:OCMOCK_ANY + completion:[OCMArg invokeBlockWithArgs:mockSignInResult, [NSNull null], nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin requestScopes:requestedScopes @@ -564,6 +592,9 @@ - (void)testRequestScopesReturnsFalseIfOnlySubsetGranted { } - (void)testRequestsInitializedScopes { + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); + FSIInitParams *params = [FSIInitParams makeWithScopes:@[ @"initial1", @"initial2" ] hostedDomain:nil clientId:nil @@ -580,27 +611,28 @@ - (void)testRequestsInitializedScopes { }]; // All four scopes are requested. - [[self.mockSignIn verify] - addScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { + [[mockUser verify] addScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { return [[NSSet setWithArray:scopes] isEqualToSet:[NSSet setWithObjects:@"initial1", @"initial2", @"addScope1", @"addScope2", nil]]; }] presentingViewController:OCMOCK_ANY - callback:OCMOCK_ANY]; + completion:OCMOCK_ANY]; } - (void)testRequestScopesReturnsTrueIfGranted { - GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); + id mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); NSArray *requestedScopes = @[ @"mockScope1", @"mockScope2" ]; // Grant both of the requested scopes. - OCMStub(mockUser.grantedScopes).andReturn(requestedScopes); + id mockSignInResult = OCMClassMock([GIDSignInResult class]); + OCMStub([mockUser grantedScopes]).andReturn(requestedScopes); + OCMStub([mockSignInResult user]).andReturn(mockUser); - [[self.mockSignIn stub] addScopes:requestedScopes - presentingViewController:OCMOCK_ANY - callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + [[mockUser stub] addScopes:requestedScopes + presentingViewController:OCMOCK_ANY + completion:[OCMArg invokeBlockWithArgs:mockSignInResult, [NSNull null], nil]]; XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"]; [self.plugin requestScopes:requestedScopes diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h index ec5403f0d3d..bc7f1417a1a 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h @@ -6,5 +6,13 @@ #import "messages.g.h" +#import + @interface FLTGoogleSignInPlugin : NSObject + +// Configuration wrapping Google Cloud Console, Google Apps, OpenID, +// and other initialization metadata. +// @property(strong) GIDConfiguration *configuration; +@property(strong, nonatomic) GIDConfiguration *configuration; + @end diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index 193c15e1ee3..bd66450dd33 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -47,10 +47,6 @@ @interface FLTGoogleSignInPlugin () -// Configuration wrapping Google Cloud Console, Google Apps, OpenID, -// and other initialization metadata. -@property(strong) GIDConfiguration *configuration; - // Permissions requested during at sign in "init" method call // unioned with scopes requested later with incremental authorization // "requestScopes" method call. @@ -116,18 +112,14 @@ - (void)initializeSignInWithParameters:(nonnull FSIInitParams *)params if (configuration != nil) { self.requestedScopes = [NSSet setWithArray:params.scopes]; self.configuration = configuration; - } else { - *error = [FlutterError errorWithCode:@"missing-config" - message:@"GoogleService-Info.plist file not found and clientId " - @"was not provided programmatically." - details:nil]; } } - (void)signInSilentlyWithCompletion:(nonnull void (^)(FSIUserData *_Nullable, FlutterError *_Nullable))completion { - [self.signIn restorePreviousSignInWithCallback:^(GIDGoogleUser *user, NSError *error) { - [self didSignInForUser:user withCompletion:completion error:error]; + [self.signIn restorePreviousSignInWithCompletion:^(GIDGoogleUser *_Nullable user, + NSError *_Nullable error) { + [self didSignInForUser:user serverAuthCode:nil withCompletion:completion error:error]; }]; } @@ -139,19 +131,36 @@ - (nullable NSNumber *)isSignedInWithError: - (void)signInWithCompletion:(nonnull void (^)(FSIUserData *_Nullable, FlutterError *_Nullable))completion { @try { - GIDConfiguration *configuration = self.configuration - ?: [self configurationWithClientIdArgument:nil - serverClientIdArgument:nil - hostedDomainArgument:nil]; - [self.signIn signInWithConfiguration:configuration - presentingViewController:[self topViewController] - hint:nil - additionalScopes:self.requestedScopes.allObjects - callback:^(GIDGoogleUser *user, NSError *error) { - [self didSignInForUser:user - withCompletion:completion - error:error]; - }]; + // If the configuration settings are passed from the Dart API, use those. + // Otherwise, use settings from the GoogleService-Info.plist if available. + // If neither are available, do not set the configuration - GIDSignIn will automatically use + // settings from the Info.plist (which is the recommended method). + if (!self.configuration && self.googleServiceProperties) { + self.configuration = [self configurationWithClientIdArgument:nil + serverClientIdArgument:nil + hostedDomainArgument:nil]; + } + if (self.configuration) { + self.signIn.configuration = self.configuration; + } + + [self.signIn signInWithPresentingViewController:[self topViewController] + hint:nil + additionalScopes:self.requestedScopes.allObjects + completion:^(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error) { + GIDGoogleUser *user; + NSString *serverAuthCode; + if (signInResult) { + user = signInResult.user; + serverAuthCode = signInResult.serverAuthCode; + } + + [self didSignInForUser:user + serverAuthCode:serverAuthCode + withCompletion:completion + error:error]; + }]; } @catch (NSException *e) { completion(nil, [FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]); [e raise]; @@ -161,13 +170,13 @@ - (void)signInWithCompletion:(nonnull void (^)(FSIUserData *_Nullable, - (void)getAccessTokenWithCompletion:(nonnull void (^)(FSITokenData *_Nullable, FlutterError *_Nullable))completion { GIDGoogleUser *currentUser = self.signIn.currentUser; - GIDAuthentication *auth = currentUser.authentication; - [auth doWithFreshTokens:^void(GIDAuthentication *authentication, NSError *error) { + [currentUser refreshTokensIfNeededWithCompletion:^(GIDGoogleUser *_Nullable user, + NSError *_Nullable error) { if (error) { completion(nil, getFlutterError(error)); } else { - completion([FSITokenData makeWithIdToken:authentication.idToken - accessToken:authentication.accessToken], + completion([FSITokenData makeWithIdToken:user.idToken.tokenString + accessToken:user.accessToken.tokenString], nil); } }]; @@ -177,7 +186,7 @@ - (void)signOutWithError:(FlutterError *_Nullable *_Nonnull)error; { [self.signIn signOut]; } - (void)disconnectWithCompletion:(nonnull void (^)(FlutterError *_Nullable))completion { - [self.signIn disconnectWithCallback:^(NSError *error) { + [self.signIn disconnectWithCompletion:^(NSError *_Nullable error) { // TODO(stuartmorgan): This preserves the pre-Pigeon-migration behavior, but it's unclear why // 'error' is being ignored here. completion(nil); @@ -190,31 +199,38 @@ - (void)requestScopes:(nonnull NSArray *)scopes NSSet *requestedScopes = self.requestedScopes; @try { - [self.signIn addScopes:requestedScopes.allObjects + GIDGoogleUser *currentUser = self.signIn.currentUser; + if (currentUser == nil) { + completion(nil, [FlutterError errorWithCode:@"sign_in_required" + message:@"No account to grant scopes." + details:nil]); + } + [currentUser addScopes:requestedScopes.allObjects presentingViewController:[self topViewController] - callback:^(GIDGoogleUser *addedScopeUser, NSError *addedScopeError) { - BOOL granted = NO; - FlutterError *error = nil; - if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] && - addedScopeError.code == kGIDSignInErrorCodeNoCurrentUser) { - error = [FlutterError errorWithCode:@"sign_in_required" - message:@"No account to grant scopes." - details:nil]; - } else if ([addedScopeError.domain - isEqualToString:kGIDSignInErrorDomain] && - addedScopeError.code == - kGIDSignInErrorCodeScopesAlreadyGranted) { - // Scopes already granted, report success. - granted = YES; - } else if (addedScopeUser == nil) { - granted = NO; - } else { - NSSet *grantedScopes = - [NSSet setWithArray:addedScopeUser.grantedScopes]; - granted = [requestedScopes isSubsetOfSet:grantedScopes]; - } - completion(error == nil ? @(granted) : nil, error); - }]; + completion:^(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable addedScopeError) { + BOOL granted = NO; + FlutterError *error = nil; + + if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] && + addedScopeError.code == kGIDSignInErrorCodeMismatchWithCurrentUser) { + error = + [FlutterError errorWithCode:@"request_scopes" + message:@"There is an operation on a previous " + @"user. Try signing in again." + details:nil]; + } else if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] && + addedScopeError.code == + kGIDSignInErrorCodeScopesAlreadyGranted) { + // Scopes already granted, report success. + granted = YES; + } else if (signInResult.user) { + NSSet *grantedScopes = + [NSSet setWithArray:signInResult.user.grantedScopes]; + granted = [requestedScopes isSubsetOfSet:grantedScopes]; + } + completion(error == nil ? @(granted) : nil, error); + }]; } @catch (NSException *e) { completion(nil, [FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]); } @@ -265,6 +281,7 @@ - (GIDConfiguration *)configurationWithClientIdArgument:(id)clientIDArg } - (void)didSignInForUser:(GIDGoogleUser *)user + serverAuthCode:(NSString *_Nullable)serverAuthCode withCompletion:(nonnull void (^)(FSIUserData *_Nullable, FlutterError *_Nullable))completion error:(NSError *)error { @@ -281,7 +298,7 @@ - (void)didSignInForUser:(GIDGoogleUser *)user email:user.profile.email userId:user.userID photoUrl:[photoUrl absoluteString] - serverAuthCode:user.serverAuthCode], + serverAuthCode:serverAuthCode], nil); } } diff --git a/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec index 7739005bdd4..9658184c883 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec +++ b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec @@ -16,7 +16,7 @@ Enables Google Sign-In in Flutter apps. s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/FLTGoogleSignInPlugin.modulemap' s.dependency 'Flutter' - s.dependency 'GoogleSignIn', '~> 6.2' + s.dependency 'GoogleSignIn', '~> 7.0' s.static_framework = true s.platform = :ios, '11.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } From bb30b60f96a691382a67f4ee70dec9f6d1ad0b49 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Thu, 26 Oct 2023 15:49:10 -0500 Subject: [PATCH 2/9] update changelog and version --- packages/google_sign_in/google_sign_in_ios/CHANGELOG.md | 4 ++++ packages/google_sign_in/google_sign_in_ios/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index 4f67a64510f..28641320706 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.6.5 + +* Upgrades GoogleSignIn iOS SDK to 7.0. + ## 5.6.4 * Converts platform communication to Pigeon. diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index c2e780e4ac4..5fda9236b80 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_ios description: iOS implementation of the google_sign_in plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.6.4 +version: 5.6.5 environment: sdk: ">=2.19.0 <4.0.0" From 815fd8f4a7e8ceb6bafd82c2dc531fa90b75d0ab Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Fri, 27 Oct 2023 08:57:28 -0500 Subject: [PATCH 3/9] remove comment and make match --- .../google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h index bc7f1417a1a..4d645e62b0b 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h @@ -12,7 +12,6 @@ // Configuration wrapping Google Cloud Console, Google Apps, OpenID, // and other initialization metadata. -// @property(strong) GIDConfiguration *configuration; -@property(strong, nonatomic) GIDConfiguration *configuration; +@property(strong) GIDConfiguration *configuration; @end From 177f2f63aab0b31d4e834fb0f515f054ac93b1d2 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Fri, 3 Nov 2023 15:09:08 -0500 Subject: [PATCH 4/9] Update README, update example app to use recommended settings, add value for dart type, fix scopes not being set, add more tests --- .../google_sign_in/google_sign_in/README.md | 58 +------------------ .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../example/ios/Runner/Info.plist | 4 ++ .../google_sign_in_ios/README.md | 53 +++++++++++++++++ .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../ios/Runner/GoogleService-Info.plist | 44 -------------- .../example/ios/Runner/Info.plist | 4 ++ .../ios/RunnerTests}/GoogleService-Info.plist | 0 .../ios/RunnerTests/GoogleSignInTests.m | 38 +++++++++++- .../ios/Classes/FLTGoogleSignInPlugin.h | 4 -- .../ios/Classes/FLTGoogleSignInPlugin.m | 21 +++---- .../ios/Classes/FLTGoogleSignInPlugin_Test.h | 14 +++++ .../ios/Classes/messages.g.h | 4 +- .../ios/Classes/messages.g.m | 6 +- .../lib/google_sign_in_ios.dart | 1 + .../lib/src/messages.g.dart | 5 ++ .../google_sign_in_ios/pigeons/messages.dart | 2 + .../test/google_sign_in_ios_test.dart | 3 + 19 files changed, 147 insertions(+), 128 deletions(-) delete mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist rename packages/google_sign_in/{google_sign_in/example/ios/Runner => google_sign_in_ios/example/ios/RunnerTests}/GoogleService-Info.plist (100%) diff --git a/packages/google_sign_in/google_sign_in/README.md b/packages/google_sign_in/google_sign_in/README.md index 154e683c376..87f28571805 100644 --- a/packages/google_sign_in/google_sign_in/README.md +++ b/packages/google_sign_in/google_sign_in/README.md @@ -27,59 +27,7 @@ Otherwise, you may encounter `APIException` errors. ### iOS integration -1. [First register your application](https://firebase.google.com/docs/ios/setup). -2. Make sure the file you download in step 1 is named - `GoogleService-Info.plist`. -3. Move or copy `GoogleService-Info.plist` into the `[my_project]/ios/Runner` - directory. -4. Open Xcode, then right-click on `Runner` directory and select - `Add Files to "Runner"`. -5. Select `GoogleService-Info.plist` from the file manager. -6. A dialog will show up and ask you to select the targets, select the `Runner` - target. -7. If you need to authenticate to a backend server you can add a - `SERVER_CLIENT_ID` key value pair in your `GoogleService-Info.plist`. - ```xml - SERVER_CLIENT_ID - [YOUR SERVER CLIENT ID] - ``` -8. Then add the `CFBundleURLTypes` attributes below into the - `[my_project]/ios/Runner/Info.plist` file. - -```xml - - -CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLSchemes - - - - com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn - - - - -``` - -As an alternative to adding `GoogleService-Info.plist` to your Xcode project, -you can instead configure your app in Dart code. In this case, skip steps 3 to 7 - and pass `clientId` and `serverClientId` to the `GoogleSignIn` constructor: - -```dart -GoogleSignIn _googleSignIn = GoogleSignIn( - ... - // The OAuth client id of your app. This is required. - clientId: ..., - // If you need to authenticate to a backend server, specify its OAuth client. This is optional. - serverClientId: ..., -); -``` - -Note that step 8 is still required. +Please see [instructions on integrating Google Sign-In for iOS](https://pub.dev/packages/google_sign_in_ios#ios-integration). #### iOS additional requirement @@ -212,12 +160,12 @@ For more details, take a look at the ### Does an app always need to check `canAccessScopes`? -The new web SDK implicitly grant access to the `email`, `profile` and `openid` +The new web SDK implicitly grant access to the `email`, `profile` and `openid` scopes when users complete the sign-in process (either via the One Tap UX or the Google Sign In button). If an app only needs an `idToken`, or only requests permissions to any/all of -the three scopes mentioned above +the three scopes mentioned above ([OpenID Connect scopes](https://developers.google.com/identity/protocols/oauth2/scopes#openid-connect)), it won't need to implement any additional scope handling. diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj index 45b4e802664..5af6f7cec13 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */; }; - 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */; }; 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -39,7 +38,6 @@ 5A76713E622F06379AEDEBFA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 5C6F5A6C1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -118,7 +116,6 @@ 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */, 97C147021CF9000F007C117D /* Info.plist */, 97C146F11CF9000F007C117D /* Supporting Files */, ); @@ -173,7 +170,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -204,7 +201,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index f4569c48ce1..8e83ef7194e 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ UIApplicationSupportsIndirectInputEvents + GIDClientID + 479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com + GIDServerClientID + YOUR_SERVER_CLIENT_ID diff --git a/packages/google_sign_in/google_sign_in_ios/README.md b/packages/google_sign_in/google_sign_in_ios/README.md index 6a5d862d77e..a6d3e36b928 100644 --- a/packages/google_sign_in/google_sign_in_ios/README.md +++ b/packages/google_sign_in/google_sign_in_ios/README.md @@ -13,3 +13,56 @@ should add it to your `pubspec.yaml` as usual. [1]: https://pub.dev/packages/google_sign_in [2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin + +### iOS integration + +1. [Get an OAuth client ID](https://developers.google.com/identity/sign-in/ios/start-integrating#get_an_oauth_client_id). +2. Add your client ID into your app's `[my_project]/ios/Runner/Info.plist` file. + ```xml + GIDClientID + [YOUR IOS CLIENT ID] + ``` +3. If you need to authenticate to a backend server you can add a + `GIDServerClientID` key value pair in your `[my_project]/ios/Runner/Info.plist` file. + ```xml + GIDServerClientID + [YOUR SERVER CLIENT ID] + ``` +4. Then add your reversed client ID in the `CFBundleURLTypes` attributes into the + `[my_project]/ios/Runner/Info.plist` file. + + The reversed client ID is shown under "iOS URL scheme" when [selecting an existing iOS OAuth client in the Cloud console](https://console.cloud.google.com/apis/credentials?project=_). + +```xml + + +CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + + [YOUR REVERSED CLIENT ID] + + + + +``` + +As an alternative to editing the `Info.plist` in your Xcode project, +you can instead configure your app in Dart code. In this case, skip steps 2 to 3 + and pass `clientId` and `serverClientId` to the `GoogleSignIn` constructor: + +```dart +GoogleSignIn _googleSignIn = GoogleSignIn( + ... + // The OAuth client id of your app. This is required. + clientId: ..., + // If you need to authenticate to a backend server, specify its OAuth client. This is optional. + serverClientId: ..., +); +``` + +Note that step 4 is still required. diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj index abbb5ef47ca..ca1508eba8c 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */; }; - 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */; }; + 78A36DA12AF5761E00CBFD43 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */; }; 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -163,7 +163,6 @@ 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */, 97C147021CF9000F007C117D /* Info.plist */, 97C146F11CF9000F007C117D /* Supporting Files */, ); @@ -191,6 +190,7 @@ isa = PBXGroup; children = ( F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */, + 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */, F76AC1A62666D0540040C8BC /* Info.plist */, ); path = RunnerTests; @@ -316,7 +316,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, @@ -328,6 +327,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 78A36DA12AF5761E00CBFD43 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist deleted file mode 100644 index 6042aab908a..00000000000 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist +++ /dev/null @@ -1,44 +0,0 @@ - - - - - AD_UNIT_ID_FOR_BANNER_TEST - ca-app-pub-3940256099942544/2934735716 - AD_UNIT_ID_FOR_INTERSTITIAL_TEST - ca-app-pub-3940256099942544/4411468910 - CLIENT_ID - 479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u - ANDROID_CLIENT_ID - 479882132969-jie8r1me6dsra60pal6ejaj8dgme3tg0.apps.googleusercontent.com - API_KEY - AIzaSyBECOwLTAN6PU4Aet1b2QLGIb3kRK8Xjew - GCM_SENDER_ID - 479882132969 - PLIST_VERSION - 1 - BUNDLE_ID - io.flutter.plugins.googleSignInExample - PROJECT_ID - my-flutter-proj - STORAGE_BUCKET - my-flutter-proj.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:479882132969:ios:2643f950e0a0da08 - DATABASE_URL - https://my-flutter-proj.firebaseio.com - SERVER_CLIENT_ID - YOUR_SERVER_CLIENT_ID - - \ No newline at end of file diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist index 08fef9a9fe4..5c3953aef76 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist @@ -60,5 +60,9 @@ UIApplicationSupportsIndirectInputEvents + GIDClientID + 479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com + GIDServerClientID + YOUR_SERVER_CLIENT_ID diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner/GoogleService-Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleService-Info.plist similarity index 100% rename from packages/google_sign_in/google_sign_in/example/ios/Runner/GoogleService-Info.plist rename to packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleService-Info.plist diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index f0c9c6a72d0..de504636e65 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -18,6 +18,7 @@ @interface FLTGoogleSignInPluginTest : XCTestCase @property(strong, nonatomic) NSObject *mockPluginRegistrar; @property(strong, nonatomic) FLTGoogleSignInPlugin *plugin; @property(strong, nonatomic) id mockSignIn; +@property(strong, nonatomic) NSDictionary *googleServiceInfo; @end @@ -34,6 +35,13 @@ - (void)setUp { OCMStub(self.mockPluginRegistrar.messenger).andReturn(self.mockBinaryMessenger); self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:mockSignIn]; [FLTGoogleSignInPlugin registerWithRegistrar:self.mockPluginRegistrar]; + + NSString *plistPath = + [[NSBundle bundleForClass:[self class]] pathForResource:@"GoogleService-Info" + ofType:@"plist"]; + if (plistPath) { + self.googleServiceInfo = [[NSDictionary alloc] initWithContentsOfFile:plistPath]; + } } - (void)testSignOut { @@ -87,6 +95,8 @@ - (void)testInitNoClientIdNoError { } - (void)testInitGoogleServiceInfoPlist { + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + withGoogleServiceProperties:self.googleServiceInfo]; FSIInitParams *params = [FSIInitParams makeWithScopes:@[] hostedDomain:@"example.com" clientId:nil @@ -106,6 +116,7 @@ - (void)testInitGoogleServiceInfoPlist { completion:OCMOCK_ANY]); XCTAssertEqualObjects(self.plugin.configuration.hostedDomain, @"example.com"); + // Set in example app GoogleService-Info.plist. XCTAssertEqualObjects( self.plugin.configuration.clientID, @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); @@ -141,6 +152,8 @@ - (void)testInitDynamicClientIdNullDomain { } - (void)testInitDynamicServerClientIdNullDomain { + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + withGoogleServiceProperties:self.googleServiceInfo]; FSIInitParams *params = [FSIInitParams makeWithScopes:@[] hostedDomain:nil clientId:nil @@ -159,12 +172,32 @@ - (void)testInitDynamicServerClientIdNullDomain { completion:OCMOCK_ANY]); XCTAssertEqualObjects(self.plugin.configuration.hostedDomain, nil); + // Set in example app GoogleService-Info.plist. XCTAssertEqualObjects( self.plugin.configuration.clientID, @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); XCTAssertEqualObjects(self.plugin.configuration.serverClientID, @"mockServerClientId"); } +- (void)testInitInfoPlist { + FSIInitParams *params = [FSIInitParams makeWithScopes:@[ @"scope1" ] + hostedDomain:@"example.com" + clientId:nil + serverClientId:nil]; + + FlutterError *error; + self.plugin = [[FLTGoogleSignInPlugin alloc] init]; + [self.plugin initializeSignInWithParameters:params error:&error]; + XCTAssertNil(error); + XCTAssertNil(self.plugin.configuration); + XCTAssertNotNil(self.plugin.requestedScopes); + // Set in example app Info.plist. + XCTAssertEqualObjects( + self.plugin.signIn.configuration.clientID, + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); + XCTAssertEqualObjects(self.plugin.signIn.configuration.serverClientID, @"YOUR_SERVER_CLIENT_ID"); +} + #pragma mark - Is signed in - (void)testIsNotSignedIn { @@ -229,6 +262,8 @@ - (void)testSignInSilentlyWithError { #pragma mark - Sign in - (void)testSignIn { + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + withGoogleServiceProperties:self.googleServiceInfo]; id mockUser = OCMClassMock([GIDGoogleUser class]); id mockUserProfile = OCMClassMock([GIDProfileData class]); OCMStub([mockUserProfile name]).andReturn(@"mockDisplay"); @@ -263,6 +298,7 @@ - (void)testSignIn { }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; + // Set in example app GoogleService-Info.plist. XCTAssertEqualObjects( self.plugin.configuration.clientID, @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"); @@ -526,7 +562,7 @@ - (void)testRequestScopesResultErrorIfMismatchingUser { [self.plugin requestScopes:@[ @"mockScope1" ] completion:^(NSNumber *success, FlutterError *error) { XCTAssertNil(success); - XCTAssertEqualObjects(error.code, @"request_scopes"); + XCTAssertEqualObjects(error.code, @"mismatch_user"); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h index 4d645e62b0b..fb6ec5a7c72 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h @@ -10,8 +10,4 @@ @interface FLTGoogleSignInPlugin : NSObject -// Configuration wrapping Google Cloud Console, Google Apps, OpenID, -// and other initialization metadata. -@property(strong) GIDConfiguration *configuration; - @end diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index bd66450dd33..8e898fada32 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -47,16 +47,6 @@ @interface FLTGoogleSignInPlugin () -// Permissions requested during at sign in "init" method call -// unioned with scopes requested later with incremental authorization -// "requestScopes" method call. -// The "email" and "profile" base scopes are always implicitly requested. -@property(copy) NSSet *requestedScopes; - -// Instance used to manage Google Sign In authentication including -// sign in, sign out, and requesting additional scopes. -@property(strong, readonly) GIDSignIn *signIn; - // The contents of GoogleService-Info.plist, if it exists. @property(strong, nullable) NSDictionary *googleServiceProperties; @@ -109,8 +99,8 @@ - (void)initializeSignInWithParameters:(nonnull FSIInitParams *)params GIDConfiguration *configuration = [self configurationWithClientIdArgument:params.clientId serverClientIdArgument:params.serverClientId hostedDomainArgument:params.hostedDomain]; + self.requestedScopes = [NSSet setWithArray:params.scopes]; if (configuration != nil) { - self.requestedScopes = [NSSet setWithArray:params.scopes]; self.configuration = configuration; } } @@ -215,7 +205,7 @@ - (void)requestScopes:(nonnull NSArray *)scopes if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] && addedScopeError.code == kGIDSignInErrorCodeMismatchWithCurrentUser) { error = - [FlutterError errorWithCode:@"request_scopes" + [FlutterError errorWithCode:@"mismatch_user" message:@"There is an operation on a previous " @"user. Try signing in again." details:nil]; @@ -294,11 +284,16 @@ - (void)didSignInForUser:(GIDGoogleUser *)user // Placeholder that will be replaced by on the Dart side based on screen size. photoUrl = [user.profile imageURLWithDimension:1337]; } + NSString *idToken; + if (user.idToken) { + idToken = user.idToken.tokenString; + } completion([FSIUserData makeWithDisplayName:user.profile.name email:user.profile.email userId:user.userID photoUrl:[photoUrl absoluteString] - serverAuthCode:serverAuthCode], + serverAuthCode:serverAuthCode + idToken:idToken], nil); } } diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h index 17ddb7f616b..2d8b799437a 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h @@ -13,6 +13,20 @@ NS_ASSUME_NONNULL_BEGIN /// Methods exposed for unit testing. @interface FLTGoogleSignInPlugin () +// Configuration wrapping Google Cloud Console, Google Apps, OpenID, +// and other initialization metadata. +@property(strong) GIDConfiguration *configuration; + +// Permissions requested during at sign in "init" method call +// unioned with scopes requested later with incremental authorization +// "requestScopes" method call. +// The "email" and "profile" base scopes are always implicitly requested. +@property(copy) NSSet *requestedScopes; + +// Instance used to manage Google Sign In authentication including +// sign in, sign out, and requesting additional scopes. +@property(strong, readonly) GIDSignIn *signIn; + /// Inject @c GIDSignIn for testing. - (instancetype)initWithSignIn:(GIDSignIn *)signIn; diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/messages.g.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/messages.g.h index 37493aa26c6..745c1ec9180 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/messages.g.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/messages.g.h @@ -43,12 +43,14 @@ NS_ASSUME_NONNULL_BEGIN email:(NSString *)email userId:(NSString *)userId photoUrl:(nullable NSString *)photoUrl - serverAuthCode:(nullable NSString *)serverAuthCode; + serverAuthCode:(nullable NSString *)serverAuthCode + idToken:(nullable NSString *)idToken; @property(nonatomic, copy, nullable) NSString *displayName; @property(nonatomic, copy) NSString *email; @property(nonatomic, copy) NSString *userId; @property(nonatomic, copy, nullable) NSString *photoUrl; @property(nonatomic, copy, nullable) NSString *serverAuthCode; +@property(nonatomic, copy, nullable) NSString *idToken; @end /// Pigeon version of GoogleSignInTokenData. diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/messages.g.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/messages.g.m index 4d617ca81f0..96d6b54232a 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/messages.g.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/messages.g.m @@ -86,13 +86,15 @@ + (instancetype)makeWithDisplayName:(nullable NSString *)displayName email:(NSString *)email userId:(NSString *)userId photoUrl:(nullable NSString *)photoUrl - serverAuthCode:(nullable NSString *)serverAuthCode { + serverAuthCode:(nullable NSString *)serverAuthCode + idToken:(nullable NSString *)idToken { FSIUserData *pigeonResult = [[FSIUserData alloc] init]; pigeonResult.displayName = displayName; pigeonResult.email = email; pigeonResult.userId = userId; pigeonResult.photoUrl = photoUrl; pigeonResult.serverAuthCode = serverAuthCode; + pigeonResult.idToken = idToken; return pigeonResult; } + (FSIUserData *)fromList:(NSArray *)list { @@ -104,6 +106,7 @@ + (FSIUserData *)fromList:(NSArray *)list { NSAssert(pigeonResult.userId != nil, @""); pigeonResult.photoUrl = GetNullableObjectAtIndex(list, 3); pigeonResult.serverAuthCode = GetNullableObjectAtIndex(list, 4); + pigeonResult.idToken = GetNullableObjectAtIndex(list, 5); return pigeonResult; } + (nullable FSIUserData *)nullableFromList:(NSArray *)list { @@ -116,6 +119,7 @@ - (NSArray *)toList { (self.userId ?: [NSNull null]), (self.photoUrl ?: [NSNull null]), (self.serverAuthCode ?: [NSNull null]), + (self.idToken ?: [NSNull null]), ]; } @end diff --git a/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart index 73113abb49a..9c589501114 100644 --- a/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart +++ b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart @@ -103,6 +103,7 @@ class GoogleSignInIOS extends GoogleSignInPlatform { displayName: data.displayName, photoUrl: data.photoUrl, serverAuthCode: data.serverAuthCode, + idToken: data.idToken, ); } diff --git a/packages/google_sign_in/google_sign_in_ios/lib/src/messages.g.dart b/packages/google_sign_in/google_sign_in_ios/lib/src/messages.g.dart index 78fa37259be..21dd2ebf8e2 100644 --- a/packages/google_sign_in/google_sign_in_ios/lib/src/messages.g.dart +++ b/packages/google_sign_in/google_sign_in_ios/lib/src/messages.g.dart @@ -60,6 +60,7 @@ class UserData { required this.userId, this.photoUrl, this.serverAuthCode, + this.idToken, }); String? displayName; @@ -72,6 +73,8 @@ class UserData { String? serverAuthCode; + String? idToken; + Object encode() { return [ displayName, @@ -79,6 +82,7 @@ class UserData { userId, photoUrl, serverAuthCode, + idToken, ]; } @@ -90,6 +94,7 @@ class UserData { userId: result[2]! as String, photoUrl: result[3] as String?, serverAuthCode: result[4] as String?, + idToken: result[5] as String?, ); } } diff --git a/packages/google_sign_in/google_sign_in_ios/pigeons/messages.dart b/packages/google_sign_in/google_sign_in_ios/pigeons/messages.dart index f92f1d06c63..2baf9af82ad 100644 --- a/packages/google_sign_in/google_sign_in_ios/pigeons/messages.dart +++ b/packages/google_sign_in/google_sign_in_ios/pigeons/messages.dart @@ -43,6 +43,7 @@ class UserData { this.displayName, this.photoUrl, this.serverAuthCode, + this.idToken, }); final String? displayName; @@ -50,6 +51,7 @@ class UserData { final String userId; final String? photoUrl; final String? serverAuthCode; + final String? idToken; } /// Pigeon version of GoogleSignInTokenData. diff --git a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart index ace1a0fec30..9604cbde41b 100644 --- a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart +++ b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart @@ -18,6 +18,7 @@ final GoogleSignInUserData _user = GoogleSignInUserData( photoUrl: 'https://lh5.googleusercontent.com/photo.jpg', displayName: 'John Doe', serverAuthCode: '789', + idToken: '123' ); final GoogleSignInTokenData _token = GoogleSignInTokenData( @@ -60,6 +61,7 @@ void main() { photoUrl: _user.photoUrl, displayName: _user.displayName, serverAuthCode: _user.serverAuthCode, + idToken: _user.idToken, )); final dynamic response = await googleSignIn.signInSilently(); @@ -82,6 +84,7 @@ void main() { photoUrl: _user.photoUrl, displayName: _user.displayName, serverAuthCode: _user.serverAuthCode, + idToken: _user.idToken, )); final dynamic response = await googleSignIn.signIn(); From 57a2b31b9ff7540d5a7fe7165a17bf176f009a03 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Fri, 3 Nov 2023 15:41:22 -0500 Subject: [PATCH 5/9] update README again --- .../google_sign_in_ios/README.md | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/README.md b/packages/google_sign_in/google_sign_in_ios/README.md index a6d3e36b928..76b9cab7caf 100644 --- a/packages/google_sign_in/google_sign_in_ios/README.md +++ b/packages/google_sign_in/google_sign_in_ios/README.md @@ -16,23 +16,28 @@ should add it to your `pubspec.yaml` as usual. ### iOS integration -1. [Get an OAuth client ID](https://developers.google.com/identity/sign-in/ios/start-integrating#get_an_oauth_client_id). -2. Add your client ID into your app's `[my_project]/ios/Runner/Info.plist` file. +1. [Create a Firebase project](https://firebase.google.com/docs/ios/setup#create-firebase-project) + and [register your application](https://firebase.google.com/docs/ios/setup#register-app). +2. [Enable Google Sign-In for your Firebase project](https://firebase.google.com/docs/auth/ios/google-signin#enable_google_sign-in_for_your_firebase_project). +3. Make sure to download a new copy of your project's + `GoogleService-Info.plist` from step 2. Do not put this file in your project. +4. Add the client ID from the `GoogleService-Info.plist` into your app's + `[my_project]/ios/Runner/Info.plist` file. ```xml GIDClientID + + [YOUR IOS CLIENT ID] ``` -3. If you need to authenticate to a backend server you can add a +5. If you need to authenticate to a backend server you can add a `GIDServerClientID` key value pair in your `[my_project]/ios/Runner/Info.plist` file. ```xml GIDServerClientID [YOUR SERVER CLIENT ID] ``` -4. Then add your reversed client ID in the `CFBundleURLTypes` attributes into the +6. Then add the `CFBundleURLTypes` attributes below into the `[my_project]/ios/Runner/Info.plist` file. - The reversed client ID is shown under "iOS URL scheme" when [selecting an existing iOS OAuth client in the Cloud console](https://console.cloud.google.com/apis/credentials?project=_). - ```xml @@ -44,7 +49,8 @@ should add it to your `pubspec.yaml` as usual. CFBundleURLSchemes - [YOUR REVERSED CLIENT ID] + + com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn @@ -52,7 +58,7 @@ should add it to your `pubspec.yaml` as usual. ``` As an alternative to editing the `Info.plist` in your Xcode project, -you can instead configure your app in Dart code. In this case, skip steps 2 to 3 +you can instead configure your app in Dart code. In this case, skip steps 4 to 5 and pass `clientId` and `serverClientId` to the `GoogleSignIn` constructor: ```dart @@ -65,4 +71,4 @@ GoogleSignIn _googleSignIn = GoogleSignIn( ); ``` -Note that step 4 is still required. +Note that step 6 is still required. \ No newline at end of file From 798fc19c38acbd5617a64954e1a922d0a664fc1d Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Fri, 3 Nov 2023 15:46:41 -0500 Subject: [PATCH 6/9] fix format --- .../test/google_sign_in_ios_test.dart | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart index 9604cbde41b..d76bc978fc7 100644 --- a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart +++ b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart @@ -13,13 +13,12 @@ import 'package:mockito/mockito.dart'; import 'google_sign_in_ios_test.mocks.dart'; final GoogleSignInUserData _user = GoogleSignInUserData( - email: 'john.doe@gmail.com', - id: '8162538176523816253123', - photoUrl: 'https://lh5.googleusercontent.com/photo.jpg', - displayName: 'John Doe', - serverAuthCode: '789', - idToken: '123' -); + email: 'john.doe@gmail.com', + id: '8162538176523816253123', + photoUrl: 'https://lh5.googleusercontent.com/photo.jpg', + displayName: 'John Doe', + serverAuthCode: '789', + idToken: '123'); final GoogleSignInTokenData _token = GoogleSignInTokenData( idToken: '123', From 04fe8a7169961ee31784ca8818fe81251e6892a9 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Fri, 3 Nov 2023 16:52:52 -0500 Subject: [PATCH 7/9] add changelog, update false secrets, use code-excerpt --- .../google_sign_in/CHANGELOG.md | 4 ++++ .../google_sign_in/pubspec.yaml | 5 +++-- .../test/google_sign_in_test.dart | 20 +++++++++++++++++++ .../google_sign_in_ios/README.md | 12 +++++------ 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 9668392da3e..7e5b8665a62 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.6 + +* Updates README to direct to google_sign_in_ios README for iOS integration instructions. + ## 6.1.5 * Adds pub topics to package metadata. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 2f3767f0cdb..05d8bfbc6de 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 6.1.5 +version: 6.1.6 environment: sdk: ">=2.19.0 <4.0.0" @@ -43,5 +43,6 @@ topics: # The example deliberately includes limited-use secrets. false_secrets: - /example/android/app/google-services.json - - /example/ios/Runner/GoogleService-Info.plist + - /example/ios/Runner/Info.plist + - /example/ios/RunnerTests/GoogleService-Info.plist - /example/ios/RunnerTests/GoogleSignInTests.m diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 86b83cf4a09..18b3da6f32c 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -81,6 +81,26 @@ void main() { verify(mockPlatform.signIn()); }); + test('clientId and serverClientId parameters is forwarded to implementation', () async { + // #docregion GoogleSignIn + final GoogleSignIn googleSignIn = GoogleSignIn( + // The OAuth client id of your app. This is required. + clientId: 'Your Client ID', + // If you need to authenticate to a backend server, specify its OAuth client. This is optional. + serverClientId: 'Your Server ID', + ); + // #enddocregion GoogleSignIn + + await googleSignIn.signIn(); + + _verifyInit( + mockPlatform, + clientId: 'Your Client ID', + serverClientId: 'Your Server ID', + ); + verify(mockPlatform.signIn()); + }); + test('forceCodeForRefreshToken sent with init method call', () async { final GoogleSignIn googleSignIn = GoogleSignIn(forceCodeForRefreshToken: true); diff --git a/packages/google_sign_in/google_sign_in_ios/README.md b/packages/google_sign_in/google_sign_in_ios/README.md index 76b9cab7caf..a7b64381113 100644 --- a/packages/google_sign_in/google_sign_in_ios/README.md +++ b/packages/google_sign_in/google_sign_in_ios/README.md @@ -61,14 +61,12 @@ As an alternative to editing the `Info.plist` in your Xcode project, you can instead configure your app in Dart code. In this case, skip steps 4 to 5 and pass `clientId` and `serverClientId` to the `GoogleSignIn` constructor: + ```dart -GoogleSignIn _googleSignIn = GoogleSignIn( - ... - // The OAuth client id of your app. This is required. - clientId: ..., - // If you need to authenticate to a backend server, specify its OAuth client. This is optional. - serverClientId: ..., +final GoogleSignIn googleSignIn = GoogleSignIn( + clientId: 'Your Client ID', + serverClientId: 'Your Server ID', ); ``` -Note that step 6 is still required. \ No newline at end of file +Note that step 6 is still required. From f66c4cb904323ddeadc10171da67a2e83fb85147 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Mon, 6 Nov 2023 10:14:47 -0600 Subject: [PATCH 8/9] fix more repo checks --- .../google_sign_in/test/google_sign_in_test.dart | 4 +++- packages/google_sign_in/google_sign_in_ios/README.md | 2 ++ packages/google_sign_in/google_sign_in_ios/pubspec.yaml | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 18b3da6f32c..94fabc58aec 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -81,7 +81,9 @@ void main() { verify(mockPlatform.signIn()); }); - test('clientId and serverClientId parameters is forwarded to implementation', () async { + test( + 'clientId and serverClientId parameters is forwarded to implementation', + () async { // #docregion GoogleSignIn final GoogleSignIn googleSignIn = GoogleSignIn( // The OAuth client id of your app. This is required. diff --git a/packages/google_sign_in/google_sign_in_ios/README.md b/packages/google_sign_in/google_sign_in_ios/README.md index a7b64381113..29bcc25fb18 100644 --- a/packages/google_sign_in/google_sign_in_ios/README.md +++ b/packages/google_sign_in/google_sign_in_ios/README.md @@ -64,7 +64,9 @@ you can instead configure your app in Dart code. In this case, skip steps 4 to 5 ```dart final GoogleSignIn googleSignIn = GoogleSignIn( + // The OAuth client id of your app. This is required. clientId: 'Your Client ID', + // If you need to authenticate to a backend server, specify its OAuth client. This is optional. serverClientId: 'Your Server ID', ); ``` diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index 5fda9236b80..d6a9e975fa9 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -36,6 +36,7 @@ topics: # The example deliberately includes limited-use secrets. false_secrets: - - /example/ios/Runner/GoogleService-Info.plist + - /example/ios/Runner/Info.plist + - /example/ios/RunnerTests/GoogleService-Info.plist - /example/ios/RunnerTests/GoogleSignInTests.m - /example/lib/main.dart From f366a3e3393b7e293e3918a8ec6d63c59fcd8797 Mon Sep 17 00:00:00 2001 From: Victoria Ashworth Date: Tue, 7 Nov 2023 09:56:19 -0600 Subject: [PATCH 9/9] fix nits --- .../ios/Classes/FLTGoogleSignInPlugin.h | 2 -- .../ios/Classes/FLTGoogleSignInPlugin.m | 10 +++++----- .../ios/Classes/FLTGoogleSignInPlugin_Test.h | 2 ++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h index fb6ec5a7c72..503b6a4a32e 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h @@ -6,8 +6,6 @@ #import "messages.g.h" -#import - @interface FLTGoogleSignInPlugin : NSObject @end diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index 8e898fada32..2ccc4670c5f 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -109,7 +109,7 @@ - (void)signInSilentlyWithCompletion:(nonnull void (^)(FSIUserData *_Nullable, FlutterError *_Nullable))completion { [self.signIn restorePreviousSignInWithCompletion:^(GIDGoogleUser *_Nullable user, NSError *_Nullable error) { - [self didSignInForUser:user serverAuthCode:nil withCompletion:completion error:error]; + [self didSignInForUser:user withServerAuthCode:nil completion:completion error:error]; }]; } @@ -147,8 +147,8 @@ - (void)signInWithCompletion:(nonnull void (^)(FSIUserData *_Nullable, } [self didSignInForUser:user - serverAuthCode:serverAuthCode - withCompletion:completion + withServerAuthCode:serverAuthCode + completion:completion error:error]; }]; } @catch (NSException *e) { @@ -271,8 +271,8 @@ - (GIDConfiguration *)configurationWithClientIdArgument:(id)clientIDArg } - (void)didSignInForUser:(GIDGoogleUser *)user - serverAuthCode:(NSString *_Nullable)serverAuthCode - withCompletion:(nonnull void (^)(FSIUserData *_Nullable, + withServerAuthCode:(NSString *_Nullable)serverAuthCode + completion:(nonnull void (^)(FSIUserData *_Nullable, FlutterError *_Nullable))completion error:(NSError *)error { if (error != nil) { diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h index 2d8b799437a..fc18c9b9e51 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h @@ -6,6 +6,8 @@ #import +#import + NS_ASSUME_NONNULL_BEGIN @class GIDSignIn;