Przeglądaj źródła

Modify the private method `restoredGoogleUserFromPreviousSignIn`. (#45)

- Rename it to `restorePreviousSignInNoRefresh`.
- In this method restores the previous sign-in user in the keychain and set it as the current account without refreshing the access token.
pinlu 4 lat temu
rodzic
commit
c90a734cbf

+ 84 - 59
GoogleSignIn/Sources/GIDSignIn.m

@@ -180,6 +180,27 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   [self signInWithOptions:[GIDSignInInternalOptions silentOptionsWithCallback:callback]];
 }
 
+- (BOOL)restorePreviousSignInNoRefresh {
+  if (_currentUser) {
+    return YES;
+  }
+
+  // Try retrieving an authorization object from the keychain.
+  OIDAuthState *authState = [self loadAuthState];
+  if (!authState) {
+    return NO;
+  }
+
+  // Restore current user without refreshing the access token.
+  OIDIDToken *idToken =
+      [[OIDIDToken alloc] initWithIDTokenString:authState.lastTokenResponse.idToken];
+  GIDProfileData *profileData = [self profileDataWithIDToken:idToken];
+
+  GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState profileData:profileData];
+  [self setCurrentUserWithKVO:user];
+  return YES;
+}
+
 - (void)signInWithConfiguration:(GIDConfiguration *)configuration
        presentingViewController:(UIViewController *)presentingViewController
                            hint:(nullable NSString *)hint
@@ -201,7 +222,6 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
                        callback:callback];
 }
 
-
 - (void)addScopes:(NSArray<NSString *> *)scopes
     presentingViewController:(UIViewController *)presentingViewController
                     callback:(nullable GIDSignInCallback)callback {
@@ -402,18 +422,7 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   }
 }
 
-- (nullable GIDGoogleUser *)restoredGoogleUserFromPreviousSignIn {
-  OIDAuthState *authState = [self loadAuthState];
-
-  if (!authState) {
-    return nil;
-  }
-
-  return [[GIDGoogleUser alloc] initWithAuthState:authState
-                                      profileData:nil];
-}
-
-# pragma mark - Authentication flow
+#pragma mark - Authentication flow
 
 - (void)authenticateInteractivelyWithOptions:(GIDSignInInternalOptions *)options {
   GIDSignInCallbackSchemes *schemes =
@@ -616,10 +625,9 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
                                                  code:kGIDSignInErrorCodeKeychain];
         return;
       }
-      [self willChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
-      self->_currentUser = [[GIDGoogleUser alloc] initWithAuthState:authState
-                                                        profileData:handlerAuthFlow.profileData];
-      [self didChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
+      GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState
+                                                         profileData:handlerAuthFlow.profileData];
+      [self setCurrentUserWithKVO:user];
     }
   }];
 }
@@ -636,50 +644,42 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
     }
     OIDIDToken *idToken =
         [[OIDIDToken alloc] initWithIDTokenString:authState.lastTokenResponse.idToken];
