diff --git a/GoogleSignIn/Sources/GIDGoogleUser.m b/GoogleSignIn/Sources/GIDGoogleUser.m index b03bd55d..cb215f7f 100644 --- a/GoogleSignIn/Sources/GIDGoogleUser.m +++ b/GoogleSignIn/Sources/GIDGoogleUser.m @@ -42,12 +42,19 @@ NS_ASSUME_NONNULL_BEGIN +@interface GIDGoogleUser () + +@property(nonatomic, readwrite) GIDToken *accessToken; + +@property(nonatomic, readwrite) GIDToken *refreshToken; + +@property(nonatomic, readwrite, nullable) GIDToken *idToken; + +@end + @implementation GIDGoogleUser { OIDAuthState *_authState; GIDConfiguration *_cachedConfiguration; - GIDToken *_cachedAccessToken; - GIDToken *_cachedRefreshToken; - GIDToken *_cachedIdToken; } - (nullable NSString *)userID { @@ -59,7 +66,6 @@ - (nullable NSString *)userID { return [idTokenDecoded.subject copy]; } } - return nil; } @@ -97,44 +103,9 @@ - (GIDConfiguration *)configuration { openIDRealm:openIDRealm]; }; } - return _cachedConfiguration; } -- (GIDToken *)accessToken { - @synchronized(self) { - if (!_cachedAccessToken) { - _cachedAccessToken = [[GIDToken alloc] initWithTokenString:_authState.lastTokenResponse.accessToken - expirationDate:_authState.lastTokenResponse. - accessTokenExpirationDate]; - } - } - return _cachedAccessToken; -} - -- (GIDToken *)refreshToken { - @synchronized(self) { - if (!_cachedRefreshToken) { - _cachedRefreshToken = [[GIDToken alloc] initWithTokenString:_authState.refreshToken - expirationDate:nil]; - } - } - return _cachedRefreshToken; -} - -- (nullable GIDToken *)idToken { - @synchronized(self) { - NSString *idTokenString = _authState.lastTokenResponse.idToken; - if (!_cachedIdToken && idTokenString) { - NSDate *idTokenExpirationDate = [[[OIDIDToken alloc] - initWithIDTokenString:idTokenString] expiresAt]; - _cachedIdToken = [[GIDToken alloc] initWithTokenString:idTokenString - expirationDate:idTokenExpirationDate]; - } - } - return _cachedIdToken; -} - #pragma mark - Private Methods - (instancetype)initWithAuthState:(OIDAuthState *)authState @@ -153,10 +124,36 @@ - (void)updateAuthState:(OIDAuthState *)authState _authentication = [[GIDAuthentication alloc] initWithAuthState:authState]; _profile = profileData; - // These three tokens will be generated in the getter and cached . - _cachedAccessToken = nil; - _cachedRefreshToken = nil; - _cachedIdToken = nil; + [self updateTokensWithAuthState:authState]; + } +} + +- (void)updateTokensWithAuthState:(OIDAuthState *)authState { + GIDToken *accessToken = + [[GIDToken alloc] initWithTokenString:authState.lastTokenResponse.accessToken + expirationDate:authState.lastTokenResponse.accessTokenExpirationDate]; + if (![self.accessToken isEqualToToken:accessToken]) { + self.accessToken = accessToken; + } + + GIDToken *refreshToken = [[GIDToken alloc] initWithTokenString:authState.refreshToken + expirationDate:nil]; + if (![self.refreshToken isEqualToToken:refreshToken]) { + self.refreshToken = refreshToken; + } + + GIDToken *idToken; + NSString *idTokenString = authState.lastTokenResponse.idToken; + if (idTokenString) { + NSDate *idTokenExpirationDate = + [[[OIDIDToken alloc] initWithIDTokenString:idTokenString] expiresAt]; + idToken = [[GIDToken alloc] initWithTokenString:idTokenString + expirationDate:idTokenExpirationDate]; + } else { + idToken = nil; + } + if ((self.idToken || idToken) && ![self.idToken isEqualToToken:idToken]) { + self.idToken = idToken; } } diff --git a/GoogleSignIn/Tests/Unit/GIDAuthenticationTest.m b/GoogleSignIn/Tests/Unit/GIDAuthenticationTest.m index 89f7f982..970ccdd9 100644 --- a/GoogleSignIn/Tests/Unit/GIDAuthenticationTest.m +++ b/GoogleSignIn/Tests/Unit/GIDAuthenticationTest.m @@ -561,6 +561,7 @@ - (GIDAuthentication *)auth { [OIDTokenResponse testInstanceWithIDToken:idToken accessToken:kAccessToken expiresIn:accessTokenExpiresIn + refreshToken:kRefreshToken tokenRequest:tokenRequest]; return [[GIDAuthentication alloc] initWithAuthState:[OIDAuthState testInstanceWithTokenResponse:tokenResponse]]; @@ -599,6 +600,7 @@ - (OIDTokenResponse *)tokenResponseWithNewTokens { return [OIDTokenResponse testInstanceWithIDToken:(_hasIDToken ? [self idTokenNew] : nil) accessToken:kNewAccessToken expiresIn:expiresIn + refreshToken:kRefreshToken tokenRequest:_tokenRequest ?: nil]; } diff --git a/GoogleSignIn/Tests/Unit/GIDGoogleUserTest.m b/GoogleSignIn/Tests/Unit/GIDGoogleUserTest.m index d09ddc5a..1410ce74 100644 --- a/GoogleSignIn/Tests/Unit/GIDGoogleUserTest.m +++ b/GoogleSignIn/Tests/Unit/GIDGoogleUserTest.m @@ -36,6 +36,8 @@ #endif static NSString *const kNewAccessToken = @"new_access_token"; +static NSString *const kNewRefreshToken = @"new_refresh_token"; + static NSTimeInterval const kTimeAccuracy = 10; // The difference between times. // It should be larger than kTimeAccuracy which is used in the method `XCTAssertEqualWithAccuracy`. @@ -102,19 +104,16 @@ - (void)testUpdateAuthState { NSTimeInterval accessTokenExpireTime = [[NSDate date] timeIntervalSince1970]; NSTimeInterval idTokenExpireTime = accessTokenExpireTime + kTimeIncrement; - NSString *idToken = [self idTokenWithExpireTime:idTokenExpireTime]; - OIDAuthState *authState = [OIDAuthState testInstanceWithIDToken:idToken - accessToken:kAccessToken - accessTokenExpireTime:accessTokenExpireTime]; - - GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState profileData:nil]; + GIDGoogleUser *user = [self googleUserWithAccessTokenExpireTime:accessTokenExpireTime + idTokenExpireTime:idTokenExpireTime]; NSTimeInterval updatedAccessTokenExpireTime = idTokenExpireTime + kTimeIncrement; NSTimeInterval updatedIDTokenExpireTime = updatedAccessTokenExpireTime + kTimeIncrement; NSString *updatedIDToken = [self idTokenWithExpireTime:updatedIDTokenExpireTime]; OIDAuthState *updatedAuthState = [OIDAuthState testInstanceWithIDToken:updatedIDToken accessToken:kNewAccessToken - accessTokenExpireTime:updatedAccessTokenExpireTime]; + accessTokenExpireTime:updatedAccessTokenExpireTime + refreshToken:kNewRefreshToken]; GIDProfileData *updatedProfileData = [GIDProfileData testInstance]; [user updateAuthState:updatedAuthState profileData:updatedProfileData]; @@ -125,11 +124,48 @@ - (void)testUpdateAuthState { XCTAssertEqualObjects(user.idToken.tokenString, updatedIDToken); XCTAssertEqualWithAccuracy([user.idToken.expirationDate timeIntervalSince1970], updatedIDTokenExpireTime, kTimeAccuracy); + XCTAssertEqualObjects(user.refreshToken.tokenString, kNewRefreshToken); XCTAssertEqual(user.profile, updatedProfileData); } +// When updating with a new OIDAuthState in which token information is not changed, the token objects +// should remain the same. +- (void)testUpdateAuthState_tokensAreNotChanged { + NSTimeInterval accessTokenExpireTime = [[NSDate date] timeIntervalSince1970]; + NSTimeInterval idTokenExpireTime = [[NSDate date] timeIntervalSince1970]; + + NSString *idToken = [self idTokenWithExpireTime:idTokenExpireTime]; + OIDAuthState *authState = [OIDAuthState testInstanceWithIDToken:idToken + accessToken:kAccessToken + accessTokenExpireTime:accessTokenExpireTime + refreshToken:kRefreshToken]; + + GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState profileData:nil]; + + GIDToken *accessTokenBeforeUpdate = user.accessToken; + GIDToken *refreshTokenBeforeUpdate = user.refreshToken; + GIDToken *idTokenBeforeUpdate = user.idToken; + + [user updateAuthState:authState profileData:nil]; + + XCTAssertIdentical(user.accessToken, accessTokenBeforeUpdate); + XCTAssertIdentical(user.idToken, idTokenBeforeUpdate); + XCTAssertIdentical(user.refreshToken, refreshTokenBeforeUpdate); +} + #pragma mark - Helpers +- (GIDGoogleUser *)googleUserWithAccessTokenExpireTime:(NSTimeInterval)accessTokenExpireTime + idTokenExpireTime:(NSTimeInterval)idTokenExpireTime { + NSString *idToken = [self idTokenWithExpireTime:idTokenExpireTime]; + OIDAuthState *authState = [OIDAuthState testInstanceWithIDToken:idToken + accessToken:kAccessToken + accessTokenExpireTime:accessTokenExpireTime + refreshToken:kRefreshToken]; + + return [[GIDGoogleUser alloc] initWithAuthState:authState profileData:nil]; +} + // The expireTime should be based on 1970. - (NSString *)idTokenWithExpireTime:(NSTimeInterval)expireTime { return [OIDTokenResponse idTokenWithSub:kUserID exp:@(expireTime)]; diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 5d9dac77..fdcd1027 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -1202,6 +1202,7 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow [OIDTokenResponse testInstanceWithIDToken:[OIDTokenResponse fatIDToken] accessToken:restoredSignIn ? kAccessToken : nil expiresIn:oldAccessToken ? @(300) : nil + refreshToken:kRefreshToken tokenRequest:nil]; OIDTokenRequest *tokenRequest = [[OIDTokenRequest alloc] diff --git a/GoogleSignIn/Tests/Unit/OIDAuthState+Testing.h b/GoogleSignIn/Tests/Unit/OIDAuthState+Testing.h index b255352f..f660e34e 100644 --- a/GoogleSignIn/Tests/Unit/OIDAuthState+Testing.h +++ b/GoogleSignIn/Tests/Unit/OIDAuthState+Testing.h @@ -30,6 +30,7 @@ + (instancetype)testInstanceWithIDToken:(NSString *)idToken accessToken:(NSString *)accessToken - accessTokenExpireTime:(NSTimeInterval)accessTokenExpireTime; + accessTokenExpireTime:(NSTimeInterval)accessTokenExpireTime + refreshToken:(NSString *)refreshToken; @end diff --git a/GoogleSignIn/Tests/Unit/OIDAuthState+Testing.m b/GoogleSignIn/Tests/Unit/OIDAuthState+Testing.m index 9143c4f7..b9082227 100644 --- a/GoogleSignIn/Tests/Unit/OIDAuthState+Testing.m +++ b/GoogleSignIn/Tests/Unit/OIDAuthState+Testing.m @@ -35,13 +35,15 @@ + (instancetype)testInstanceWithTokenResponse:(OIDTokenResponse *)tokenResponse + (instancetype)testInstanceWithIDToken:(NSString *)idToken accessToken:(NSString *)accessToken - accessTokenExpireTime:(NSTimeInterval)accessTokenExpireTime { + accessTokenExpireTime:(NSTimeInterval)accessTokenExpireTime + refreshToken:(NSString *)refreshToken { NSNumber *accessTokenExpiresIn = @(accessTokenExpireTime - [[NSDate date] timeIntervalSince1970]); OIDTokenResponse *newResponse = [OIDTokenResponse testInstanceWithIDToken:idToken accessToken:accessToken expiresIn:accessTokenExpiresIn + refreshToken:refreshToken tokenRequest:nil]; return [self testInstanceWithTokenResponse:newResponse]; } diff --git a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h index dc30523c..b565a392 100644 --- a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h +++ b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h @@ -56,6 +56,7 @@ extern NSString * const kFatPictureURL; + (instancetype)testInstanceWithIDToken:(NSString *)idToken accessToken:(NSString *)accessToken expiresIn:(NSNumber *)expiresIn + refreshToken:(NSString *)refreshToken tokenRequest:(OIDTokenRequest *)tokenRequest; + (NSString *)idToken; diff --git a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m index 1e1b95bf..49823be7 100644 --- a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m +++ b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m @@ -61,19 +61,21 @@ + (instancetype)testInstanceWithIDToken:(NSString *)idToken { return [OIDTokenResponse testInstanceWithIDToken:idToken accessToken:nil expiresIn:nil + refreshToken:nil tokenRequest:nil]; } + (instancetype)testInstanceWithIDToken:(NSString *)idToken accessToken:(NSString *)accessToken expiresIn:(NSNumber *)expiresIn + refreshToken:(NSString *)refreshToken tokenRequest:(OIDTokenRequest *)tokenRequest { NSMutableDictionary *parameters; parameters = [[NSMutableDictionary alloc] initWithDictionary:@{ @"access_token" : accessToken ?: kAccessToken, @"expires_in" : expiresIn ?: @(kAccessTokenExpiresIn), @"token_type" : @"example_token_type", - @"refresh_token" : kRefreshToken, + @"refresh_token" : refreshToken ?: kRefreshToken, @"scope" : [OIDScopeUtilities scopesWithArray:@[ OIDAuthorizationRequestTestingScope2 ]], @"server_code" : kServerAuthCode, }];