From a310d6818e90cbddf47efd66a4af6284c05ba2c3 Mon Sep 17 00:00:00 2001 From: Worthing ~ <115107835+w-goog@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:59:13 -0700 Subject: [PATCH 1/4] g-orchestrated: Carry nonce and claims JSON into continuation options --- GoogleSignIn/Sources/GIDSignInInternalOptions.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GoogleSignIn/Sources/GIDSignInInternalOptions.m b/GoogleSignIn/Sources/GIDSignInInternalOptions.m index 4a87bddf..4ea6a0da 100644 --- a/GoogleSignIn/Sources/GIDSignInInternalOptions.m +++ b/GoogleSignIn/Sources/GIDSignInInternalOptions.m @@ -124,7 +124,9 @@ - (instancetype)optionsWithExtraParameters:(NSDictionary *)extraParams options->_loginHint = _loginHint; options->_completion = _completion; options->_scopes = _scopes; + options->_nonce = _nonce; options->_claims = _claims; + options->_claimsAsJSON = _claimsAsJSON; options->_extraParams = [extraParams copy]; } return options; From c8a90b8e2748d4ae49ea83cdc17bedaa9f8069d9 Mon Sep 17 00:00:00 2001 From: Worthing ~ <115107835+w-goog@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:59:19 -0700 Subject: [PATCH 2/4] g-orchestrated: Changelog: carry nonce and claims across continuation --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 187dc61d..2636f7cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unreleased +- Fix a custom `nonce` and requested token `claims` being dropped when a sign-in is continued after a Device Policy app restart. + # 10.0.0 - **BREAKING**: Update to AppAuth 3.0.0 and GTMAppAuth 6.0.0, which raises the minimum deployment targets to iOS 15.0 and macOS 12.0, widens the `GTMSessionFetcher` dependency to allow 4.x and 5.x, and renames the version-specific Swift Package Manager manifest to `Package@swift-5.7.swift`. Projects that must keep supporting earlier OS versions should stay on GoogleSignIn 9.2.0. ([#628](https://github.com/google/GoogleSignIn-iOS/pull/628)) - Add `GIDSignIn.wrapperIdentifier` so SDKs that embed Google Sign-In can self-identify in Google's diagnostic logs via a new `gidwrapper` parameter. It is opt-in and pre-existing behavior is unchanged. ([#625](https://github.com/google/GoogleSignIn-iOS/pull/625)) From 91e52fd80d2ae077279fe2085f29e64813f9687a Mon Sep 17 00:00:00 2001 From: Worthing ~ <115107835+w-goog@users.noreply.github.com> Date: Fri, 28 Aug 2026 13:05:02 -0700 Subject: [PATCH 3/4] g-orchestrated: Test continuation options preserve nonce and claims --- .../Tests/Unit/GIDSignInInternalOptionsTest.m | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/GoogleSignIn/Tests/Unit/GIDSignInInternalOptionsTest.m b/GoogleSignIn/Tests/Unit/GIDSignInInternalOptionsTest.m index 13fcac4d..69a4dff8 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInInternalOptionsTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInInternalOptionsTest.m @@ -111,6 +111,59 @@ - (void)testDefaultOptions_withAllParameters_initializesPropertiesCorrectly { #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST } +- (void)testOptionsWithExtraParameters_forContinuation_preservesNonceAndClaims { + id configuration = OCMStrictClassMock([GIDConfiguration class]); +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + id presentingViewController = OCMStrictClassMock([UIViewController class]); +#elif TARGET_OS_OSX + id presentingWindow = OCMStrictClassMock([NSWindow class]); +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST + NSString *loginHint = @"login_hint"; + NSArray *scopes = @[@"scope1", @"scope2"]; + NSString *nonce = @"test_nonce"; + NSSet *claims = [NSSet setWithObject:[GIDClaim authTimeClaim]]; + + GIDSignInCompletion completion = ^(GIDSignInResult *_Nullable signInResult, + NSError * _Nullable error) {}; + GIDSignInInternalOptions *options = + [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + presentingViewController:presentingViewController +#elif TARGET_OS_OSX + presentingWindow:presentingWindow +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST + loginHint:loginHint + addScopesFlow:NO + scopes:scopes + nonce:nonce + claims:claims + completion:completion]; + + NSString *claimsAsJSON = @"{\"claim\":\"value\"}"; + options.claimsAsJSON = claimsAsJSON; + NSDictionary *extraParams = @{@"extra_key" : @"extra_value"}; + GIDSignInInternalOptions *continuationOptions = + [options optionsWithExtraParameters:extraParams forContinuation:YES]; + + XCTAssertEqualObjects(continuationOptions.nonce, nonce); + XCTAssertEqualObjects(continuationOptions.claims, claims); + XCTAssertEqualObjects(continuationOptions.claimsAsJSON, claimsAsJSON); + XCTAssertTrue(continuationOptions.continuation); + XCTAssertEqualObjects(continuationOptions.extraParams, extraParams); + XCTAssertEqualObjects(continuationOptions.loginHint, loginHint); + XCTAssertEqualObjects([NSSet setWithArray:continuationOptions.scopes], + [NSSet setWithArray:options.scopes]); + XCTAssertFalse(continuationOptions.addScopesFlow); + XCTAssertTrue(continuationOptions.interactive); + + OCMVerifyAll(configuration); +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + OCMVerifyAll(presentingViewController); +#elif TARGET_OS_OSX + OCMVerifyAll(presentingWindow); +#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST +} + - (void)testSilentOptions { GIDSignInCompletion completion = ^(GIDSignInResult *_Nullable signInResult, From 73e34c6baf497d5f415276466039ff0ec5764e4b Mon Sep 17 00:00:00 2001 From: Worthing ~ <115107835+w-goog@users.noreply.github.com> Date: Wed, 9 Sep 2026 13:52:19 -0700 Subject: [PATCH 4/4] g-orchestrated: Share test setup and rename the continuation test --- .../Tests/Unit/GIDSignInInternalOptionsTest.m | 176 +++++++++--------- 1 file changed, 85 insertions(+), 91 deletions(-) diff --git a/GoogleSignIn/Tests/Unit/GIDSignInInternalOptionsTest.m b/GoogleSignIn/Tests/Unit/GIDSignInInternalOptionsTest.m index 69a4dff8..52c61010 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInInternalOptionsTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInInternalOptionsTest.m @@ -25,146 +25,140 @@ #import #endif -@interface GIDSignInInternalOptionsTest : XCTestCase -@end +static NSString *const kLoginHint = @"login_hint"; +static NSString *const kScope1 = @"scope1"; +static NSString *const kScope2 = @"scope2"; +static NSString *const kNonce = @"test_nonce"; +static NSString *const kClaimsAsJSON = @"{\"claim\":\"value\"}"; -@implementation GIDSignInInternalOptionsTest +@interface GIDSignInInternalOptionsTest : XCTestCase { + // Mock for the configuration passed to the option factories. + id _configuration; -- (void)testDefaultOptions { - id configuration = OCMStrictClassMock([GIDConfiguration class]); #if TARGET_OS_IOS || TARGET_OS_MACCATALYST - id presentingViewController = OCMStrictClassMock([UIViewController class]); + // Mock for the presenting view controller passed to the option factories. + id _presentingViewController; #elif TARGET_OS_OSX - id presentingWindow = OCMStrictClassMock([NSWindow class]); + // Mock for the presenting window passed to the option factories. + id _presentingWindow; #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - NSString *loginHint = @"login_hint"; +} +@end - GIDSignInCompletion completion = ^(GIDSignInResult *_Nullable signInResult, - NSError * _Nullable error) {}; - GIDSignInInternalOptions *options = - [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration +@implementation GIDSignInInternalOptionsTest + +#pragma mark - Lifecycle + +- (void)setUp { + [super setUp]; + _configuration = OCMStrictClassMock([GIDConfiguration class]); #if TARGET_OS_IOS || TARGET_OS_MACCATALYST - presentingViewController:presentingViewController + _presentingViewController = OCMStrictClassMock([UIViewController class]); #elif TARGET_OS_OSX - presentingWindow:presentingWindow + _presentingWindow = OCMStrictClassMock([NSWindow class]); #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - loginHint:loginHint - addScopesFlow:NO - completion:completion]; - XCTAssertTrue(options.interactive); - XCTAssertFalse(options.continuation); - XCTAssertFalse(options.addScopesFlow); - XCTAssertNil(options.extraParams); +} + +#pragma mark - Helpers - OCMVerifyAll(configuration); +// The claim set requested by `-optionsWithAllParameters`. `GIDClaim` implements +// `-isEqual:` by name and essentiality, so a freshly built set compares equal. +- (NSSet *)expectedClaims { + return [NSSet setWithObject:[GIDClaim authTimeClaim]]; +} + +- (GIDSignInInternalOptions *)optionsWithAllParameters { + GIDSignInCompletion completion = ^(GIDSignInResult *_Nullable signInResult, + NSError *_Nullable error) {}; + return [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration #if TARGET_OS_IOS || TARGET_OS_MACCATALYST - OCMVerifyAll(presentingViewController); + presentingViewController:_presentingViewController #elif TARGET_OS_OSX - OCMVerifyAll(presentingWindow); + presentingWindow:_presentingWindow #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST + loginHint:kLoginHint + addScopesFlow:NO + scopes:@[kScope1, kScope2] + nonce:kNonce + claims:[self expectedClaims] + completion:completion]; } -- (void)testDefaultOptions_withAllParameters_initializesPropertiesCorrectly { - id configuration = OCMStrictClassMock([GIDConfiguration class]); +// Verifies the mocks created in `-setUp` have no unfulfilled expectations. +- (void)verifyConfigurationAndPresentationMocks { + OCMVerifyAll(_configuration); #if TARGET_OS_IOS || TARGET_OS_MACCATALYST - id presentingViewController = OCMStrictClassMock([UIViewController class]); + OCMVerifyAll(_presentingViewController); #elif TARGET_OS_OSX - id presentingWindow = OCMStrictClassMock([NSWindow class]); + OCMVerifyAll(_presentingWindow); #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - NSString *loginHint = @"login_hint"; - NSArray *scopes = @[@"scope1", @"scope2"]; - NSString *nonce = @"test_nonce"; - NSSet *claims = [NSSet setWithObject:[GIDClaim authTimeClaim]]; - NSArray *expectedScopes = @[@"scope1", @"scope2", @"email", @"profile"]; +} +#pragma mark - Tests + +- (void)testDefaultOptions { GIDSignInCompletion completion = ^(GIDSignInResult *_Nullable signInResult, - NSError * _Nullable error) {}; + NSError *_Nullable error) {}; GIDSignInInternalOptions *options = - [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration + [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration #if TARGET_OS_IOS || TARGET_OS_MACCATALYST - presentingViewController:presentingViewController + presentingViewController:_presentingViewController #elif TARGET_OS_OSX - presentingWindow:presentingWindow + presentingWindow:_presentingWindow #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - loginHint:loginHint + loginHint:kLoginHint addScopesFlow:NO - scopes:scopes - nonce:nonce - claims:claims completion:completion]; XCTAssertTrue(options.interactive); XCTAssertFalse(options.continuation); XCTAssertFalse(options.addScopesFlow); XCTAssertNil(options.extraParams); + [self verifyConfigurationAndPresentationMocks]; +} + +- (void)testDefaultOptions_withAllParameters_initializesPropertiesCorrectly { + NSArray *expectedScopes = @[kScope1, kScope2, @"email", @"profile"]; + + GIDSignInInternalOptions *options = [self optionsWithAllParameters]; + + XCTAssertTrue(options.interactive); + XCTAssertFalse(options.continuation); + XCTAssertFalse(options.addScopesFlow); + XCTAssertNil(options.extraParams); + // Convert arrays to sets for comparison to make the test order-independent. - XCTAssertEqualObjects([NSSet setWithArray:options.scopes], [NSSet setWithArray:expectedScopes]); - XCTAssertEqualObjects(options.nonce, nonce); - XCTAssertEqualObjects(options.claims, claims); + XCTAssertEqualObjects([NSSet setWithArray:options.scopes], + [NSSet setWithArray:expectedScopes]); + XCTAssertEqualObjects(options.nonce, kNonce); + XCTAssertEqualObjects(options.claims, [self expectedClaims]); XCTAssertNil(options.claimsAsJSON); - OCMVerifyAll(configuration); -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - OCMVerifyAll(presentingViewController); -#elif TARGET_OS_OSX - OCMVerifyAll(presentingWindow); -#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST + [self verifyConfigurationAndPresentationMocks]; } -- (void)testOptionsWithExtraParameters_forContinuation_preservesNonceAndClaims { - id configuration = OCMStrictClassMock([GIDConfiguration class]); -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - id presentingViewController = OCMStrictClassMock([UIViewController class]); -#elif TARGET_OS_OSX - id presentingWindow = OCMStrictClassMock([NSWindow class]); -#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - NSString *loginHint = @"login_hint"; - NSArray *scopes = @[@"scope1", @"scope2"]; - NSString *nonce = @"test_nonce"; - NSSet *claims = [NSSet setWithObject:[GIDClaim authTimeClaim]]; - - GIDSignInCompletion completion = ^(GIDSignInResult *_Nullable signInResult, - NSError * _Nullable error) {}; - GIDSignInInternalOptions *options = - [GIDSignInInternalOptions defaultOptionsWithConfiguration:configuration -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - presentingViewController:presentingViewController -#elif TARGET_OS_OSX - presentingWindow:presentingWindow -#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - loginHint:loginHint - addScopesFlow:NO - scopes:scopes - nonce:nonce - claims:claims - completion:completion]; - - NSString *claimsAsJSON = @"{\"claim\":\"value\"}"; - options.claimsAsJSON = claimsAsJSON; +- (void)testOptionsWithExtraParameters_forContinuation_preservesAllPropertiesAndSetsContinuation { + GIDSignInInternalOptions *options = [self optionsWithAllParameters]; + options.claimsAsJSON = kClaimsAsJSON; NSDictionary *extraParams = @{@"extra_key" : @"extra_value"}; + GIDSignInInternalOptions *continuationOptions = [options optionsWithExtraParameters:extraParams forContinuation:YES]; - XCTAssertEqualObjects(continuationOptions.nonce, nonce); - XCTAssertEqualObjects(continuationOptions.claims, claims); - XCTAssertEqualObjects(continuationOptions.claimsAsJSON, claimsAsJSON); + XCTAssertEqualObjects(continuationOptions.nonce, kNonce); + XCTAssertEqualObjects(continuationOptions.claims, [self expectedClaims]); + XCTAssertEqualObjects(continuationOptions.claimsAsJSON, kClaimsAsJSON); XCTAssertTrue(continuationOptions.continuation); XCTAssertEqualObjects(continuationOptions.extraParams, extraParams); - XCTAssertEqualObjects(continuationOptions.loginHint, loginHint); + XCTAssertEqualObjects(continuationOptions.loginHint, kLoginHint); XCTAssertEqualObjects([NSSet setWithArray:continuationOptions.scopes], [NSSet setWithArray:options.scopes]); XCTAssertFalse(continuationOptions.addScopesFlow); XCTAssertTrue(continuationOptions.interactive); - OCMVerifyAll(configuration); -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - OCMVerifyAll(presentingViewController); -#elif TARGET_OS_OSX - OCMVerifyAll(presentingWindow); -#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST + [self verifyConfigurationAndPresentationMocks]; } - - (void)testSilentOptions { GIDSignInCompletion completion = ^(GIDSignInResult *_Nullable signInResult, NSError * _Nullable error) {};