GIDGoogleUserTest.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright 2021 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDGoogleUser.h"
  15. #import <XCTest/XCTest.h>
  16. #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDAuthentication.h"
  17. #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDConfiguration.h"
  18. #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDProfileData.h"
  19. #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDToken.h"
  20. #import "GoogleSignIn/Sources/GIDAuthentication_Private.h"
  21. #import "GoogleSignIn/Sources/GIDGoogleUser_Private.h"
  22. #import "GoogleSignIn/Tests/Unit/GIDProfileData+Testing.h"
  23. #import "GoogleSignIn/Tests/Unit/OIDAuthState+Testing.h"
  24. #import "GoogleSignIn/Tests/Unit/OIDAuthorizationRequest+Testing.h"
  25. #import "GoogleSignIn/Tests/Unit/OIDTokenRequest+Testing.h"
  26. #import "GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h"
  27. #ifdef SWIFT_PACKAGE
  28. @import AppAuth;
  29. #else
  30. #import <AppAuth/OIDAuthState.h>
  31. #endif
  32. static NSString *const kNewAccessToken = @"new_access_token";
  33. static NSTimeInterval const kExpireTime = 442886117;
  34. static NSTimeInterval const kNewAccessTokenExpireTime = 442886123;
  35. static NSTimeInterval const kNewIDTokenExpireTime = 442886124;
  36. static NSTimeInterval const kTimeAccuracy = 10;
  37. // List of observed properties of the class being tested.
  38. static NSString *const kObservedProperties[] = {
  39. @"accessToken",
  40. @"refreshToken",
  41. @"idToken",
  42. };
  43. static const NSUInteger kNumberOfObservedProperties =
  44. sizeof(kObservedProperties) / sizeof(*kObservedProperties);
  45. // Bit position for notification change type bitmask flags.
  46. // Must match the list of observed properties above.
  47. typedef NS_ENUM(NSUInteger, ChangeType) {
  48. kChangeTypeAccessTokenPrior,
  49. kChangeTypeAccessToken,
  50. kChangeTypeRefreshTokenPrior,
  51. kChangeTypeRefreshToken,
  52. kChangeTypeIDTokenPrior,
  53. kChangeTypeIDToken,
  54. kChangeTypeEnd // not a real change type but an end mark for calculating |kChangeAll|
  55. };
  56. static const NSUInteger kChangeNone = 0u;
  57. static const NSUInteger kChangeAll = (1u << kChangeTypeEnd) - 1u;
  58. #if __has_feature(c_static_assert) || __has_extension(c_static_assert)
  59. _Static_assert(kChangeTypeEnd == (sizeof(kObservedProperties) / sizeof(*kObservedProperties)) * 2,
  60. "List of observed properties must match list of change notification enums");
  61. #endif
  62. @interface GIDGoogleUserTest : XCTestCase
  63. @end
  64. @implementation GIDGoogleUserTest {
  65. // Fake data used to generate the expiration date of the access token.
  66. NSTimeInterval _accessTokenExpireTime;
  67. // Fake data used to generate the expiration date of the ID token.
  68. NSTimeInterval _idTokenExpireTime;
  69. // Bitmask flags for observed changes, as specified in |ChangeType|.
  70. NSUInteger _changesObserved;
  71. }
  72. - (void)setUp {
  73. _accessTokenExpireTime = kAccessTokenExpiresIn;
  74. _idTokenExpireTime = kExpireTime;
  75. _changesObserved = 0;
  76. }
  77. #pragma mark - Tests
  78. - (void)testInitWithAuthState {
  79. OIDAuthState *authState = [OIDAuthState testInstance];
  80. GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState
  81. profileData:[GIDProfileData testInstance]];
  82. GIDAuthentication *authentication =
  83. [[GIDAuthentication alloc] initWithAuthState:authState];
  84. XCTAssertEqualObjects(user.authentication, authentication);
  85. XCTAssertEqualObjects(user.grantedScopes, @[ OIDAuthorizationRequestTestingScope2 ]);
  86. XCTAssertEqualObjects(user.userID, kUserID);
  87. XCTAssertEqualObjects(user.configuration.hostedDomain, kHostedDomain);
  88. XCTAssertEqualObjects(user.configuration.clientID, OIDAuthorizationRequestTestingClientID);
  89. XCTAssertEqualObjects(user.profile, [GIDProfileData testInstance]);
  90. XCTAssertEqualObjects(user.accessToken.tokenString, kAccessToken);
  91. XCTAssertEqualObjects(user.refreshToken.tokenString, kRefreshToken);
  92. OIDIDToken *idToken = [[OIDIDToken alloc]
  93. initWithIDTokenString:authState.lastTokenResponse.idToken];
  94. XCTAssertEqualObjects(user.idToken.expirationDate, [idToken expiresAt]);
  95. }
  96. - (void)testCoding {
  97. if (@available(iOS 11, macOS 10.13, *)) {
  98. GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:[OIDAuthState testInstance]
  99. profileData:[GIDProfileData testInstance]];
  100. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:user
  101. requiringSecureCoding:YES
  102. error:nil];
  103. GIDGoogleUser *newUser = [NSKeyedUnarchiver unarchivedObjectOfClass:[GIDGoogleUser class]
  104. fromData:data
  105. error:nil];
  106. XCTAssertEqualObjects(user, newUser);
  107. XCTAssertTrue(GIDGoogleUser.supportsSecureCoding);
  108. } else {
  109. XCTSkip(@"Required API is not available for this test.");
  110. }
  111. }
  112. #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
  113. // Deprecated in iOS 13 and macOS 10.14
  114. - (void)testLegacyCoding {
  115. GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:[OIDAuthState testInstance]
  116. profileData:[GIDProfileData testInstance]];
  117. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:user];
  118. GIDGoogleUser *newUser = [NSKeyedUnarchiver unarchiveObjectWithData:data];
  119. XCTAssertEqualObjects(user, newUser);
  120. XCTAssertTrue(GIDGoogleUser.supportsSecureCoding);
  121. }
  122. #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST
  123. - (void) testUpdateAuthState {
  124. GIDGoogleUser *user = [self observedGoogleUser];
  125. XCTAssertEqualObjects(user.accessToken.tokenString, kAccessToken);
  126. [self assertDate:user.accessToken.expirationDate equalTime:_accessTokenExpireTime];
  127. XCTAssertEqualObjects(user.idToken.tokenString, [self idToken]);
  128. [self assertDate:user.idToken.expirationDate equalTime:_idTokenExpireTime];
  129. OIDAuthState *newAuthState = [self newAuthState];
  130. [user updateAuthState:newAuthState profileData:nil];
  131. XCTAssertEqualObjects(user.accessToken.tokenString, kNewAccessToken);
  132. [self assertDate:user.accessToken.expirationDate equalTime:kNewAccessTokenExpireTime];
  133. XCTAssertEqualObjects(user.idToken.tokenString, [self idTokenNew]);
  134. [self assertDate:user.idToken.expirationDate equalTime:kNewIDTokenExpireTime];
  135. XCTAssertEqual(_changesObserved, kChangeAll);
  136. }
  137. #pragma mark - Helpers
  138. - (GIDGoogleUser *)observedGoogleUser {
  139. GIDGoogleUser *user = [self googleUser];
  140. for (unsigned int i = 0; i < kNumberOfObservedProperties; ++i) {
  141. [user addObserver:self
  142. forKeyPath:kObservedProperties[i]
  143. options:NSKeyValueObservingOptionPrior
  144. context:NULL];
  145. }
  146. return user;
  147. }
  148. - (GIDGoogleUser *)googleUser {
  149. NSString *idToken = [self idToken];
  150. NSNumber *accessTokenExpiresIn =
  151. @(_accessTokenExpireTime - [[NSDate date] timeIntervalSinceReferenceDate]);
  152. OIDTokenRequest *tokenRequest =
  153. [OIDTokenRequest testInstanceWithAdditionalParameters:nil];
  154. OIDTokenResponse *tokenResponse =
  155. [OIDTokenResponse testInstanceWithIDToken:idToken
  156. accessToken:kAccessToken
  157. expiresIn:accessTokenExpiresIn
  158. tokenRequest:tokenRequest];
  159. return [[GIDGoogleUser alloc]
  160. initWithAuthState:[OIDAuthState testInstanceWithTokenResponse:tokenResponse]
  161. profileData:nil];
  162. }
  163. - (NSString *)idToken {
  164. return [self idTokenWithExpireTime:_idTokenExpireTime];
  165. }
  166. - (NSString *)idTokenNew {
  167. return [self idTokenWithExpireTime:kNewIDTokenExpireTime];
  168. }
  169. - (NSString *)idTokenWithExpireTime:(NSTimeInterval)expireTime {
  170. return [OIDTokenResponse idTokenWithSub:kUserID exp:@(expireTime + NSTimeIntervalSince1970)];
  171. }
  172. - (OIDAuthState *)newAuthState {
  173. NSNumber *expiresIn = @(kNewAccessTokenExpireTime - [NSDate timeIntervalSinceReferenceDate]);
  174. OIDTokenResponse *newResponse =
  175. [OIDTokenResponse testInstanceWithIDToken:[self idTokenNew]
  176. accessToken:kNewAccessToken
  177. expiresIn:expiresIn
  178. tokenRequest:nil];
  179. return [OIDAuthState testInstanceWithTokenResponse:newResponse];
  180. }
  181. - (void)assertDate:(NSDate *)date equalTime:(NSTimeInterval)time {
  182. XCTAssertEqualWithAccuracy([date timeIntervalSinceReferenceDate], time, kTimeAccuracy);
  183. }
  184. #pragma mark - NSKeyValueObserving
  185. - (void)observeValueForKeyPath:(NSString *)keyPath
  186. ofObject:(id)object
  187. change:(NSDictionary<NSKeyValueChangeKey,id> *)change
  188. context:(void *)context {
  189. GIDGoogleUser *user = (GIDGoogleUser*)object;
  190. ChangeType changeType;
  191. if ([keyPath isEqualToString:@"accessToken"]) {
  192. if (change[NSKeyValueChangeNotificationIsPriorKey]) {
  193. XCTAssertEqualObjects(user.accessToken.tokenString, kAccessToken);
  194. [self assertDate:user.accessToken.expirationDate equalTime:_accessTokenExpireTime];
  195. changeType = kChangeTypeAccessTokenPrior;
  196. } else {
  197. XCTAssertEqualObjects(user.accessToken.tokenString, kNewAccessToken);
  198. [self assertDate:user.accessToken.expirationDate equalTime:kNewAccessTokenExpireTime];
  199. changeType = kChangeTypeAccessToken;
  200. }
  201. } else if ([keyPath isEqualToString:@"refreshToken"]) {
  202. if (change[NSKeyValueChangeNotificationIsPriorKey]) {
  203. changeType = kChangeTypeRefreshTokenPrior;
  204. } else {
  205. changeType = kChangeTypeRefreshToken;
  206. }
  207. } else if ([keyPath isEqualToString:@"idToken"]) {
  208. if (change[NSKeyValueChangeNotificationIsPriorKey]) {
  209. XCTAssertEqualObjects(user.idToken.tokenString, [self idToken]);
  210. [self assertDate:user.idToken.expirationDate equalTime:_idTokenExpireTime];
  211. changeType = kChangeTypeIDTokenPrior;
  212. } else {
  213. XCTAssertEqualObjects(user.idToken.tokenString, [self idTokenNew]);
  214. [self assertDate:user.idToken.expirationDate equalTime:kNewIDTokenExpireTime];
  215. changeType = kChangeTypeIDToken;
  216. }
  217. } else {
  218. XCTFail(@"unexpected keyPath");
  219. return; // so compiler knows |changeType| is always assigned
  220. }
  221. NSInteger changeMask = 1 << changeType;
  222. XCTAssertFalse(_changesObserved & changeMask); // each change type should only fire once
  223. _changesObserved |= changeMask;
  224. }
  225. @end