From 5b9f016bf6129dd587086de7ca40409aa26c3e8b Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Sun, 25 Jan 2026 21:06:52 -0800 Subject: [PATCH 1/3] Add unit tests for AMR claim --- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 112 ++++++++++++++++-- .../Tests/Unit/OIDTokenResponse+Testing.h | 13 +- .../Tests/Unit/OIDTokenResponse+Testing.m | 30 ++--- 3 files changed, 115 insertions(+), 40 deletions(-) diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 13b4435e..c85e77bc 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -160,11 +160,18 @@ static NSString *const kGrantedScope = @"grantedScope"; static NSString *const kNewScope = @"newScope"; -static NSString *const kEssentialAuthTimeClaimsJsonString = +static NSString *const kEssentialAuthTimeClaimJsonString = @"{\"id_token\":{\"auth_time\":{\"essential\":true}}}"; -static NSString *const kNonEssentialAuthTimeClaimsJsonString = +static NSString *const kNonEssentialAuthTimeClaimJsonString = @"{\"id_token\":{\"auth_time\":{\"essential\":false}}}"; +static NSString *const kEssentialAMRClaimJsonString = + @"{\"id_token\":{\"amr\":{\"essential\":true}}}"; +static NSString *const kNonEssentialAMRClaimJsonString = + @"{\"id_token\":{\"amr\":{\"essential\":false}}}"; + +static NSString *const kMultipleClaimsJsonString = + @"{\"id_token\":{\"amr\":{\"essential\":false},\"auth_time\":{\"essential\":false}}}"; #if TARGET_OS_IOS || TARGET_OS_MACCATALYST // This category is used to allow the test to swizzle a private method. @@ -751,7 +758,7 @@ - (void)testOAuthLogin_AdditionalScopes { XCTAssertEqualObjects(_savedAuthorizationRequest.scope, expectedScopeString); } -- (void)testOAuthLogin_WithClaims_FormatsParametersCorrectly { +- (void)testOAuthLogin_WithAuthTimeClaim_FormatsParametersCorrectly { GIDClaim *authTimeClaim = [GIDClaim authTimeClaim]; GIDClaim *essentialAuthTimeClaim = [GIDClaim essentialAuthTimeClaim]; @@ -776,7 +783,7 @@ - (void)testOAuthLogin_WithClaims_FormatsParametersCorrectly { claims:[NSSet setWithObject:essentialAuthTimeClaim]]; XCTAssertEqualObjects(_savedAuthorizationRequest.additionalParameters[@"claims"], - kEssentialAuthTimeClaimsJsonString, + kEssentialAuthTimeClaimJsonString, @"Claims JSON should be correctly formatted"); [self OAuthLoginWithAddScopesFlow:NO @@ -795,12 +802,92 @@ - (void)testOAuthLogin_WithClaims_FormatsParametersCorrectly { claims:[NSSet setWithObject:authTimeClaim]]; XCTAssertEqualObjects(_savedAuthorizationRequest.additionalParameters[@"claims"], - kNonEssentialAuthTimeClaimsJsonString, + kNonEssentialAuthTimeClaimJsonString, + @"Claims JSON should be correctly formatted"); +} + +- (void)testOAuthLogin_WithAMRClaim_FormatsParametersCorrectly { + GIDClaim *AMRClaim = [GIDClaim AMRClaim]; + GIDClaim *essentialAMRClaim = [GIDClaim essentialAMRClaim]; + + OCMStub([_keychainStore saveAuthSession:OCMOCK_ANY error:OCMArg.anyObjectRef] + ).andDo(^(NSInvocation *invocation){ + self->_keychainSaved = self->_saveAuthorizationReturnValue; + }); + + [self OAuthLoginWithAddScopesFlow:NO + authError:nil + tokenError:nil + emmPasscodeInfoRequired:NO + claimsAsJSONRequired:NO + keychainError:NO + claimsError:NO + restoredSignIn:NO + oldAccessToken:NO + modalCancel:NO + useAdditionalScopes:NO + additionalScopes:nil + manualNonce:nil + claims:[NSSet setWithObject:essentialAMRClaim]]; + + XCTAssertEqualObjects(_savedAuthorizationRequest.additionalParameters[@"claims"], + kEssentialAMRClaimJsonString, + @"Claims JSON should be correctly formatted"); + + [self OAuthLoginWithAddScopesFlow:NO + authError:nil + tokenError:nil + emmPasscodeInfoRequired:NO + claimsAsJSONRequired:NO + keychainError:NO + claimsError:NO + restoredSignIn:NO + oldAccessToken:NO + modalCancel:NO + useAdditionalScopes:NO + additionalScopes:nil + manualNonce:nil + claims:[NSSet setWithObject:AMRClaim]]; + + XCTAssertEqualObjects(_savedAuthorizationRequest.additionalParameters[@"claims"], + kNonEssentialAMRClaimJsonString, + @"Claims JSON should be correctly formatted"); +} + +- (void)testOAuthLogin_WithClaims_FormatsParametersCorrectly { + GIDClaim *authTimeClaim = [GIDClaim authTimeClaim]; + GIDClaim *AMRClaim = [GIDClaim AMRClaim]; + NSSet *claims = [NSSet setWithArray:@[authTimeClaim, AMRClaim]]; + + OCMStub([_keychainStore saveAuthSession:OCMOCK_ANY error:OCMArg.anyObjectRef] + ).andDo(^(NSInvocation *invocation){ + self->_keychainSaved = self->_saveAuthorizationReturnValue; + }); + + [self OAuthLoginWithAddScopesFlow:NO + authError:nil + tokenError:nil + emmPasscodeInfoRequired:NO + claimsAsJSONRequired:NO + keychainError:NO + claimsError:NO + restoredSignIn:NO + oldAccessToken:NO + modalCancel:NO + useAdditionalScopes:NO + additionalScopes:nil + manualNonce:nil + claims:claims]; + + XCTAssertEqualObjects(_savedAuthorizationRequest.additionalParameters[@"claims"], + kMultipleClaimsJsonString, @"Claims JSON should be correctly formatted"); } - (void)testOAuthLogin_WithClaims_ReturnsIdTokenWithCorrectClaims { GIDClaim *authTimeClaim = [GIDClaim authTimeClaim]; + GIDClaim *AMRClaim = [GIDClaim AMRClaim]; + NSSet *claims = [NSSet setWithArray:@[authTimeClaim, AMRClaim]]; OCMStub([_keychainStore saveAuthSession:OCMOCK_ANY error:OCMArg.anyObjectRef] ).andDo(^(NSInvocation *invocation){ @@ -820,7 +907,7 @@ - (void)testOAuthLogin_WithClaims_ReturnsIdTokenWithCorrectClaims { useAdditionalScopes:NO additionalScopes:nil manualNonce:nil - claims:[NSSet setWithObject:authTimeClaim]]; + claims:claims]; XCTAssertNotNil(_signIn.currentUser, @"The currentUser should not be nil after a successful sign-in."); NSString *idTokenString = _signIn.currentUser.idToken.tokenString; @@ -830,10 +917,13 @@ - (void)testOAuthLogin_WithClaims_ReturnsIdTokenWithCorrectClaims { NSData *payloadData = [[NSData alloc] initWithBase64EncodedString:components[1] options:NSDataBase64DecodingIgnoreUnknownCharacters]; - NSDictionary *claims = [NSJSONSerialization JSONObjectWithData:payloadData options:0 error:nil]; - XCTAssertEqualObjects(claims[@"auth_time"], + NSDictionary *receivedClaims = [NSJSONSerialization JSONObjectWithData:payloadData options:0 error:nil]; + XCTAssertEqualObjects(receivedClaims[@"auth_time"], kAuthTime, @"The 'auth_time' claim should be present and correct."); + XCTAssertEqualObjects(receivedClaims[@"amr"], + [OIDTokenResponse testAMRValues], + @"The 'amr' claim should be present and correct."); } - (void)testAddScopes { @@ -963,7 +1053,7 @@ - (void)testAddScopes_WithPreviouslyRequestedClaims { NSArray *expectedScopes = @[kNewScope, kGrantedScope]; XCTAssertEqualObjects(grantedScopes, expectedScopes); XCTAssertEqualObjects(_savedAuthorizationRequest.additionalParameters[@"claims"], - kNonEssentialAuthTimeClaimsJsonString, + kNonEssentialAuthTimeClaimJsonString, @"Claims JSON should be correctly formatted"); [_user verify]; @@ -1688,7 +1778,7 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow nonce:nonce errorString:authError]; - NSString *idToken = claims ? [OIDTokenResponse fatIDTokenWithAuthTime] : [OIDTokenResponse fatIDToken]; + NSString *idToken = claims ? [OIDTokenResponse fatIDTokenWithClaims] : [OIDTokenResponse fatIDToken]; OIDTokenResponse *tokenResponse = [OIDTokenResponse testInstanceWithIDToken:idToken accessToken:restoredSignIn ? kAccessToken : nil @@ -1958,7 +2048,7 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow additionalParameters[@"emm_passcode_info_required"] = @"1"; } if (claimsAsJSONRequired) { - additionalParameters[@"claims"] = kNonEssentialAuthTimeClaimsJsonString; + additionalParameters[@"claims"] = kNonEssentialAuthTimeClaimJsonString; } return [additionalParameters copy]; diff --git a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h index b8329c67..4f3f3917 100644 --- a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h +++ b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h @@ -60,18 +60,13 @@ extern NSString * const kFatPictureURL; refreshToken:(NSString *)refreshToken tokenRequest:(OIDTokenRequest *)tokenRequest; -+ (instancetype)testInstanceWithIDToken:(NSString *)idToken - accessToken:(NSString *)accessToken - expiresIn:(NSNumber *)expiresIn - refreshToken:(NSString *)refreshToken - authTime:(NSString *)authTime - tokenRequest:(OIDTokenRequest *)tokenRequest; - + (NSString *)idToken; + (NSString *)fatIDToken; -+ (NSString *)fatIDTokenWithAuthTime; ++ (NSString *)fatIDTokenWithClaims; + ++ (NSArray *)testAMRValues; /** * @sub The subject of the ID token. @@ -81,6 +76,6 @@ extern NSString * const kFatPictureURL; + (NSString *)idTokenWithSub:(NSString *)sub exp:(NSNumber *)exp fat:(BOOL)fat; -+ (NSString *)idTokenWithSub:(NSString *)sub exp:(NSNumber *)exp fat:(BOOL)fat authTime:(NSString *)authTime; ++ (NSString *)idTokenWithSub:(NSString *)sub exp:(NSNumber *)exp fat:(BOOL)fat claims:(BOOL)claims; @end diff --git a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m index bf2a5fa7..e1281883 100644 --- a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m +++ b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m @@ -71,21 +71,6 @@ + (instancetype)testInstanceWithIDToken:(NSString *)idToken expiresIn:(NSNumber *)expiresIn refreshToken:(NSString *)refreshToken tokenRequest:(OIDTokenRequest *)tokenRequest { - return [OIDTokenResponse testInstanceWithIDToken:idToken - accessToken:accessToken - expiresIn:expiresIn - refreshToken:refreshToken - authTime:nil - tokenRequest:tokenRequest]; -} - -+ (instancetype)testInstanceWithIDToken:(NSString *)idToken - accessToken:(NSString *)accessToken - expiresIn:(NSNumber *)expiresIn - refreshToken:(NSString *)refreshToken - authTime:(NSString *)authTime - tokenRequest:(OIDTokenRequest *)tokenRequest { - NSMutableDictionary *parameters = [[NSMutableDictionary alloc] initWithDictionary:@{ @"access_token" : accessToken ?: kAccessToken, @"expires_in" : expiresIn ?: @(kAccessTokenExpiresIn), @@ -109,8 +94,12 @@ + (NSString *)fatIDToken { return [self idTokenWithSub:kUserID exp:@(kIDTokenExpires) fat:YES]; } -+ (NSString *)fatIDTokenWithAuthTime { - return [self idTokenWithSub:kUserID exp:@(kIDTokenExpires) fat:YES authTime:kAuthTime]; ++ (NSString *)fatIDTokenWithClaims { + return [self idTokenWithSub:kUserID exp:@(kIDTokenExpires) fat:YES claims:YES]; +} + ++ (NSArray *)testAMRValues { + return @[ @"pwd", @"mfa", @"otp" ]; } + (NSString *)idTokenWithSub:(NSString *)sub exp:(NSNumber *)exp { @@ -120,13 +109,13 @@ + (NSString *)idTokenWithSub:(NSString *)sub exp:(NSNumber *)exp { + (NSString *)idTokenWithSub:(NSString *)sub exp:(NSNumber *)exp fat:(BOOL)fat { - return [self idTokenWithSub:kUserID exp:exp fat:fat authTime:nil]; + return [self idTokenWithSub:kUserID exp:exp fat:fat claims:NO]; } + (NSString *)idTokenWithSub:(NSString *)sub exp:(NSNumber *)exp fat:(BOOL)fat - authTime:(NSString *)authTime{ + claims:(BOOL)claims { NSError *error; NSDictionary *headerContents = @{ @"alg" : kAlg, @@ -156,9 +145,10 @@ + (NSString *)idTokenWithSub:(NSString *)sub kFatPictureURLKey : kFatPictureURL, }]; } - if (authTime) { + if (claims) { [payloadContents addEntriesFromDictionary:@{ @"auth_time": kAuthTime, + @"amr": [OIDTokenResponse testAMRValues], }]; } NSData *payloadJson = [NSJSONSerialization dataWithJSONObject:payloadContents From e1ba3f1dc39cc49a87913cbbdda1c7e19714a444 Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Mon, 26 Jan 2026 08:36:04 -0800 Subject: [PATCH 2/3] Updated unit tests --- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index c85e77bc..fd22bd1c 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -854,7 +854,7 @@ - (void)testOAuthLogin_WithAMRClaim_FormatsParametersCorrectly { @"Claims JSON should be correctly formatted"); } -- (void)testOAuthLogin_WithClaims_FormatsParametersCorrectly { +- (void)testOAuthLogin_WithMultipleClaims_FormatsParametersCorrectly { GIDClaim *authTimeClaim = [GIDClaim authTimeClaim]; GIDClaim *AMRClaim = [GIDClaim AMRClaim]; NSSet *claims = [NSSet setWithArray:@[authTimeClaim, AMRClaim]]; @@ -884,7 +884,7 @@ - (void)testOAuthLogin_WithClaims_FormatsParametersCorrectly { @"Claims JSON should be correctly formatted"); } -- (void)testOAuthLogin_WithClaims_ReturnsIdTokenWithCorrectClaims { +- (void)testOAuthLogin_WithMultipleClaims_ReturnsIdTokenWithCorrectClaims { GIDClaim *authTimeClaim = [GIDClaim authTimeClaim]; GIDClaim *AMRClaim = [GIDClaim AMRClaim]; NSSet *claims = [NSSet setWithArray:@[authTimeClaim, AMRClaim]]; From 3602ad97f3d27fc7e15170768aa01741a4f5d5bf Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:35:10 -0800 Subject: [PATCH 3/3] Renamed testAMRVaues to stubbedAMRValues --- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 2 +- GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h | 2 +- GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index fd22bd1c..ac245739 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -922,7 +922,7 @@ - (void)testOAuthLogin_WithMultipleClaims_ReturnsIdTokenWithCorrectClaims { kAuthTime, @"The 'auth_time' claim should be present and correct."); XCTAssertEqualObjects(receivedClaims[@"amr"], - [OIDTokenResponse testAMRValues], + [OIDTokenResponse stubbedAMRValues], @"The 'amr' claim should be present and correct."); } diff --git a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h index 4f3f3917..4ef08862 100644 --- a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h +++ b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.h @@ -66,7 +66,7 @@ extern NSString * const kFatPictureURL; + (NSString *)fatIDTokenWithClaims; -+ (NSArray *)testAMRValues; ++ (NSArray *)stubbedAMRValues; /** * @sub The subject of the ID token. diff --git a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m index e1281883..4dc8b0ae 100644 --- a/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m +++ b/GoogleSignIn/Tests/Unit/OIDTokenResponse+Testing.m @@ -98,7 +98,7 @@ + (NSString *)fatIDTokenWithClaims { return [self idTokenWithSub:kUserID exp:@(kIDTokenExpires) fat:YES claims:YES]; } -+ (NSArray *)testAMRValues { ++ (NSArray *)stubbedAMRValues { return @[ @"pwd", @"mfa", @"otp" ]; } @@ -148,7 +148,7 @@ + (NSString *)idTokenWithSub:(NSString *)sub if (claims) { [payloadContents addEntriesFromDictionary:@{ @"auth_time": kAuthTime, - @"amr": [OIDTokenResponse testAMRValues], + @"amr": [OIDTokenResponse stubbedAMRValues], }]; } NSData *payloadJson = [NSJSONSerialization dataWithJSONObject:payloadContents