+    // If the profile data are present in the ID token, use them.
     if (idToken) {
-      // If the picture and name fields are present in the ID token, use them, otherwise make
-      // a userinfo request to fetch them.
-      if (idToken.claims[kBasicProfilePictureKey] &&
-          idToken.claims[kBasicProfileNameKey] &&
-          idToken.claims[kBasicProfileGivenNameKey] &&
-          idToken.claims[kBasicProfileFamilyNameKey]) {
-        handlerAuthFlow.profileData = [[GIDProfileData alloc]
-            initWithEmail:idToken.claims[kBasicProfileEmailKey]
-                     name:idToken.claims[kBasicProfileNameKey]
-                givenName:idToken.claims[kBasicProfileGivenNameKey]
-               familyName:idToken.claims[kBasicProfileFamilyNameKey]
-                 imageURL:[NSURL URLWithString:idToken.claims[kBasicProfilePictureKey]]];
-      } else {
-        [handlerAuthFlow wait];
-        NSURL *infoURL = [NSURL URLWithString:
-            [NSString stringWithFormat:kUserInfoURLTemplate,
-                [GIDSignInPreferences googleUserInfoServer],
-                authState.lastTokenResponse.accessToken]];
-        [self startFetchURL:infoURL
-                    fromAuthState:authState
-                      withComment:@"GIDSignIn: fetch basic profile info"
-            withCompletionHandler:^(NSData *data, NSError *error) {
-          if (data && !error) {
-            NSError *jsonDeserializationError;
-            NSDictionary<NSString *, NSString *> *profileDict =
-                [NSJSONSerialization JSONObjectWithData:data
-                                                options:NSJSONReadingMutableContainers
-                                                  error:&jsonDeserializationError];
-            if (profileDict) {
-              handlerAuthFlow.profileData = [[GIDProfileData alloc]
-                  initWithEmail:idToken.claims[kBasicProfileEmailKey]
-                           name:profileDict[kBasicProfileNameKey]
-                      givenName:profileDict[kBasicProfileGivenNameKey]
-                     familyName:profileDict[kBasicProfileFamilyNameKey]
-                       imageURL:[NSURL URLWithString:profileDict[kBasicProfilePictureKey]]];
-            }
-          }
-          if (error) {
-            handlerAuthFlow.error = error;
+      handlerAuthFlow.profileData = [self profileDataWithIDToken:idToken];
+    }
+    
+    // If we can't retrieve profile data from the ID token, make a userInfo request to fetch them.
+    if (!handlerAuthFlow.profileData) {
+      [handlerAuthFlow wait];
+      NSURL *infoURL = [NSURL URLWithString:
+          [NSString stringWithFormat:kUserInfoURLTemplate,
+              [GIDSignInPreferences googleUserInfoServer],
+              authState.lastTokenResponse.accessToken]];
+      [self startFetchURL:infoURL
+                  fromAuthState:authState
+                    withComment:@"GIDSignIn: fetch basic profile info"
+          withCompletionHandler:^(NSData *data, NSError *error) {
+        if (data && !error) {
+          NSError *jsonDeserializationError;
+          NSDictionary<NSString *, NSString *> *profileDict =
+              [NSJSONSerialization JSONObjectWithData:data
+                                              options:NSJSONReadingMutableContainers
+                                                error:&jsonDeserializationError];
+          if (profileDict) {
+            handlerAuthFlow.profileData = [[GIDProfileData alloc]
+                initWithEmail:idToken.claims[kBasicProfileEmailKey]
+                          name:profileDict[kBasicProfileNameKey]
+                    givenName:profileDict[kBasicProfileGivenNameKey]
+                    familyName:profileDict[kBasicProfileFamilyNameKey]
+                      imageURL:[NSURL URLWithString:profileDict[kBasicProfilePictureKey]]];
           }
-          [handlerAuthFlow next];
-        }];
-      }
+        }
+        if (error) {
+          handlerAuthFlow.error = error;
+        }
+        [handlerAuthFlow next];
+      }];
     }
   }];
 }
@@ -827,6 +827,31 @@ static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0;
   return authorization.authState;
 }
 
