Forráskód Böngészése

Add a configuration property populated via Info.plist (#228)

Peter Andrews 3 éve
szülő
commit
ae5276cf1f

+ 85 - 38
GoogleSignIn/Sources/GIDSignIn.m

@@ -135,6 +135,12 @@ static NSString *const kHostedDomainParameter = @"hd";
 // Minimum time to expiration for a restored access token.
 static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 
+// Info.plist config keys
+static NSString *const kConfigClientIDKey = @"GIDClientID";
+static NSString *const kConfigServerClientIDKey = @"GIDServerClientID";
+static NSString *const kConfigHostedDomainKey = @"GIDHostedDomain";
+static NSString *const kConfigOpenIDRealmKey = @"GIDOpenIDRealm";
+
 // The callback queue used for authentication flow.
 @interface GIDAuthFlow : GIDCallbackQueue
 
@@ -214,12 +220,11 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                           hint:(nullable NSString *)hint
-                     completion:(nullable GIDSignInCompletion)completion {
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                      hint:(nullable NSString *)hint
+                                completion:(nullable GIDSignInCompletion)completion {
   GIDSignInInternalOptions *options =
-      [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration
+      [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration
                                        presentingViewController:presentingViewController
                                                       loginHint:hint
                                                   addScopesFlow:NO
@@ -227,13 +232,12 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   [self signInWithOptions:options];
 }
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                           hint:(nullable NSString *)hint
-               additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
-                     completion:(nullable GIDSignInCompletion)completion {
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                      hint:(nullable NSString *)hint
+                          additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
+                                completion:(nullable GIDSignInCompletion)completion {
   GIDSignInInternalOptions *options =
-    [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration
+    [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration
                                      presentingViewController:presentingViewController
                                                     loginHint:hint
                                                 addScopesFlow:NO
@@ -242,13 +246,11 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   [self signInWithOptions:options];
 }
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                     completion:(nullable GIDSignInCompletion)completion {
-  [self signInWithConfiguration:configuration
-       presentingViewController:presentingViewController
-                           hint:nil
-                     completion:completion];
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                completion:(nullable GIDSignInCompletion)completion {
+  [self signInWithPresentingViewController:presentingViewController
+                                      hint:nil
+                                completion:completion];
 }
 
 - (void)addScopes:(NSArray<NSString *> *)scopes
@@ -307,12 +309,11 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 
 #elif TARGET_OS_OSX
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                           hint:(nullable NSString *)hint
-                     completion:(nullable GIDSignInCompletion)completion {
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                              hint:(nullable NSString *)hint
+                        completion:(nullable GIDSignInCompletion)completion {
   GIDSignInInternalOptions *options =
-      [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration
+      [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration
                                                presentingWindow:presentingWindow
                                                       loginHint:hint
                                                   addScopesFlow:NO
@@ -320,22 +321,19 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   [self signInWithOptions:options];
 }
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                     completion:(nullable GIDSignInCompletion)completion {
-  [self signInWithConfiguration:configuration
-               presentingWindow:presentingWindow
-                           hint:nil
-                     completion:completion];
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                        completion:(nullable GIDSignInCompletion)completion {
+  [self signInWithPresentingWindow:presentingWindow
+                              hint:nil
+                        completion:completion];
 }
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                           hint:(nullable NSString *)hint
-               additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
-                     completion:(nullable GIDSignInCompletion)completion {
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                              hint:(nullable NSString *)hint
+                  additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
+                        completion:(nullable GIDSignInCompletion)completion {
   GIDSignInInternalOptions *options =
-    [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration
+    [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration
                                              presentingWindow:presentingWindow
                                                     loginHint:hint
                                                 addScopesFlow:NO
@@ -477,6 +475,14 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 - (id)initPrivate {
   self = [super init];
   if (self) {
+    // Get the bundle of the current executable.
+    NSBundle *bundle = NSBundle.mainBundle;
+
+    // If we have a bundle, try to set the active configuration from the bundle's Info.plist.
+    if (bundle) {
+      _configuration = [GIDSignIn configurationFromBundle:bundle];
+    }
+    
     // Check to see if the 3P app is being run for the first time after a fresh install.
     BOOL isFreshInstall = [self isFreshInstall];
 
@@ -514,6 +520,14 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   }
 
   if (options.interactive) {
+    // Ensure that a configuration has been provided.
+    if (!_configuration) {
+      // NOLINTNEXTLINE(google-objc-avoid-throwing-exception)
+      [NSException raise:NSInvalidArgumentException
+                  format:@"No active configuration.  Make sure GIDClientID is set in Info.plist."];
+      return;
+    }
+
     // Explicitly throw exception for missing client ID here. This must come before
     // scheme check because schemes rely on reverse client IDs.
     [self assertValidParameters];
@@ -962,10 +976,11 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
 // Assert that the presenting view controller has been set.
 - (void)assertValidPresentingViewController {
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-  if (!_currentOptions.presentingViewController) {
+  if (!_currentOptions.presentingViewController)
 #elif TARGET_OS_OSX
-  if (!_currentOptions.presentingWindow) {
+  if (!_currentOptions.presentingWindow)
 #endif // TARGET_OS_OSX
+  {
     // NOLINTNEXTLINE(google-objc-avoid-throwing-exception)
     [NSException raise:NSInvalidArgumentException
                 format:@"|presentingViewController| must be set."];
@@ -1027,6 +1042,38 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   [self didChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
 }
 
+// Try to retrieve a configuration value from an |NSBundle|'s Info.plist for a given key.
++ (nullable NSString *)configValueFromBundle:(NSBundle *)bundle forKey:(NSString *)key {
+  NSString *value;
+  id configValue = [bundle objectForInfoDictionaryKey:key];
+  if ([configValue isKindOfClass:[NSString class]]) {
+    value = configValue;
+  }
+  return value;
+}
+
+// Try to generate a |GIDConfiguration| from an |NSBundle|'s Info.plist.
++ (nullable GIDConfiguration *)configurationFromBundle:(NSBundle *)bundle {
+  GIDConfiguration *configuration;
+
+  // Retrieve any valid config parameters from the bundle's Info.plist.
+  NSString *clientID = [GIDSignIn configValueFromBundle:bundle forKey:kConfigClientIDKey];
+  NSString *serverClientID = [GIDSignIn configValueFromBundle:bundle
+                                                       forKey:kConfigServerClientIDKey];
+  NSString *hostedDomain = [GIDSignIn configValueFromBundle:bundle forKey:kConfigHostedDomainKey];
+  NSString *openIDRealm = [GIDSignIn configValueFromBundle:bundle forKey:kConfigOpenIDRealmKey];
+    
+  // If we have at least a client ID, try to construct a configuration.
+  if (clientID) {
+    configuration = [[GIDConfiguration alloc] initWithClientID:clientID
+                                                 serverClientID:serverClientID
+                                                   hostedDomain:hostedDomain
+                                                    openIDRealm:openIDRealm];
+  }
+  
+  return configuration;
+}
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 27 - 36
GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h

@@ -69,6 +69,9 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
 /// The `GIDGoogleUser` object representing the current user or `nil` if there is no signed-in user.
 @property(nonatomic, readonly, nullable) GIDGoogleUser *currentUser;
 
+/// The active configuration for this instance of `GIDSignIn`.
+@property(nonatomic, nullable) GIDConfiguration *configuration;
+
 /// Unavailable. Use the `sharedInstance` property to instantiate `GIDSignIn`.
 /// :nodoc:
 + (instancetype)new NS_UNAVAILABLE;
@@ -106,32 +109,29 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
 - (void)disconnectWithCompletion:(nullable GIDDisconnectCompletion)completion;
 
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-/// Starts an interactive sign-in flow on iOS using the provided configuration.
+/// Starts an interactive sign-in flow on iOS.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingViewController The view controller used to present `SFSafariViewContoller` on
 ///     iOS 9 and 10 and to supply `presentationContextProvider` for `ASWebAuthenticationSession` on
 ///     iOS 13+.
 /// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
 ///     be called asynchronously on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                     completion:(nullable GIDSignInCompletion)completion
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                completion:(nullable GIDSignInCompletion)completion
     NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions.");
 
-/// Starts an interactive sign-in flow on iOS using the provided configuration and a login hint.
+/// Starts an interactive sign-in flow on iOS using the provided hint.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingViewController The view controller used to present `SFSafariViewContoller` on
 ///     iOS 9 and 10 and to supply `presentationContextProvider` for `ASWebAuthenticationSession` on
 ///     iOS 13+.
@@ -139,20 +139,18 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
 ///     address, to be prefilled if possible.
 /// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
 ///     be called asynchronously on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                           hint:(nullable NSString *)hint
-                     completion:(nullable GIDSignInCompletion)completion
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                      hint:(nullable NSString *)hint
+                                completion:(nullable GIDSignInCompletion)completion
     NS_EXTENSION_UNAVAILABLE("The sign-in flow is not supported in App Extensions.");
 
-/// Starts an interactive sign-in flow on iOS using the provided configuration and a login hint.
+/// Starts an interactive sign-in flow on iOS using the provided hint and additional scopes.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingViewController The view controller used to present `SFSafariViewContoller` on
 ///     iOS 9 and 10.
 /// @param hint An optional hint for the authorization server, for example the user's ID or email
@@ -161,11 +159,10 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
 /// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
 ///     be called asynchronously on the main queue.
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-       presentingViewController:(UIViewController *)presentingViewController
-                           hint:(nullable NSString *)hint
-               additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
-                     completion:(nullable GIDSignInCompletion)completion;
+- (void)signInWithPresentingViewController:(UIViewController *)presentingViewController
+                                      hint:(nullable NSString *)hint
+                          additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
+                                completion:(nullable GIDSignInCompletion)completion;
 
 /// Starts an interactive consent flow on iOS to add scopes to the current user's grants.
 ///
@@ -184,47 +181,42 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
     NS_EXTENSION_UNAVAILABLE("The add scopes flow is not supported in App Extensions."); 
 
 #elif TARGET_OS_OSX
-/// Starts an interactive sign-in flow on macOS using the provided configuration.
+/// Starts an interactive sign-in flow on macOS.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`.
 /// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
 ///     be called asynchronously on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                     completion:(nullable GIDSignInCompletion)completion;
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                        completion:(nullable GIDSignInCompletion)completion;
 
-/// Starts an interactive sign-in flow on macOS using the provided configuration and a login hint.
+/// Starts an interactive sign-in flow on macOS using the provided hint.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`.
 /// @param hint An optional hint for the authorization server, for example the user's ID or email
 ///     address, to be prefilled if possible.
 /// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
 ///     be called asynchronously on the main queue.
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                           hint:(nullable NSString *)hint
-                     completion:(nullable GIDSignInCompletion)completion;
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                              hint:(nullable NSString *)hint
+                        completion:(nullable GIDSignInCompletion)completion;
 
-/// Starts an interactive sign-in flow on macOS using the provided configuration and a login hint.
+/// Starts an interactive sign-in flow on macOS using the provided hint.
 ///
 /// The completion will be called at the end of this process.  Any saved sign-in state will be
 /// replaced by the result of this flow.  Note that this method should not be called when the app is
 /// starting up, (e.g in `application:didFinishLaunchingWithOptions:`); instead use the
 /// `restorePreviousSignInWithCompletion:` method to restore a previous sign-in.
 ///
-/// @param configuration The configuration properties to be used for this flow.
 /// @param presentingWindow The window used to supply `presentationContextProvider` for `ASWebAuthenticationSession`.
 /// @param hint An optional hint for the authorization server, for example the user's ID or email
 ///     address, to be prefilled if possible.
@@ -232,11 +224,10 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error);
 /// @param completion The `GIDSignInCompletion` block that is called on completion.  This block will
 ///     be called asynchronously on the main queue.
 
-- (void)signInWithConfiguration:(GIDConfiguration *)configuration
-               presentingWindow:(NSWindow *)presentingWindow
-                           hint:(nullable NSString *)hint
-               additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
-                     completion:(nullable GIDSignInCompletion)completion;
+- (void)signInWithPresentingWindow:(NSWindow *)presentingWindow
+                              hint:(nullable NSString *)hint
+                  additionalScopes:(nullable NSArray<NSString *> *)additionalScopes
+                        completion:(nullable GIDSignInCompletion)completion;
 
 /// Starts an interactive consent flow on macOS to add scopes to the current user's grants.
 ///

+ 16 - 4
GoogleSignIn/Tests/Unit/GIDFakeMainBundle.h

@@ -23,12 +23,11 @@
 @interface GIDFakeMainBundle : NSObject
 
 /**
- * @fn startFakingWithBundleId:clientId:
+ * @fn startFakingWithClientID:
  * @brief Starts faking [NSBundle mainBundle]
- * @param bundleId The fake bundle idenfitier for the app.
- * @param clientId The fake client idenfitier for the app.
+ * @param clientID The fake client idenfitier for the app.
  */
-- (void)startFakingWithBundleId:(NSString *)bundleId clientId:(NSString *)clientId;
+- (void)startFakingWithClientID:(NSString *)clientID;
 
 /**
  * @fn stopFaking
@@ -80,4 +79,17 @@
  */
 - (void)fakeOtherSchemesAndAllSchemes;
 
+/**
+ * @fn fakeWithClientID:serverClientID:hostedDomain:openIDRealm:
+ * @brief Sets values for faked Info.plist params.
+ * @param clientID The fake client idenfitier for the app.
+ * @param serverClientID The fake server client idenfitier for the app.
+ * @param hostedDomain The fake hosted domain for the app.
+ * @param openIDRealm The fake OpenID realm for the app.
+ */
+- (void)fakeWithClientID:(id)clientID
+          serverClientID:(id)serverClientID
+            hostedDomain:(id)hostedDomain
+             openIDRealm:(id)openIDRealm;
+
 @end

+ 42 - 33
GoogleSignIn/Tests/Unit/GIDFakeMainBundle.m

@@ -21,24 +21,39 @@ static NSString *const kCFBundleURLTypesKey = @"CFBundleURLTypes";
 
 static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 
-@implementation GIDFakeMainBundle {
-  // Represents the CFBundleURLTypes of the mocked app bundle's info.plist.
-  __block NSArray *_fakeSupportedSchemes;
+// Info.plist config keys
+static NSString *const kConfigClientIDKey = @"GIDClientID";
+static NSString *const kConfigServerClientIDKey = @"GIDServerClientID";
+static NSString *const kConfigHostedDomainKey = @"GIDHostedDomain";
+static NSString *const kConfigOpenIDRealmKey = @"GIDOpenIDRealm";
 
+@implementation GIDFakeMainBundle {
   NSString *_clientId;
-  NSString *_bundleId;
+
+  // Represents the Info.plist keys to fake.
+  NSArray *_fakedKeys;
+
+  // Represents the values for any Info.plist keys to be faked.
+  NSMutableDictionary *_fakeConfig;
 }
 
-- (void)startFakingWithBundleId:(NSString *)bundleId clientId:(NSString *)clientId {
-  _bundleId = bundleId;
+- (void)startFakingWithClientID:(NSString *)clientId {
   _clientId = clientId;
 
+  _fakedKeys = @[ kCFBundleURLTypesKey,
+                  kConfigClientIDKey,
+                  kConfigServerClientIDKey,
+                  kConfigHostedDomainKey,
+                  kConfigOpenIDRealmKey ];
+  
+  _fakeConfig = [@{ @"GIDClientID" : clientId } mutableCopy];
+
   [GULSwizzler swizzleClass:[NSBundle class]
                    selector:@selector(objectForInfoDictionaryKey:)
             isClassSelector:NO
-                  withBlock:^(id _self, NSString *key) {
-    if ([key isEqual:kCFBundleURLTypesKey]) {
-      return self->_fakeSupportedSchemes;
+                  withBlock:^id(id _self, NSString *key) {
+    if ([self->_fakedKeys containsObject:key]) {
+      return self->_fakeConfig[key];
     } else {
       @throw [NSException exceptionWithName:@"Requested unexpected info.plist key."
                                      reason:nil
@@ -51,7 +66,7 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
   [GULSwizzler unswizzleClass:[NSBundle class]
                      selector:@selector(objectForInfoDictionaryKey:)
               isClassSelector:NO];
-  _fakeSupportedSchemes = nil;
+  _fakeConfig = nil;
 }
 
 #pragma mark - Utilities
@@ -93,10 +108,7 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 #pragma mark - URL Schemes
 
 - (void)fakeAllSchemesSupported {
-  _fakeSupportedSchemes = @[
-    @{
-      kCFBundleURLSchemesKey : @[ _bundleId ]
-    },
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[ [self reversedClientId] ]
     }
@@ -104,10 +116,9 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 }
 
 - (void)fakeAllSchemesSupportedAndMerged {
-  _fakeSupportedSchemes = @[
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[
-        _bundleId,
         [self reversedClientId]
       ]
     },
@@ -115,14 +126,9 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 }
 
 - (void)fakeAllSchemesSupportedWithCasesMangled {
-  NSString *caseFlippedBundleId =
-      [self stringByFlippingCasesInString:_bundleId];
   NSString *caseFlippedReverseClientId =
       [self stringByFlippingCasesInString:[self reversedClientId]];
-  _fakeSupportedSchemes = @[
-    @{
-      kCFBundleURLSchemesKey : @[ caseFlippedBundleId ]
-    },
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[ caseFlippedReverseClientId ]
     }
@@ -130,19 +136,15 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 }
 
 - (void)fakeMissingClientIdScheme {
-  _fakeSupportedSchemes = @[
-    @{
-      kCFBundleURLSchemesKey : @[ _bundleId ]
-    }
-  ];
+  [self fakeMissingAllSchemes];
 }
 
 - (void)fakeMissingAllSchemes {
-  _fakeSupportedSchemes = nil;
+  _fakeConfig[kCFBundleURLTypesKey] = nil;
 }
 
 - (void)fakeOtherSchemes {
-  _fakeSupportedSchemes = @[
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[ @"junk" ]
     }
@@ -150,10 +152,7 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
 }
 
 - (void)fakeOtherSchemesAndAllSchemes {
-  _fakeSupportedSchemes = @[
-    @{
-      kCFBundleURLSchemesKey : @[ _bundleId ]
-    },
+  _fakeConfig[kCFBundleURLTypesKey] = @[
     @{
       kCFBundleURLSchemesKey : @[ @"junk" ]
     },
@@ -163,4 +162,14 @@ static NSString *const kCFBundleURLSchemesKey = @"CFBundleURLSchemes";
   ];
 }
 
+- (void)fakeWithClientID:(id)clientID
+          serverClientID:(id)serverClientID
+            hostedDomain:(id)hostedDomain
+             openIDRealm:(id)openIDRealm {
+  _fakeConfig[kConfigClientIDKey] = clientID;
+  _fakeConfig[kConfigServerClientIDKey] = serverClientID;
+  _fakeConfig[kConfigHostedDomainKey] = hostedDomain;
+  _fakeConfig[kConfigOpenIDRealmKey] = openIDRealm;
+}
+
 @end

+ 1 - 2
GoogleSignIn/Tests/Unit/GIDSignInCallbackSchemesTest.m

@@ -18,7 +18,6 @@
 #import "GoogleSignIn/Tests/Unit/GIDFakeMainBundle.h"
 
 static NSString *const kClientId = @"FakeClientID";
-static NSString *const kBundleId = @"FakeBundleID";
 
 @interface GIDSignInCallbackSchemesTest : XCTestCase
 @end
@@ -29,7 +28,7 @@ static NSString *const kBundleId = @"FakeBundleID";
 
 - (void)setUp {
   _fakeMainBundle = [[GIDFakeMainBundle alloc] init];
-  [_fakeMainBundle startFakingWithBundleId:kBundleId clientId:kClientId];
+  [_fakeMainBundle startFakingWithClientID:kClientId];
 }
 
 - (void)tearDown {

+ 71 - 37
GoogleSignIn/Tests/Unit/GIDSignInTest.m

@@ -85,7 +85,7 @@ static NSString * const kFakeIDToken = @"FakeIDToken";
 static NSString * const kClientId = @"FakeClientID";
 static NSString * const kDotReversedClientId = @"FakeClientID";
 static NSString * const kClientId2 = @"FakeClientID2";
-static NSString * const kAppBundleId = @"FakeBundleID";
+static NSString * const kServerClientId = @"FakeServerClientID";
 static NSString * const kLanguage = @"FakeLanguage";
 static NSString * const kScope = @"FakeScope";
 static NSString * const kScope2 = @"FakeScope2";
@@ -334,7 +334,7 @@ static void *kTestObserverContext = &kTestObserverContext;
   // Fakes
   _fetcherService = [[GIDFakeFetcherService alloc] init];
   _fakeMainBundle = [[GIDFakeMainBundle alloc] init];
-  [_fakeMainBundle startFakingWithBundleId:kAppBundleId clientId:kClientId];
+  [_fakeMainBundle startFakingWithClientID:kClientId];
   [_fakeMainBundle fakeAllSchemesSupported];
 
   // Object under test
@@ -342,7 +342,6 @@ static void *kTestObserverContext = &kTestObserverContext;
                                           forKey:kAppHasRunBeforeKey];
 
   _signIn = [[GIDSignIn alloc] initPrivate];
-  _configuration = [[GIDConfiguration alloc] initWithClientID:kClientId];
   _hint = nil;
 
   __weak GIDSignInTest *weakSelf = self;
@@ -394,6 +393,46 @@ static void *kTestObserverContext = &kTestObserverContext;
   XCTAssertTrue(signIn1 == signIn2, @"shared instance must be singleton");
 }
 
+- (void)testInitPrivate {
+  GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate];
+  XCTAssertNotNil(signIn.configuration);
+  XCTAssertEqual(signIn.configuration.clientID, kClientId);
+  XCTAssertNil(signIn.configuration.serverClientID);
+  XCTAssertNil(signIn.configuration.hostedDomain);
+  XCTAssertNil(signIn.configuration.openIDRealm);
+}
+
+- (void)testInitPrivate_noConfig {
+  [_fakeMainBundle fakeWithClientID:nil
+                     serverClientID:nil
+                       hostedDomain:nil
+                        openIDRealm:nil];
+  GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate];
+  XCTAssertNil(signIn.configuration);
+}
+
+- (void)testInitPrivate_fullConfig {
+  [_fakeMainBundle fakeWithClientID:kClientId
+                     serverClientID:kServerClientId
+                       hostedDomain:kFakeHostedDomain
+                        openIDRealm:kOpenIDRealm];
+  GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate];
+  XCTAssertNotNil(signIn.configuration);
+  XCTAssertEqual(signIn.configuration.clientID, kClientId);
+  XCTAssertEqual(signIn.configuration.serverClientID, kServerClientId);
+  XCTAssertEqual(signIn.configuration.hostedDomain, kFakeHostedDomain);
+  XCTAssertEqual(signIn.configuration.openIDRealm, kOpenIDRealm);
+}
+
+- (void)testInitPrivate_invalidConfig {
+  [_fakeMainBundle fakeWithClientID:@[ @"bad", @"config", @"values" ]
+                     serverClientID:nil
+                       hostedDomain:nil
+                        openIDRealm:nil];
+  GIDSignIn *signIn = [[GIDSignIn alloc] initPrivate];
+  XCTAssertNil(signIn.configuration);
+}
+
 - (void)testRestorePreviousSignInNoRefresh_hasPreviousUser {
   [[[_authorization expect] andReturn:_authState] authState];
   OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
@@ -607,10 +646,10 @@ static void *kTestObserverContext = &kTestObserverContext;
 }
 
 - (void)testOpenIDRealm {
-  _configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
-                                               serverClientID:nil
-                                                 hostedDomain:nil
-                                                  openIDRealm:kOpenIDRealm];
+  _signIn.configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
+                                                      serverClientID:nil
+                                                        hostedDomain:nil
+                                                         openIDRealm:kOpenIDRealm];
 
   [self OAuthLoginWithAddScopesFlow:NO
                           authError:nil
@@ -642,10 +681,10 @@ static void *kTestObserverContext = &kTestObserverContext;
 }
 
 - (void)testOAuthLogin_HostedDomain {
-  _configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
-                                               serverClientID:nil
-                                                 hostedDomain:kHostedDomain
-                                                  openIDRealm:nil];
+  _signIn.configuration = [[GIDConfiguration alloc] initWithClientID:kClientId
+                                                      serverClientID:nil
+                                                        hostedDomain:kHostedDomain
+                                                         openIDRealm:nil];
 
   [self OAuthLoginWithAddScopesFlow:NO
                           authError:nil
@@ -880,30 +919,28 @@ static void *kTestObserverContext = &kTestObserverContext;
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
 
 
-  XCTAssertThrows([_signIn signInWithConfiguration:_configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                          presentingViewController:_presentingViewController
+  XCTAssertThrows([_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                                  presentingWindow:_presentingWindow
+  XCTAssertThrows([_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                                              hint:_hint
-                                        completion:_completion]);
+                                                 hint:_hint
+                                           completion:_completion]);
 }
 
 - (void)testClientIDMissingException {
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wnonnull"
-  GIDConfiguration *configuration = [[GIDConfiguration alloc] initWithClientID:nil];
+  _signIn.configuration = [[GIDConfiguration alloc] initWithClientID:nil];
 #pragma GCC diagnostic pop
   BOOL threw = NO;
   @try {
-    [_signIn signInWithConfiguration:configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-            presentingViewController:_presentingViewController
+    [_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                    presentingWindow:_presentingWindow
+    [_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                          completion:nil];
+                             completion:nil];
   } @catch (NSException *exception) {
     threw = YES;
     XCTAssertEqualObjects(exception.description,
@@ -917,14 +954,13 @@ static void *kTestObserverContext = &kTestObserverContext;
   [_fakeMainBundle fakeMissingAllSchemes];
   BOOL threw = NO;
   @try {
-    [_signIn signInWithConfiguration:_configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-            presentingViewController:_presentingViewController
+    [_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                    presentingWindow:_presentingWindow
+    [_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                                hint:_hint
-                          completion:_completion];
+                                   hint:_hint
+                             completion:_completion];
   } @catch (NSException *exception) {
     threw = YES;
     XCTAssertEqualObjects(exception.description,
@@ -1239,24 +1275,22 @@ static void *kTestObserverContext = &kTestObserverContext;
               completion:completion];
     } else {
       if (useAdditionalScopes) {
-        [_signIn signInWithConfiguration:_configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                presentingViewController:_presentingViewController
+        [_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                        presentingWindow:_presentingWindow
+        [_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                                    hint:_hint
-                        additionalScopes:additionalScopes
-                              completion:completion];
+                                       hint:_hint
+                           additionalScopes:additionalScopes
+                                 completion:completion];
       } else {
-        [_signIn signInWithConfiguration:_configuration
 #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                presentingViewController:_presentingViewController
+        [_signIn signInWithPresentingViewController:_presentingViewController
 #elif TARGET_OS_OSX
-                        presentingWindow:_presentingWindow
+        [_signIn signInWithPresentingWindow:_presentingWindow
 #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
-                                    hint:_hint
-                              completion:completion];
+                                       hint:_hint
+                                 completion:completion];
       }
     }
 

+ 3 - 0
Samples/ObjC/SignInSample/SignInSample-Info.plist

@@ -67,5 +67,8 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
+  <!-- Do not use this client ID in your own app, it will only work for this sample. -->
+  <key>GIDClientID</key>
+  <string>589453917038-qaoga89fitj2ukrsq27ko56fimmojac6.apps.googleusercontent.com</string>
 </dict>
 </plist>

+ 3 - 13
Samples/ObjC/SignInSample/Source/SignInViewController.m

@@ -37,11 +37,6 @@ static NSString *const kStyleCellLabel = @"Style";
 // Accessibility Identifiers.
 static NSString *const kCredentialsButtonAccessibilityIdentifier = @"Credentials";
 
-// DO NOT USE THIS CLIENT ID. IT WILL NOT WORK FOR YOUR APP.
-// Please use the client ID created for you by Google.
-static NSString * const kClientID =
-    @"589453917038-qaoga89fitj2ukrsq27ko56fimmojac6.apps.googleusercontent.com";
-
 @implementation SignInViewController {
   // This is an array of arrays, each one corresponding to the cell
   // labels for its respective section.
@@ -58,9 +53,6 @@ static NSString * const kClientID =
 
   // Map that keeps track of which cell corresponds to which DataPickerState.
   NSDictionary *_drilldownCellState;
-
-  // Configuration options for GIDSignIn.
-  GIDConfiguration *_configuration;
 }
 
 #pragma mark - View lifecycle
@@ -97,8 +89,6 @@ static NSString * const kClientID =
   // Make sure the GIDSignInButton class is linked in because references from
   // xib file doesn't count.
   [GIDSignInButton class];
-
-  _configuration = [[GIDConfiguration alloc] initWithClientID:kClientID];
 }
 
 - (id)initWithNibName:(NSString *)nibNameOrNil
@@ -257,9 +247,9 @@ static NSString * const kClientID =
 #pragma mark - IBActions
 
 - (IBAction)signIn:(id)sender {
-  [GIDSignIn.sharedInstance signInWithConfiguration:_configuration
-                           presentingViewController:self
-                                         completion:^(GIDGoogleUser *user, NSError *error) {
+  [GIDSignIn.sharedInstance signInWithPresentingViewController:self
+                                                    completion:^(GIDGoogleUser *user,
+                                                                 NSError *error) {
     if (error) {
       self->_signInAuthStatus.text =
           [NSString stringWithFormat:@"Status: Authentication error: %@", error];

+ 15 - 4
Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj/project.pbxproj

@@ -553,8 +553,10 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
-				CODE_SIGN_STYLE = Automatic;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Manual;
 				DEVELOPMENT_ASSET_PATHS = "\"iOS/Preview Content\"";
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = iOS/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -564,6 +566,7 @@
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthday;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "Google Development";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -574,8 +577,10 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
-				CODE_SIGN_STYLE = Automatic;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				CODE_SIGN_STYLE = Manual;
 				DEVELOPMENT_ASSET_PATHS = "\"iOS/Preview Content\"";
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
 				ENABLE_PREVIEWS = YES;
 				INFOPLIST_FILE = iOS/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -585,6 +590,7 @@
 				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.google.DaysUntilBirthday;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "Google Development";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -652,10 +658,12 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CODE_SIGN_ENTITLEMENTS = macOS/DaysUntilBirthdayOnMac.entitlements;
-				CODE_SIGN_STYLE = Automatic;
+				CODE_SIGN_IDENTITY = "Mac Developer";
+				CODE_SIGN_STYLE = Manual;
 				COMBINE_HIDPI_IMAGES = YES;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_ASSET_PATHS = "\"macOS/Preview Content\"";
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = macOS/Info.plist;
@@ -668,6 +676,7 @@
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = Google.DaysUntilBirthdayOnMac;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "Google Development macOS";
 				SDKROOT = macosx;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;
@@ -681,10 +690,11 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CODE_SIGN_ENTITLEMENTS = macOS/DaysUntilBirthdayOnMac.entitlements;
-				CODE_SIGN_STYLE = Automatic;
+				CODE_SIGN_STYLE = Manual;
 				COMBINE_HIDPI_IMAGES = YES;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_ASSET_PATHS = "\"macOS/Preview Content\"";
+				DEVELOPMENT_TEAM = EQHXZ8M8AV;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = macOS/Info.plist;
@@ -697,6 +707,7 @@
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = Google.DaysUntilBirthdayOnMac;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PROVISIONING_PROFILE_SPECIFIER = "Google Development macOS";
 				SDKROOT = macosx;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_VERSION = 5.0;

+ 2 - 15
Samples/Swift/DaysUntilBirthday/Shared/Services/GoogleSignInAuthenticator.swift

@@ -19,17 +19,6 @@ import GoogleSignIn
 
 /// An observable class for authenticating via Google.
 final class GoogleSignInAuthenticator: ObservableObject {
-  // TODO: Replace this with your own ID.
-  #if os(iOS)
-  private let clientID = "687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com"
-  #elseif os(macOS)
-  private let clientID = "687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com"
-  #endif
-
-  private lazy var configuration: GIDConfiguration = {
-    return GIDConfiguration(clientID: clientID)
-  }()
-
   private var authViewModel: AuthenticationViewModel
 
   /// Creates an instance of this authenticator.
@@ -47,8 +36,7 @@ final class GoogleSignInAuthenticator: ObservableObject {
       return
     }
 
-    GIDSignIn.sharedInstance.signIn(with: configuration,
-                                    presenting: rootViewController) { user, error in
+    GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) { user, error in
       guard let user = user else {
         print("Error! \(String(describing: error))")
         return
@@ -62,8 +50,7 @@ final class GoogleSignInAuthenticator: ObservableObject {
       return
     }
 
-    GIDSignIn.sharedInstance.signIn(with: configuration,
-                                    presenting: presentingWindow) { user, error in
+    GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) { user, error in
       guard let user = user else {
         print("Error! \(String(describing: error))")
         return

+ 3 - 0
Samples/Swift/DaysUntilBirthday/iOS/Info.plist

@@ -61,5 +61,8 @@
 		<string>UIInterfaceOrientationLandscapeLeft</string>
 		<string>UIInterfaceOrientationLandscapeRight</string>
 	</array>
+  <!-- Do not use this client ID in your own app, it will only work for this sample. -->
+  <key>GIDClientID</key>
+  <string>687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com</string>
 </dict>
 </plist>

+ 3 - 0
Samples/Swift/DaysUntilBirthday/macOS/Info.plist

@@ -15,5 +15,8 @@
 			</array>
 		</dict>
 	</array>
+  <!-- Do not use this client ID in your own app, it will only work for this sample. -->
+  <key>GIDClientID</key>
+  <string>687389107077-8qr6dh8fr4uaja89sdr5ieqb7mep04qv.apps.googleusercontent.com</string>
 </dict>
 </plist>