GIDAuthenticationTest.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  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 <XCTest/XCTest.h>
  15. #import "GoogleSignIn/Sources/GIDAuthentication.h"
  16. #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h"
  17. #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h"
  18. #import "GoogleSignIn/Sources/GIDMDMPasscodeState.h"
  19. #import "GoogleSignIn/Sources/GIDSignInPreferences.h"
  20. #import "GoogleSignIn/Tests/Unit/OIDAuthState+Testing.h"
  21. #import "GoogleSignIn/Tests/Unit/OIDTokenRequest+Testing.h"
  22. #import "GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h"
  23. #ifdef SWIFT_PACKAGE
  24. @import AppAuth;
  25. @import GoogleUtilities_MethodSwizzler;
  26. @import GoogleUtilities_SwizzlerTestHelpers;
  27. @import GTMAppAuth;
  28. @import GTMSessionFetcherCore;
  29. @import OCMock;
  30. #else
  31. #import <AppAuth/OIDAuthState.h>
  32. #import <AppAuth/OIDAuthorizationRequest.h>
  33. #import <AppAuth/OIDAuthorizationResponse.h>
  34. #import <AppAuth/OIDAuthorizationService.h>
  35. #import <AppAuth/OIDError.h>
  36. #import <AppAuth/OIDIDToken.h>
  37. #import <AppAuth/OIDServiceConfiguration.h>
  38. #import <AppAuth/OIDTokenRequest.h>
  39. #import <AppAuth/OIDTokenResponse.h>
  40. #import <GoogleUtilities/GULSwizzler.h>
  41. #import <GoogleUtilities/GULSwizzler+Unswizzle.h>
  42. #import <GTMAppAuth/GTMAppAuthFetcherAuthorization.h>
  43. #import <GTMSessionFetcher/GTMSessionFetcher.h>
  44. #import <OCMock/OCMock.h>
  45. #endif
  46. static NSString *const kClientID = @"87654321.googleusercontent.com";
  47. static NSString *const kNewAccessToken = @"new_access_token";
  48. static NSString *const kUserEmail = @"foo@gmail.com";
  49. static NSTimeInterval const kExpireTime = 442886117;
  50. static NSTimeInterval const kNewExpireTime = 442886123;
  51. static NSTimeInterval const kNewExpireTime2 = 442886124;
  52. static NSTimeInterval const kTimeAccuracy = 10;
  53. // The system name in old iOS versions.
  54. static NSString *const kOldIOSName = @"iPhone OS";
  55. // The system name in new iOS versions.
  56. static NSString *const kNewIOSName = @"iOS";
  57. @interface GIDAuthenticationTest : XCTestCase
  58. @end
  59. @implementation GIDAuthenticationTest {
  60. // Whether the auth object has ID token or not.
  61. BOOL _hasIDToken;
  62. // Fake data used to generate the expiration date of the access token.
  63. NSTimeInterval _accessTokenExpireTime;
  64. // Fake data used to generate the expiration date of the ID token.
  65. NSTimeInterval _idTokenExpireTime;
  66. // Fake data used to generate the additional token request parameters.
  67. NSDictionary *_additionalTokenRequestParameters;
  68. // The saved token fetch handler.
  69. OIDTokenCallback _tokenFetchHandler;
  70. // The saved token request.
  71. OIDTokenRequest *_tokenRequest;
  72. // The fake system name used for testing.
  73. NSString *_fakeSystemName;
  74. }
  75. - (void)setUp {
  76. _hasIDToken = YES;
  77. _accessTokenExpireTime = kAccessTokenExpiresIn;
  78. _idTokenExpireTime = kExpireTime;
  79. _additionalTokenRequestParameters = nil;
  80. _tokenFetchHandler = nil;
  81. _tokenRequest = nil;
  82. [GULSwizzler swizzleClass:[OIDAuthorizationService class]
  83. selector:@selector(performTokenRequest:originalAuthorizationResponse:callback:)
  84. isClassSelector:YES
  85. withBlock:^(id sender,
  86. OIDTokenRequest *request,
  87. OIDAuthorizationResponse *authorizationResponse,
  88. OIDTokenCallback callback) {
  89. XCTAssertNotNil(authorizationResponse.request.clientID);
  90. XCTAssertNotNil(authorizationResponse.request.configuration.tokenEndpoint);
  91. XCTAssertNil(self->_tokenFetchHandler); // only one on-going fetch allowed
  92. self->_tokenFetchHandler = [callback copy];
  93. self->_tokenRequest = [request copy];
  94. return nil;
  95. }];
  96. _fakeSystemName = kNewIOSName;
  97. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  98. [GULSwizzler swizzleClass:[UIDevice class]
  99. selector:@selector(systemName)
  100. isClassSelector:NO
  101. withBlock:^(id sender) { return self->_fakeSystemName; }];
  102. #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  103. }
  104. - (void)tearDown {
  105. [GULSwizzler unswizzleClass:[OIDAuthorizationService class]
  106. selector:@selector(performTokenRequest:originalAuthorizationResponse:callback:)
  107. isClassSelector:YES];
  108. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  109. [GULSwizzler unswizzleClass:[UIDevice class]
  110. selector:@selector(systemName)
  111. isClassSelector:NO];
  112. #endif
  113. }
  114. #pragma mark - Tests
  115. - (void)testAuthState {
  116. OIDAuthState *authState = [OIDAuthState testInstance];
  117. GIDAuthentication *auth = [[GIDAuthentication alloc] initWithAuthState:authState];
  118. OIDAuthState *authStateReturned = auth.authState;
  119. XCTAssertEqual(authState, authStateReturned);
  120. }
  121. - (void)testFetcherAuthorizer {
  122. // This is really hard to test without assuming how GTMAppAuthFetcherAuthorization works
  123. // internally, so let's just take the shortcut here by asserting we get a
  124. // GTMAppAuthFetcherAuthorization object.
  125. GIDAuthentication *auth = [self auth];
  126. id<GTMFetcherAuthorizationProtocol> fetcherAuthroizer = auth.fetcherAuthorizer;
  127. XCTAssertTrue([fetcherAuthroizer isKindOfClass:[GTMAppAuthFetcherAuthorization class]]);
  128. XCTAssertTrue([fetcherAuthroizer canAuthorize]);
  129. }
  130. - (void)testDoWithFreshTokensWithBothExpired {
  131. // Both tokens expired 10 seconds ago.
  132. [self setExpireTimeForAccessToken:-10 IDToken:-10];
  133. [self verifyTokensRefreshedWithMethod:@selector(doWithFreshTokens:)];
  134. }
  135. - (void)testDoWithFreshTokensWithAccessTokenExpired {
  136. // Access token expired 10 seconds ago while ID token to expire in 10 minutes.
  137. [self setExpireTimeForAccessToken:-10 IDToken:10 * 60];
  138. [self verifyTokensRefreshedWithMethod:@selector(doWithFreshTokens:)];
  139. }
  140. - (void)testDoWithFreshTokensWithIDTokenToExpire {
  141. // Access token to expire in 10 minutes while ID token to expire in 10 seconds.
  142. [self setExpireTimeForAccessToken:10 * 60 IDToken:10];
  143. [self verifyTokensRefreshedWithMethod:@selector(doWithFreshTokens:)];
  144. }
  145. - (void)testDoWithFreshTokensWithBothFresh {
  146. // Both tokens to expire in 10 minutes.
  147. [self setExpireTimeForAccessToken:10 * 60 IDToken:10 * 60];
  148. [self verifyTokensNotRefreshedWithMethod:@selector(doWithFreshTokens:)];
  149. }
  150. - (void)testDoWithFreshTokensWithAccessTokenExpiredAndNoIDToken {
  151. _hasIDToken = NO;
  152. [self setExpireTimeForAccessToken:-10 IDToken:10 * 60]; // access token expired 10 seconds ago
  153. [self verifyTokensRefreshedWithMethod:@selector(doWithFreshTokens:)];
  154. }
  155. - (void)testDoWithFreshTokensWithAccessTokenFreshAndNoIDToken {
  156. _hasIDToken = NO;
  157. [self setExpireTimeForAccessToken:10 * 60 IDToken:-10]; // access token to expire in 10 minutes
  158. [self verifyTokensNotRefreshedWithMethod:@selector(doWithFreshTokens:)];
  159. }
  160. - (void)testDoWithFreshTokensError {
  161. [self setTokensExpireTime:-10]; // expired 10 seconds ago
  162. GIDAuthentication *auth = [self auth];
  163. XCTestExpectation *expectation = [self expectationWithDescription:@"Callback is called"];
  164. [auth doWithFreshTokens:^(OIDAuthState *authState, NSError *error) {
  165. [expectation fulfill];
  166. XCTAssertNil(authState);
  167. XCTAssertNotNil(error);
  168. }];
  169. _tokenFetchHandler(nil, [self fakeError]);
  170. [self waitForExpectationsWithTimeout:1 handler:nil];
  171. [self assertOldTokensInAuth:auth.authState];
  172. }
  173. - (void)testDoWithFreshTokensQueue {
  174. GIDAuthentication *auth = [self auth];
  175. XCTestExpectation *firstExpectation =
  176. [self expectationWithDescription:@"First callback is called"];
  177. [auth doWithFreshTokens:^(OIDAuthState *authState, NSError *error) {
  178. [firstExpectation fulfill];
  179. [self assertNewTokensInAuth:authState];
  180. XCTAssertNil(error);
  181. }];
  182. XCTestExpectation *secondExpectation =
  183. [self expectationWithDescription:@"Second callback is called"];
  184. [auth doWithFreshTokens:^(OIDAuthState *authState, NSError *error) {
  185. [secondExpectation fulfill];
  186. [self assertNewTokensInAuth:authState];
  187. XCTAssertNil(error);
  188. }];
  189. _tokenFetchHandler([self tokenResponseWithNewTokens], nil);
  190. [self waitForExpectationsWithTimeout:1 handler:nil];
  191. [self assertNewTokensInAuth:auth.authState];
  192. }
  193. #pragma mark - EMM Support
  194. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  195. - (void)testEMMSupport {
  196. _additionalTokenRequestParameters = @{
  197. @"emm_support" : @"xyz",
  198. };
  199. GIDAuthentication *auth = [self auth];
  200. [auth doWithFreshTokens:^(OIDAuthState *authState, NSError *error){}];
  201. _tokenFetchHandler([self tokenResponseWithNewTokens], nil);
  202. NSDictionary *expectedParameters = @{
  203. @"emm_support" : @"xyz",
  204. @"device_os" : [NSString stringWithFormat:@"%@ %@",
  205. _fakeSystemName, [UIDevice currentDevice].systemVersion],
  206. kSDKVersionLoggingParameter : GIDVersion(),
  207. kEnvironmentLoggingParameter : GIDEnvironment(),
  208. };
  209. XCTAssertEqualObjects(auth.authState.lastTokenResponse.request.additionalParameters,
  210. expectedParameters);
  211. }
  212. - (void)testSystemNameNormalization {
  213. _fakeSystemName = kOldIOSName;
  214. _additionalTokenRequestParameters = @{
  215. @"emm_support" : @"xyz",
  216. };
  217. GIDAuthentication *auth = [self auth];
  218. [auth doWithFreshTokens:^(OIDAuthState * _Nonnull authState,
  219. NSError * _Nullable error) {}];
  220. _tokenFetchHandler([self tokenResponseWithNewTokens], nil);
  221. NSDictionary *expectedParameters = @{
  222. @"emm_support" : @"xyz",
  223. @"device_os" : [NSString stringWithFormat:@"%@ %@",
  224. kNewIOSName, [UIDevice currentDevice].systemVersion],
  225. kSDKVersionLoggingParameter : GIDVersion(),
  226. kEnvironmentLoggingParameter : GIDEnvironment(),
  227. };
  228. XCTAssertEqualObjects(auth.authState.lastTokenResponse.request.additionalParameters,
  229. expectedParameters);
  230. }
  231. - (void)testEMMPasscodeInfo {
  232. _additionalTokenRequestParameters = @{
  233. @"emm_support" : @"xyz",
  234. @"device_os" : @"old one",
  235. @"emm_passcode_info" : @"something",
  236. };
  237. GIDAuthentication *auth = [self auth];
  238. [auth doWithFreshTokens:^(OIDAuthState * _Nonnull authState,
  239. NSError * _Nullable error) {}];
  240. _tokenFetchHandler([self tokenResponseWithNewTokens], nil);
  241. NSDictionary *expectedParameters = @{
  242. @"emm_support" : @"xyz",
  243. @"device_os" : [NSString stringWithFormat:@"%@ %@",
  244. _fakeSystemName, [UIDevice currentDevice].systemVersion],
  245. @"emm_passcode_info" : [GIDMDMPasscodeState passcodeState].info,
  246. kSDKVersionLoggingParameter : GIDVersion(),
  247. kEnvironmentLoggingParameter : GIDEnvironment(),
  248. };
  249. XCTAssertEqualObjects(auth.authState.lastTokenResponse.request.additionalParameters,
  250. expectedParameters);
  251. }
  252. - (void)testEMMError {
  253. // Set expectations.
  254. NSDictionary *errorJSON = @{ @"error" : @"EMM Specific Error" };
  255. NSError *emmError = [NSError errorWithDomain:@"anydomain"
  256. code:12345
  257. userInfo:@{ OIDOAuthErrorResponseErrorKey : errorJSON }];
  258. id mockEMMErrorHandler = OCMStrictClassMock([GIDEMMErrorHandler class]);
  259. [[[mockEMMErrorHandler stub] andReturn:mockEMMErrorHandler] sharedInstance];
  260. __block void (^completion)(void);
  261. [[[mockEMMErrorHandler expect] andReturnValue:@YES]
  262. handleErrorFromResponse:errorJSON completion:[OCMArg checkWithBlock:^(id arg) {
  263. completion = arg;
  264. return YES;
  265. }]];
  266. // Start testing.
  267. _additionalTokenRequestParameters = @{
  268. @"emm_support" : @"xyz",
  269. };
  270. GIDAuthentication *auth = [self auth];
  271. XCTestExpectation *notCalled = [self expectationWithDescription:@"Callback is not called"];
  272. notCalled.inverted = YES;
  273. XCTestExpectation *called = [self expectationWithDescription:@"Callback is called"];
  274. [auth doWithFreshTokens:^(OIDAuthState *authState, NSError *error) {
  275. [notCalled fulfill];
  276. [called fulfill];
  277. XCTAssertNil(authState);
  278. XCTAssertEqualObjects(error.domain, kGIDSignInErrorDomain);
  279. XCTAssertEqual(error.code, kGIDSignInErrorCodeEMM);
  280. }];
  281. _tokenFetchHandler(nil, emmError);
  282. // Verify and clean up.
  283. [mockEMMErrorHandler verify];
  284. [mockEMMErrorHandler stopMocking];
  285. [self waitForExpectations:@[ notCalled ] timeout:1];
  286. completion();
  287. [self waitForExpectations:@[ called ] timeout:1];
  288. [self assertOldTokensInAuth:auth.authState];
  289. }
  290. - (void)testNonEMMError {
  291. // Set expectations.
  292. NSDictionary *errorJSON = @{ @"error" : @"Not EMM Specific Error" };
  293. NSError *emmError = [NSError errorWithDomain:@"anydomain"
  294. code:12345
  295. userInfo:@{ OIDOAuthErrorResponseErrorKey : errorJSON }];
  296. id mockEMMErrorHandler = OCMStrictClassMock([GIDEMMErrorHandler class]);
  297. [[[mockEMMErrorHandler stub] andReturn:mockEMMErrorHandler] sharedInstance];
  298. __block void (^completion)(void);
  299. [[[mockEMMErrorHandler expect] andReturnValue:@NO]
  300. handleErrorFromResponse:errorJSON completion:[OCMArg checkWithBlock:^(id arg) {
  301. completion = arg;
  302. return YES;
  303. }]];
  304. // Start testing.
  305. _additionalTokenRequestParameters = @{
  306. @"emm_support" : @"xyz",
  307. };
  308. GIDAuthentication *auth = [self auth];
  309. XCTestExpectation *notCalled = [self expectationWithDescription:@"Callback is not called"];
  310. notCalled.inverted = YES;
  311. XCTestExpectation *called = [self expectationWithDescription:@"Callback is called"];
  312. [auth doWithFreshTokens:^(OIDAuthState *authState, NSError *error) {
  313. [notCalled fulfill];
  314. [called fulfill];
  315. XCTAssertNil(authState);
  316. XCTAssertEqualObjects(error.domain, @"anydomain");
  317. XCTAssertEqual(error.code, 12345);
  318. }];
  319. _tokenFetchHandler(nil, emmError);
  320. // Verify and clean up.
  321. [mockEMMErrorHandler verify];
  322. [mockEMMErrorHandler stopMocking];
  323. [self waitForExpectations:@[ notCalled ] timeout:1];
  324. completion();
  325. [self waitForExpectations:@[ called ] timeout:1];
  326. [self assertOldTokensInAuth:auth.authState];
  327. }
  328. - (void)testCodingPreserveEMMParameters {
  329. _additionalTokenRequestParameters = @{
  330. @"emm_support" : @"xyz",
  331. @"device_os" : @"old one",
  332. @"emm_passcode_info" : @"something",
  333. };
  334. NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[self auth]];
  335. GIDAuthentication *auth = [NSKeyedUnarchiver unarchiveObjectWithData:data];
  336. [auth doWithFreshTokens:^(OIDAuthState * _Nonnull authState,
  337. NSError * _Nullable error) {}];
  338. _tokenFetchHandler([self tokenResponseWithNewTokens], nil);
  339. NSDictionary *expectedParameters = @{
  340. @"emm_support" : @"xyz",
  341. @"device_os" : [NSString stringWithFormat:@"%@ %@",
  342. [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion],
  343. @"emm_passcode_info" : [GIDMDMPasscodeState passcodeState].info,
  344. kSDKVersionLoggingParameter : GIDVersion(),
  345. kEnvironmentLoggingParameter : GIDEnvironment(),
  346. };
  347. XCTAssertEqualObjects(auth.authState.lastTokenResponse.request.additionalParameters,
  348. expectedParameters);
  349. }
  350. #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  351. #pragma mark - Helpers
  352. - (GIDAuthentication *)auth {
  353. NSString *idToken = [self idToken];
  354. NSNumber *accessTokenExpiresIn =
  355. @(_accessTokenExpireTime - [[NSDate date] timeIntervalSinceReferenceDate]);
  356. OIDTokenRequest *tokenRequest =
  357. [OIDTokenRequest testInstanceWithAdditionalParameters:_additionalTokenRequestParameters];
  358. OIDTokenResponse *tokenResponse =
  359. [OIDTokenResponse testInstanceWithIDToken:idToken
  360. accessToken:kAccessToken
  361. expiresIn:accessTokenExpiresIn
  362. tokenRequest:tokenRequest];
  363. return [[GIDAuthentication alloc]
  364. initWithAuthState:[OIDAuthState testInstanceWithTokenResponse:tokenResponse]];
  365. }
  366. - (NSString *)idTokenWithExpireTime:(NSTimeInterval)expireTime {
  367. if (!_hasIDToken) {
  368. return nil;
  369. }
  370. return [OIDTokenResponse idTokenWithSub:kUserID exp:@(expireTime + NSTimeIntervalSince1970)];
  371. }
  372. - (NSString *)idToken {
  373. return [self idTokenWithExpireTime:_idTokenExpireTime];
  374. }
  375. - (NSString *)idTokenNew {
  376. return [self idTokenWithExpireTime:kNewExpireTime2];
  377. }
  378. - (OIDTokenResponse *)tokenResponseWithNewTokens {
  379. NSNumber *expiresIn = @(kNewExpireTime - [NSDate timeIntervalSinceReferenceDate]);
  380. return [OIDTokenResponse testInstanceWithIDToken:(_hasIDToken ? [self idTokenNew] : nil)
  381. accessToken:kNewAccessToken
  382. expiresIn:expiresIn
  383. tokenRequest:_tokenRequest ?: nil];
  384. }
  385. - (NSError *)fakeError {
  386. return [NSError errorWithDomain:@"fake.domain" code:-1 userInfo:nil];
  387. }
  388. - (void)assertDate:(NSDate *)date equalTime:(NSTimeInterval)time {
  389. XCTAssertEqualWithAccuracy([date timeIntervalSinceReferenceDate], time, kTimeAccuracy);
  390. }
  391. - (void)assertOldAccessTokenInAuth:(OIDAuthState *)auth {
  392. XCTAssertEqualObjects([self accessTokenString:auth], kAccessToken);
  393. [self assertDate:[self accessTokenExpirationDate:auth] equalTime:_accessTokenExpireTime];
  394. }
  395. - (void)assertNewAccessTokenInAuth:(OIDAuthState *)auth {
  396. XCTAssertEqualObjects([self accessTokenString:auth], kNewAccessToken);
  397. [self assertDate:[self accessTokenExpirationDate:auth] equalTime:kNewExpireTime];
  398. }
  399. - (void)assertOldTokensInAuth:(OIDAuthState *)auth {
  400. [self assertOldAccessTokenInAuth:auth];
  401. XCTAssertEqualObjects([self idTokenString:auth], [self idToken]);
  402. if (_hasIDToken) {
  403. [self assertDate:[self idTokenExpirationDate:auth] equalTime:_idTokenExpireTime];
  404. }
  405. }
  406. - (void)assertNewTokensInAuth:(OIDAuthState *)auth {
  407. [self assertNewAccessTokenInAuth:auth];
  408. XCTAssertEqualObjects([self idTokenString:auth], [self idTokenNew]);
  409. if (_hasIDToken) {
  410. [self assertDate:[self idTokenExpirationDate:auth] equalTime:kNewExpireTime2];
  411. }
  412. }
  413. - (void)setTokensExpireTime:(NSTimeInterval)fromNow {
  414. [self setExpireTimeForAccessToken:fromNow IDToken:fromNow];
  415. }
  416. - (void)setExpireTimeForAccessToken:(NSTimeInterval)accessExpire IDToken:(NSTimeInterval)idExpire {
  417. _accessTokenExpireTime = [[NSDate date] timeIntervalSinceReferenceDate] + accessExpire;
  418. _idTokenExpireTime = [[NSDate date] timeIntervalSinceReferenceDate] + idExpire;
  419. }
  420. - (void)verifyTokensRefreshedWithMethod:(SEL)sel {
  421. GIDAuthentication *auth = [self auth];
  422. XCTestExpectation *expectation = [self expectationWithDescription:@"Callback is called"];
  423. #pragma clang diagnostic push
  424. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  425. // We know the method doesn't return anything, so there is no risk of leaking.
  426. [auth performSelector:sel withObject:^(OIDAuthState *authState, NSError *error) {
  427. #pragma clang diagnostic pop
  428. [expectation fulfill];
  429. [self assertNewTokensInAuth:authState];
  430. XCTAssertNil(error);
  431. }];
  432. _tokenFetchHandler([self tokenResponseWithNewTokens], nil);
  433. [self waitForExpectationsWithTimeout:1 handler:nil];
  434. [self assertNewTokensInAuth:auth.authState];
  435. }
  436. - (void)verifyTokensNotRefreshedWithMethod:(SEL)sel {
  437. GIDAuthentication *auth = [self auth];
  438. XCTestExpectation *expectation = [self expectationWithDescription:@"Callback is called"];
  439. #pragma clang diagnostic push
  440. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  441. // We know the method doesn't return anything, so there is no risk of leaking.
  442. [auth performSelector:sel withObject:^(OIDAuthState *authState, NSError *error) {
  443. #pragma clang diagnostic pop
  444. [expectation fulfill];
  445. [self assertOldTokensInAuth:authState];
  446. XCTAssertNil(error);
  447. }];
  448. XCTAssertNil(_tokenFetchHandler);
  449. [self waitForExpectationsWithTimeout:1 handler:nil];
  450. [self assertOldTokensInAuth:auth.authState];
  451. }
  452. #pragma mark - Parse OIDAuthState
  453. - (NSString *)accessTokenString:(OIDAuthState *)authState {
  454. return authState.lastTokenResponse.accessToken;
  455. }
  456. - (NSDate *)accessTokenExpirationDate:(OIDAuthState *)authState {
  457. return authState.lastTokenResponse.accessTokenExpirationDate;
  458. }
  459. - (NSString *)refreshTokenString:(OIDAuthState *)authState {
  460. return authState.refreshToken;
  461. }
  462. - (nullable NSString *)idTokenString:(OIDAuthState *)authState {
  463. return authState.lastTokenResponse.idToken;
  464. }
  465. - (nullable NSDate *)idTokenExpirationDate:(OIDAuthState *)authState {
  466. NSString *idTokenString = [self idTokenString:authState];
  467. if (!idTokenString) {
  468. return nil;
  469. }
  470. return [[[OIDIDToken alloc] initWithIDTokenString:idTokenString] expiresAt];
  471. }
  472. @end