+// Generates user profile from OIDIDToken.
+- (GIDProfileData *)profileDataWithIDToken:(OIDIDToken *)idToken {
+  if (!idToken ||
+      !idToken.claims[kBasicProfilePictureKey] ||
+      !idToken.claims[kBasicProfileNameKey] || 
+      !idToken.claims[kBasicProfileGivenNameKey] ||
+      !idToken.claims[kBasicProfileFamilyNameKey]) {
+    return nil;
+  } 
+
+  return [[GIDProfileData alloc]
+      initWithEmail:idToken.claims[kBasicProfileEmailKey]
+               name:idToken.claims[kBasicProfileNameKey]
+          givenName:idToken.claims[kBasicProfileGivenNameKey]
+          familyName:idToken.claims[kBasicProfileFamilyNameKey]
+            imageURL:[NSURL URLWithString:idToken.claims[kBasicProfilePictureKey]]];
+}
+
+// Set currentUser making appropriate KVO calls.
+- (void)setCurrentUserWithKVO:(GIDGoogleUser *_Nullable)user {
+  [self willChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
+  self->_currentUser = user;
+  [self didChangeValueForKey:NSStringFromSelector(@selector(currentUser))];
+}
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 2 - 1
GoogleSignIn/Sources/GIDSignInInternalOptions.h

@@ -51,7 +51,8 @@ NS_ASSUME_NONNULL_BEGIN
 
 /// Creates the default options.
 + (instancetype)defaultOptionsWithConfiguration:(nullable GIDConfiguration *)configuration
-                       presentingViewController:(nullable UIViewController *)presentingViewController
+                       presentingViewController:
+                           (nullable UIViewController *)presentingViewController
                                       loginHint:(nullable NSString *)loginHint
                                        callback:(GIDSignInCallback)callback;
 

+ 2 - 1
GoogleSignIn/Sources/GIDSignInInternalOptions.m

@@ -21,7 +21,8 @@ NS_ASSUME_NONNULL_BEGIN
 @implementation GIDSignInInternalOptions
 
 + (instancetype)defaultOptionsWithConfiguration:(nullable GIDConfiguration *)configuration
-                       presentingViewController:(nullable UIViewController *)presentingViewController
+                       presentingViewController:
+                           (nullable UIViewController *)presentingViewController
                                       loginHint:(nullable NSString *)loginHint
                                        callback:(GIDSignInCallback)callback {
   GIDSignInInternalOptions *options = [[GIDSignInInternalOptions alloc] init];

+ 6 - 2
GoogleSignIn/Sources/GIDSignIn_Private.h

@@ -30,8 +30,12 @@ NS_ASSUME_NONNULL_BEGIN
 // Authenticates with extra options.
 - (void)signInWithOptions:(GIDSignInInternalOptions *)options;
 
-// Returns the previous sign-in user in the keychain without refreshing the access token.
-- (nullable GIDGoogleUser *)restoredGoogleUserFromPreviousSignIn;
+// Restores a previously authenticated user from the keychain synchronously without refreshing
+// the access token or making a userinfo request. The currentUser.profile will be nil unless
+// the profile data can be extracted from the ID token.
+//
+// @return NO if there is no user restored from the keychain.
+- (BOOL)restorePreviousSignInNoRefresh;
 
 @end
 

+ 6 - 6
GoogleSignIn/Tests/Unit/GIDSignInTest.m

@@ -353,7 +353,7 @@ static void *kTestObserverContext = &kTestObserverContext;
   XCTAssertTrue(signIn1 == signIn2, @"shared instance must be singleton");
 }
 
-- (void)testRestoredGoogleUserFromPreviousSignIn_hasPreviousUser {
+- (void)testRestorePreviousSignInNoRefresh_hasPreviousUser {
   [[[_authorization expect] andReturn:_authState] authState];
   OCMStub([_authState lastTokenResponse]).andReturn(_tokenResponse);
   OCMStub([_tokenResponse scope]).andReturn(nil);
@@ -367,21 +367,21 @@ static void *kTestObserverContext = &kTestObserverContext;
   OCMStub([idTokenDecoded initWithIDTokenString:OCMOCK_ANY]).andReturn(idTokenDecoded);
   OCMStub([idTokenDecoded subject]).andReturn(kFakeGaiaID);
 
-  GIDGoogleUser *previousUser = [_signIn restoredGoogleUserFromPreviousSignIn];
+  [_signIn restorePreviousSignInNoRefresh];
 
   [_authorization verify];
   [_authState verify];
   [_tokenResponse verify];
-  XCTAssertEqual(previousUser.userID, kFakeGaiaID);
+  XCTAssertEqual(_signIn.currentUser.userID, kFakeGaiaID);
 }
 
-- (void)testRestoredGoogleUserFromPreviousSignIn_hasNoPreviousUser {
+- (void)testRestoredPreviousSignInNoRefresh_hasNoPreviousUser {
   [[[_authorization expect] andReturn:nil] authState];
 
-  GIDGoogleUser *previousUser = [_signIn restoredGoogleUserFromPreviousSignIn];
+  [_signIn restorePreviousSignInNoRefresh];
 
   [_authorization verify];
-  XCTAssertNil(previousUser);
+  XCTAssertNil(_signIn.currentUser);
 }
 
 - (void)testHasPreviousSignIn_HasBeenAuthenticated {