From 1b43e1814a3fe2f5f50d98f8ed1da19dca53130a Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:53:12 -0700 Subject: [PATCH 1/7] Added GIDTokenClaimsInternalOptions Implementation + Unit Tests --- .../Sources/GIDTokenClaimsInternalOptions.h | 48 ++++++++++ .../Sources/GIDTokenClaimsInternalOptions.m | 76 +++++++++++++++ .../Sources/Public/GoogleSignIn/GIDSignIn.h | 2 + .../Unit/GIDTokenClaimsInternalOptionsTest.m | 92 +++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h create mode 100644 GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.m create mode 100644 GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m diff --git a/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h b/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h new file mode 100644 index 00000000..7cc4153e --- /dev/null +++ b/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h @@ -0,0 +1,48 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class GIDTokenClaim; + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const kTokenClaimErrorDescription; + +extern NSString *const kTokenClaimEssentialPropertyKeyName; +extern NSString *const kTokenClaimKeyName; + +/** + * An internal utility class for processing and serializing the NSSet of GIDTokenClaim objects + * into the JSON format required for an OIDAuthorizationRequest. + */ +@interface GIDTokenClaimsInternalOptions : NSObject + +/** + * Processes the NSSet of GIDTokenClaim objects, handling ambiguous claims, and returns a JSON string. + * + * @param claims The NSSet of GIDTokenClaim objects provided by the developer. + * @param error A pointer to an NSError object to be populated if an error occurs (e.g., if a + * claim is requested as both essential and non-essential). + * @return A JSON string representing the claims request, or nil if the input is empty or an + * error occurs. + */ ++ (nullable NSString *)validatedJSONStringForClaims:(nullable NSSet *)claims + error:(NSError **)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.m b/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.m new file mode 100644 index 00000000..037a95e0 --- /dev/null +++ b/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.m @@ -0,0 +1,76 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GIDTokenClaimsInternalOptions.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDTokenClaim.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h" + +NSString * const kTokenClaimErrorDescription = @"The claim was requested as both essential and non-essential. Please provide only one version."; + +NSString * const kTokenClaimEssentialPropertyKey = @"essential"; +NSString * const kTokenClaimKeyName = @"id_token"; + +@implementation GIDTokenClaimsInternalOptions + ++ (nullable NSString *)validatedJSONStringForClaims:(nullable NSSet *)claims + error:(NSError **)error { + if (!claims || claims.count == 0) { + return nil; + } + + // === Step 1: Check for claims with ambiguous essential property. === + NSMutableDictionary *validTokenClaims = + [[NSMutableDictionary alloc] init]; + + for (GIDTokenClaim *currentClaim in claims) { + GIDTokenClaim *existingClaim = validTokenClaims[currentClaim.name]; + + // Check for a conflict: a claim with the same name but different essentiality. + if (existingClaim && existingClaim.isEssential != currentClaim.isEssential) { + if (error) { + *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeAmbiguousClaims + userInfo:@{NSLocalizedDescriptionKey: kTokenClaimErrorDescription}]; + } + return nil; // Validation failed + } + validTokenClaims[currentClaim.name] = currentClaim; + } + + // === Step 2: Build the dictionary structure required for OIDC JSON === + NSMutableDictionary *tokenClaimsDictionary = [[NSMutableDictionary alloc] init]; + for (GIDTokenClaim *claim in validTokenClaims.allValues) { + if (claim.isEssential) { + tokenClaimsDictionary[claim.name] = @{ kTokenClaimEssentialPropertyKey: @YES }; + } else { + // Per OIDC spec, non-essential claims can be represented by null. + tokenClaimsDictionary[claim.name] = [NSNull null]; + } + } + NSDictionary *finalRequestDictionary = @{ kTokenClaimKeyName: tokenClaimsDictionary }; + + // === Step 3: Serialize the final dictionary into a JSON string === + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:finalRequestDictionary + options:0 + error:error]; + if (!jsonData) { + return nil; + } + + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; +} + +@end diff --git a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h index 1025a92a..b147d908 100644 --- a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h +++ b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h @@ -45,6 +45,8 @@ typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) { kGIDSignInErrorCodeCanceled = -5, /// Indicates an Enterprise Mobility Management related error has occurred. kGIDSignInErrorCodeEMM = -6, + /// Indicates a claim was requested as both essential and non-essential . + kGIDSignInErrorCodeAmbiguousClaims = -7, /// Indicates the requested scopes have already been granted to the `currentUser`. kGIDSignInErrorCodeScopesAlreadyGranted = -8, /// Indicates there is an operation on a previous user. diff --git a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m new file mode 100644 index 00000000..a3b6da44 --- /dev/null +++ b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m @@ -0,0 +1,92 @@ +// +// GIDTokenClaimsInternalOptionsTest.h +// GoogleSignIn +// +// Created by Akshat Gandhi on 9/5/25. +// + + +#import +#import "GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDTokenClaim.h" + +@import OCMock; + +static NSString *const kEssentialAuthTimeExpectedJSON = @"{\"id_token\":{\"auth_time\":{\"essential\":true}}}"; +static NSString *const kNonEssentialAuthTimeExpectedJSON = @"{\"id_token\":{\"auth_time\":null}}"; + + +@interface GIDTokenClaimsInternalOptionsTest : XCTestCase +@end + +@implementation GIDTokenClaimsInternalOptionsTest + +#pragma mark - Input Validation Tests + +- (void)testValidatedJSONStringForClaims_WithNilInput_ShouldReturnNil { + XCTAssertNil([GIDTokenClaimsInternalOptions validatedJSONStringForClaims:nil error:nil]); +} + +- (void)testValidatedJSONStringForClaims_WithEmptyInput_ShouldReturnNil { + XCTAssertNil([GIDTokenClaimsInternalOptions validatedJSONStringForClaims:[NSSet set] error:nil]); +} + +#pragma mark - Correct Formatting Tests + +- (void)testValidatedJSONStringForClaims_WithNonEssentialClaim_IsCorrectlyFormatted { + NSSet *claims = [NSSet setWithObject:[GIDTokenClaim authTimeClaim]]; + + NSError *error = nil; + NSString *result = [GIDTokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; + + XCTAssertNil(error); + XCTAssertEqualObjects(result, kNonEssentialAuthTimeExpectedJSON); +} + +- (void)testValidatedJSONStringForClaims_WithEssentialClaim_IsCorrectlyFormatted { + NSSet *claims = [NSSet setWithObject:[GIDTokenClaim essentialAuthTimeClaim]]; + + NSError *error = nil; + NSString *result = [GIDTokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; + + XCTAssertNil(error); + XCTAssertEqualObjects(result, kEssentialAuthTimeExpectedJSON); +} + +#pragma mark - Client Error Handling Tests + +- (void)testValidatedJSONStringForClaims_WithConflictingClaims_ReturnsNilAndPopulatesError { + NSSet *claims = [NSSet setWithObjects:[GIDTokenClaim authTimeClaim], + [GIDTokenClaim essentialAuthTimeClaim], + nil]; + NSError *error = nil; + + NSString *result = [GIDTokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; + + XCTAssertNil(result, @"Method should return nil for conflicting claims."); + XCTAssertNotNil(error, @"An error object should be populated."); + XCTAssertEqualObjects(error.domain, kGIDSignInErrorDomain, @"Error domain should be correct."); + XCTAssertEqual(error.code, kGIDSignInErrorCodeAmbiguousClaims, @"Error code should be for ambiguous claims."); +} + +- (void)testValidatedJSONStringForClaims_WhenSerializationFails_ReturnsNilAndError { + NSSet *claims = [NSSet setWithObject:[GIDTokenClaim authTimeClaim]]; + NSError *fakeJSONError = [NSError errorWithDomain:@"com.fake.json" code:-999 userInfo:nil]; + id mockSerialization = OCMClassMock([NSJSONSerialization class]); + + OCMStub([mockSerialization dataWithJSONObject:OCMOCK_ANY + options:0 + error:[OCMArg setTo:fakeJSONError]]).andReturn(nil); + + NSError *actualError = nil; + NSString *result = + [GIDTokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&actualError]; + + XCTAssertNil(result, @"The result should be nil when JSON serialization fails."); + XCTAssertEqualObjects(actualError, fakeJSONError, + @"The error from serialization should be passed back to the caller."); + + [mockSerialization stopMocking]; +} + +@end From e9454a6c647667ac7155fde8eed244c56e4b5509 Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:32:06 -0700 Subject: [PATCH 2/7] Added header files to GIDTokenClaimsInternalOptions --- GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m | 1 + 1 file changed, 1 insertion(+) diff --git a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m index a3b6da44..72637a34 100644 --- a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m +++ b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m @@ -9,6 +9,7 @@ #import #import "GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDTokenClaim.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h" @import OCMock; From 351afc4371ff1976de71ba071ad3e7c580a225f3 Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:55:32 -0700 Subject: [PATCH 3/7] Replaced OCMock with Fake impl to support unit testing within GIDTokenClaimsInternalOptions --- .../GIDJSONSerializer/API/GIDJSONSerializer.h | 38 ++++++++++ .../Fake/GIDFakeJSONSerializerImpl.h | 35 ++++++++++ .../Fake/GIDFakeJSONSerializerImpl.m | 40 +++++++++++ .../Implementation/GIDJSONSerializerImpl.h | 26 +++++++ .../Implementation/GIDJSONSerializerImpl.m | 46 ++++++++++++ .../Sources/GIDTokenClaimsInternalOptions.h | 29 +++++--- .../Sources/GIDTokenClaimsInternalOptions.m | 61 ++++++++++------ .../Sources/Public/GoogleSignIn/GIDSignIn.h | 2 + .../Unit/GIDTokenClaimsInternalOptionsTest.m | 70 +++++++++++-------- 9 files changed, 283 insertions(+), 64 deletions(-) create mode 100644 GoogleSignIn/Sources/GIDJSONSerializer/API/GIDJSONSerializer.h create mode 100644 GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h create mode 100644 GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m create mode 100644 GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h create mode 100644 GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.m diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/API/GIDJSONSerializer.h b/GoogleSignIn/Sources/GIDJSONSerializer/API/GIDJSONSerializer.h new file mode 100644 index 00000000..06187949 --- /dev/null +++ b/GoogleSignIn/Sources/GIDJSONSerializer/API/GIDJSONSerializer.h @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A protocol for serializing an `NSDictionary` into a JSON string. + */ +@protocol GIDJSONSerializer + +/** + * Serializes the given dictionary into a `JSON` string. + * + * @param jsonObject The dictionary to be serialized. + * @param error A pointer to an `NSError` object to be populated upon failure. + * @return A `JSON` string representation of the dictionary, or `nil` if an error occurs. + */ +- (nullable NSString *)stringWithJSONObject:(NSDictionary *)jsonObject + error:(NSError *_Nullable *_Nullable)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h new file mode 100644 index 00000000..65380c4e --- /dev/null +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h @@ -0,0 +1,35 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GoogleSignIn/Sources/GIDJSONSerializer/API/GIDJSONSerializer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** A fake implementation of `GIDJSONSerializer` for testing purposes. */ +@interface GIDFakeJSONSerializerImpl : NSObject + +/** + * The error to be returned by `stringWithJSONObject:error:`. + * If `nil`, the method will attempt to perform a real serialization. + */ +@property(nonatomic, nullable) NSError *errorToReturn; + +/** The dictionary passed to the serialization method. */ +@property(nonatomic, readonly, nullable) NSDictionary *capturedJSONObject; + +@end + +NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m new file mode 100644 index 00000000..6b887176 --- /dev/null +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m @@ -0,0 +1,40 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h" + +@implementation GIDFakeJSONSerializerImpl + +- (nullable NSString *)stringWithJSONObject:(NSDictionary *)jsonObject + error:(NSError *_Nullable *_Nullable)error { + _capturedJSONObject = [jsonObject copy]; + + if (self.errorToReturn) { + if (error) { + *error = self.errorToReturn; + } + return nil; + } + NSData *data = [NSJSONSerialization dataWithJSONObject:jsonObject + options:0 + error:error]; + if (!data) { + return nil; + } + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; +} + +@end diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h b/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h new file mode 100644 index 00000000..5e1eb03d --- /dev/null +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h @@ -0,0 +1,26 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GoogleSignIn/Sources/GIDJSONSerializer/API/GIDJSONSerializer.h" + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const kGIDJSONSerializationErrorDescription; + +@interface GIDJSONSerializerImpl : NSObject +@end + +NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.m b/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.m new file mode 100644 index 00000000..43c76691 --- /dev/null +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.m @@ -0,0 +1,46 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h" + +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h" + +NSString * const kGIDJSONSerializationErrorDescription = + @"The provided object could not be serialized to a JSON string."; + +@implementation GIDJSONSerializerImpl + +- (nullable NSString *)stringWithJSONObject:(NSDictionary *)jsonObject + error:(NSError *_Nullable *_Nullable)error { + NSError *serializationError = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject + options:0 + error:&serializationError]; + if (!jsonData) { + if (error) { + *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeJSONSerializationFailure + userInfo:@{ + NSLocalizedDescriptionKey:kGIDJSONSerializationErrorDescription, + NSUnderlyingErrorKey:serializationError + }]; + } + return nil; + } + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; +} + +@end diff --git a/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h b/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h index 7cc4153e..adc56dd1 100644 --- a/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h +++ b/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h @@ -20,28 +20,35 @@ NS_ASSUME_NONNULL_BEGIN -extern NSString *const kTokenClaimErrorDescription; +extern NSString *const kGIDTokenClaimErrorDescription; +extern NSString *const kGIDTokenClaimEssentialPropertyKeyName; +extern NSString *const kGIDTokenClaimKeyName; -extern NSString *const kTokenClaimEssentialPropertyKeyName; -extern NSString *const kTokenClaimKeyName; +@protocol GIDJSONSerializer; /** - * An internal utility class for processing and serializing the NSSet of GIDTokenClaim objects - * into the JSON format required for an OIDAuthorizationRequest. + * An internal utility class for processing and serializing the `NSSet` of `GIDTokenClaim` objects + * into the `JSON` format required for an `OIDAuthorizationRequest`. */ @interface GIDTokenClaimsInternalOptions : NSObject +- (instancetype)init; + +- (instancetype)initWithJSONSerializer: + (id)jsonSerializer NS_DESIGNATED_INITIALIZER; + /** - * Processes the NSSet of GIDTokenClaim objects, handling ambiguous claims, and returns a JSON string. + * Processes the `NSSet` of `GIDTokenClaim` objects, handling ambiguous claims, + * and returns a `JSON` string. * - * @param claims The NSSet of GIDTokenClaim objects provided by the developer. - * @param error A pointer to an NSError object to be populated if an error occurs (e.g., if a + * @param claims The `NSSet` of `GIDTokenClaim` objects provided by the developer. + * @param error A pointer to an `NSError` object to be populated if an error occurs (e.g., if a * claim is requested as both essential and non-essential). - * @return A JSON string representing the claims request, or nil if the input is empty or an + * @return A `JSON` string representing the claims request, or `nil` if the input is empty or an * error occurs. */ -+ (nullable NSString *)validatedJSONStringForClaims:(nullable NSSet *)claims - error:(NSError **)error; +- (nullable NSString *)validatedJSONStringForClaims:(nullable NSSet *)claims + error:(NSError *_Nullable *_Nullable)error; @end diff --git a/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.m b/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.m index 037a95e0..049f00e1 100644 --- a/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.m +++ b/GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.m @@ -14,26 +14,45 @@ * limitations under the License. */ -#import "GIDTokenClaimsInternalOptions.h" -#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDTokenClaim.h" +#import "GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h" + +#import "GoogleSignIn/Sources/GIDJSONSerializer/API/GIDJSONSerializer.h" +#import "GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDTokenClaim.h" -NSString * const kTokenClaimErrorDescription = @"The claim was requested as both essential and non-essential. Please provide only one version."; +NSString * const kGIDTokenClaimErrorDescription = + @"The claim was requested as both essential and non-essential. " + @"Please provide only one version."; +NSString * const kGIDTokenClaimEssentialPropertyKey = @"essential"; +NSString * const kGIDTokenClaimKeyName = @"id_token"; -NSString * const kTokenClaimEssentialPropertyKey = @"essential"; -NSString * const kTokenClaimKeyName = @"id_token"; +@interface GIDTokenClaimsInternalOptions () +@property(nonatomic, readonly) id jsonSerializer; +@end @implementation GIDTokenClaimsInternalOptions -+ (nullable NSString *)validatedJSONStringForClaims:(nullable NSSet *)claims - error:(NSError **)error { +- (instancetype)init { + return [self initWithJSONSerializer:[[GIDJSONSerializerImpl alloc] init]]; +} + +- (instancetype)initWithJSONSerializer:(id)jsonSerializer { + if (self = [super init]) { + _jsonSerializer = jsonSerializer; + } + return self; +} + +- (nullable NSString *)validatedJSONStringForClaims:(nullable NSSet *)claims + error:(NSError *_Nullable *_Nullable)error { if (!claims || claims.count == 0) { return nil; } // === Step 1: Check for claims with ambiguous essential property. === NSMutableDictionary *validTokenClaims = - [[NSMutableDictionary alloc] init]; + [[NSMutableDictionary alloc] init]; for (GIDTokenClaim *currentClaim in claims) { GIDTokenClaim *existingClaim = validTokenClaims[currentClaim.name]; @@ -43,34 +62,30 @@ + (nullable NSString *)validatedJSONStringForClaims:(nullable NSSet *tokenClaimsDictionary = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *tokenClaimsDictionary = + [[NSMutableDictionary alloc] init]; for (GIDTokenClaim *claim in validTokenClaims.allValues) { if (claim.isEssential) { - tokenClaimsDictionary[claim.name] = @{ kTokenClaimEssentialPropertyKey: @YES }; + tokenClaimsDictionary[claim.name] = @{ kGIDTokenClaimEssentialPropertyKey: @YES }; } else { - // Per OIDC spec, non-essential claims can be represented by null. - tokenClaimsDictionary[claim.name] = [NSNull null]; + tokenClaimsDictionary[claim.name] = @{ kGIDTokenClaimEssentialPropertyKey: @NO }; } } - NSDictionary *finalRequestDictionary = @{ kTokenClaimKeyName: tokenClaimsDictionary }; + NSDictionary *finalRequestDictionary = + @{ kGIDTokenClaimKeyName: tokenClaimsDictionary }; // === Step 3: Serialize the final dictionary into a JSON string === - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:finalRequestDictionary - options:0 - error:error]; - if (!jsonData) { - return nil; - } - - return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + return [_jsonSerializer stringWithJSONObject:finalRequestDictionary error:error]; } @end diff --git a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h index b147d908..29cc0ef7 100644 --- a/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h +++ b/GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h @@ -51,6 +51,8 @@ typedef NS_ERROR_ENUM(kGIDSignInErrorDomain, GIDSignInErrorCode) { kGIDSignInErrorCodeScopesAlreadyGranted = -8, /// Indicates there is an operation on a previous user. kGIDSignInErrorCodeMismatchWithCurrentUser = -9, + /// Indicates that an object could not be serialized into a `JSON` string. + kGIDSignInErrorCodeJSONSerializationFailure = -10 }; /// This class is used to sign in users with their Google account and manage their session. diff --git a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m index 72637a34..decea23e 100644 --- a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m +++ b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m @@ -1,44 +1,62 @@ +// Copyright 2025 Google LLC // -// GIDTokenClaimsInternalOptionsTest.h -// GoogleSignIn +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// Created by Akshat Gandhi on 9/5/25. +// http://www.apache.org/licenses/LICENSE-2.0 // - +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #import + +#import "GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h" #import "GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h" -#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDTokenClaim.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h" - -@import OCMock; +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDTokenClaim.h" static NSString *const kEssentialAuthTimeExpectedJSON = @"{\"id_token\":{\"auth_time\":{\"essential\":true}}}"; -static NSString *const kNonEssentialAuthTimeExpectedJSON = @"{\"id_token\":{\"auth_time\":null}}"; - +static NSString *const kNonEssentialAuthTimeExpectedJSON = @"{\"id_token\":{\"auth_time\":{\"essential\":false}}}"; @interface GIDTokenClaimsInternalOptionsTest : XCTestCase +@property(nonatomic) GIDFakeJSONSerializerImpl *jsonSerializerFake; +@property(nonatomic) GIDTokenClaimsInternalOptions *tokenClaimsInternalOptions; @end @implementation GIDTokenClaimsInternalOptionsTest +- (void)setUp { + [super setUp]; + _jsonSerializerFake = [[GIDFakeJSONSerializerImpl alloc] init]; + _tokenClaimsInternalOptions = [[GIDTokenClaimsInternalOptions alloc] initWithJSONSerializer:_jsonSerializerFake]; +} + +- (void)tearDown { + _jsonSerializerFake = nil; + _tokenClaimsInternalOptions = nil; + [super tearDown]; +} + #pragma mark - Input Validation Tests - (void)testValidatedJSONStringForClaims_WithNilInput_ShouldReturnNil { - XCTAssertNil([GIDTokenClaimsInternalOptions validatedJSONStringForClaims:nil error:nil]); + XCTAssertNil([_tokenClaimsInternalOptions validatedJSONStringForClaims:nil error:nil]); } - (void)testValidatedJSONStringForClaims_WithEmptyInput_ShouldReturnNil { - XCTAssertNil([GIDTokenClaimsInternalOptions validatedJSONStringForClaims:[NSSet set] error:nil]); + XCTAssertNil([_tokenClaimsInternalOptions validatedJSONStringForClaims:[NSSet set] error:nil]); } #pragma mark - Correct Formatting Tests - (void)testValidatedJSONStringForClaims_WithNonEssentialClaim_IsCorrectlyFormatted { NSSet *claims = [NSSet setWithObject:[GIDTokenClaim authTimeClaim]]; - NSError *error = nil; - NSString *result = [GIDTokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; + NSString *result = [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; XCTAssertNil(error); XCTAssertEqualObjects(result, kNonEssentialAuthTimeExpectedJSON); @@ -46,9 +64,8 @@ - (void)testValidatedJSONStringForClaims_WithNonEssentialClaim_IsCorrectlyFormat - (void)testValidatedJSONStringForClaims_WithEssentialClaim_IsCorrectlyFormatted { NSSet *claims = [NSSet setWithObject:[GIDTokenClaim essentialAuthTimeClaim]]; - NSError *error = nil; - NSString *result = [GIDTokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; + NSString *result = [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; XCTAssertNil(error); XCTAssertEqualObjects(result, kEssentialAuthTimeExpectedJSON); @@ -60,34 +77,27 @@ - (void)testValidatedJSONStringForClaims_WithConflictingClaims_ReturnsNilAndPopu NSSet *claims = [NSSet setWithObjects:[GIDTokenClaim authTimeClaim], [GIDTokenClaim essentialAuthTimeClaim], nil]; - NSError *error = nil; - - NSString *result = [GIDTokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; + NSError *error; + NSString *result = [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; XCTAssertNil(result, @"Method should return nil for conflicting claims."); XCTAssertNotNil(error, @"An error object should be populated."); XCTAssertEqualObjects(error.domain, kGIDSignInErrorDomain, @"Error domain should be correct."); - XCTAssertEqual(error.code, kGIDSignInErrorCodeAmbiguousClaims, @"Error code should be for ambiguous claims."); + XCTAssertEqual(error.code, kGIDSignInErrorCodeAmbiguousClaims, + @"Error code should be for ambiguous claims."); } - (void)testValidatedJSONStringForClaims_WhenSerializationFails_ReturnsNilAndError { NSSet *claims = [NSSet setWithObject:[GIDTokenClaim authTimeClaim]]; - NSError *fakeJSONError = [NSError errorWithDomain:@"com.fake.json" code:-999 userInfo:nil]; - id mockSerialization = OCMClassMock([NSJSONSerialization class]); - - OCMStub([mockSerialization dataWithJSONObject:OCMOCK_ANY - options:0 - error:[OCMArg setTo:fakeJSONError]]).andReturn(nil); - + NSError *expectedJSONError = [NSError errorWithDomain:@"com.fake.json" code:-999 userInfo:nil]; + _jsonSerializerFake.errorToReturn = expectedJSONError; NSError *actualError = nil; NSString *result = - [GIDTokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&actualError]; + [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&actualError]; XCTAssertNil(result, @"The result should be nil when JSON serialization fails."); - XCTAssertEqualObjects(actualError, fakeJSONError, + XCTAssertEqualObjects(actualError, expectedJSONError, @"The error from serialization should be passed back to the caller."); - - [mockSerialization stopMocking]; } @end From c7a7fb53d5ea8761e1ab063884883b03324f4aec Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:17:48 -0700 Subject: [PATCH 4/7] Updated variable names --- .../GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m index 6b887176..82bd61dc 100644 --- a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m @@ -21,20 +21,19 @@ @implementation GIDFakeJSONSerializerImpl - (nullable NSString *)stringWithJSONObject:(NSDictionary *)jsonObject error:(NSError *_Nullable *_Nullable)error { _capturedJSONObject = [jsonObject copy]; - if (self.errorToReturn) { if (error) { *error = self.errorToReturn; } return nil; } - NSData *data = [NSJSONSerialization dataWithJSONObject:jsonObject + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:error]; - if (!data) { + if (!jsonData) { return nil; } - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; } @end From 3c02be4a9ca77766953b00f02b30910febe83ea3 Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:44:25 -0700 Subject: [PATCH 5/7] Removed nil from error initialization in GIDTokenClaimsInternalOptionsTest --- .../Implementation/GIDJSONSerializerImpl.m | 2 +- GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.m b/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.m index 43c76691..8a3e3d84 100644 --- a/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.m +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.m @@ -25,7 +25,7 @@ @implementation GIDJSONSerializerImpl - (nullable NSString *)stringWithJSONObject:(NSDictionary *)jsonObject error:(NSError *_Nullable *_Nullable)error { - NSError *serializationError = nil; + NSError *serializationError; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:&serializationError]; diff --git a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m index decea23e..047b58a4 100644 --- a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m +++ b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m @@ -55,7 +55,7 @@ - (void)testValidatedJSONStringForClaims_WithEmptyInput_ShouldReturnNil { - (void)testValidatedJSONStringForClaims_WithNonEssentialClaim_IsCorrectlyFormatted { NSSet *claims = [NSSet setWithObject:[GIDTokenClaim authTimeClaim]]; - NSError *error = nil; + NSError *error; NSString *result = [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; XCTAssertNil(error); @@ -64,7 +64,7 @@ - (void)testValidatedJSONStringForClaims_WithNonEssentialClaim_IsCorrectlyFormat - (void)testValidatedJSONStringForClaims_WithEssentialClaim_IsCorrectlyFormatted { NSSet *claims = [NSSet setWithObject:[GIDTokenClaim essentialAuthTimeClaim]]; - NSError *error = nil; + NSError *error; NSString *result = [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&error]; XCTAssertNil(error); @@ -91,7 +91,7 @@ - (void)testValidatedJSONStringForClaims_WhenSerializationFails_ReturnsNilAndErr NSSet *claims = [NSSet setWithObject:[GIDTokenClaim authTimeClaim]]; NSError *expectedJSONError = [NSError errorWithDomain:@"com.fake.json" code:-999 userInfo:nil]; _jsonSerializerFake.errorToReturn = expectedJSONError; - NSError *actualError = nil; + NSError *actualError; NSString *result = [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&actualError]; From 7c12f3f7334c8305cf7b4a6f40cda6c7650aa412 Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:05:14 -0700 Subject: [PATCH 6/7] Updated GIDTokenClaimsInternalOptionsTest to test correct expected error. --- .../Fake/GIDFakeJSONSerializerImpl.h | 9 +++++--- .../Fake/GIDFakeJSONSerializerImpl.m | 15 +++++++++++-- .../Unit/GIDTokenClaimsInternalOptionsTest.m | 22 ++++++++++++++----- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h index 65380c4e..fc932577 100644 --- a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h @@ -22,10 +22,13 @@ NS_ASSUME_NONNULL_BEGIN @interface GIDFakeJSONSerializerImpl : NSObject /** - * The error to be returned by `stringWithJSONObject:error:`. - * If `nil`, the method will attempt to perform a real serialization. + * A flag to control whether the serialization method should fail. + * + * If set to `YES`, `stringWithJSONObject:error:` will return `nil` and + * populate the error parameter with a serialization failure error. + * If `NO` (the default), it will attempt a real serialization. */ -@property(nonatomic, nullable) NSError *errorToReturn; +@property(nonatomic, assign) BOOL shouldFailJSONSerialization; /** The dictionary passed to the serialization method. */ @property(nonatomic, readonly, nullable) NSDictionary *capturedJSONObject; diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m index 82bd61dc..eaa9a960 100644 --- a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m @@ -16,17 +16,28 @@ #import "GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h" +#import "GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h" +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h" + @implementation GIDFakeJSONSerializerImpl - (nullable NSString *)stringWithJSONObject:(NSDictionary *)jsonObject error:(NSError *_Nullable *_Nullable)error { _capturedJSONObject = [jsonObject copy]; - if (self.errorToReturn) { + + // Check the boolean flag to see if we should simulate a failure. + if (self.shouldFailJSONSerialization) { if (error) { - *error = self.errorToReturn; + *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeJSONSerializationFailure + userInfo:@{ + NSLocalizedDescriptionKey:kGIDJSONSerializationErrorDescription, + }]; } return nil; } + + // If not failing, fall back to the real serialization path. NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:error]; diff --git a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m index 047b58a4..fa410f65 100644 --- a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m +++ b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m @@ -15,6 +15,7 @@ #import #import "GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h" +#import "GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h" #import "GoogleSignIn/Sources/GIDTokenClaimsInternalOptions.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDTokenClaim.h" @@ -23,8 +24,10 @@ static NSString *const kNonEssentialAuthTimeExpectedJSON = @"{\"id_token\":{\"auth_time\":{\"essential\":false}}}"; @interface GIDTokenClaimsInternalOptionsTest : XCTestCase + @property(nonatomic) GIDFakeJSONSerializerImpl *jsonSerializerFake; @property(nonatomic) GIDTokenClaimsInternalOptions *tokenClaimsInternalOptions; + @end @implementation GIDTokenClaimsInternalOptionsTest @@ -89,15 +92,22 @@ - (void)testValidatedJSONStringForClaims_WithConflictingClaims_ReturnsNilAndPopu - (void)testValidatedJSONStringForClaims_WhenSerializationFails_ReturnsNilAndError { NSSet *claims = [NSSet setWithObject:[GIDTokenClaim authTimeClaim]]; - NSError *expectedJSONError = [NSError errorWithDomain:@"com.fake.json" code:-999 userInfo:nil]; - _jsonSerializerFake.errorToReturn = expectedJSONError; + NSError *expectedJSONError = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeJSONSerializationFailure + userInfo:@{ + NSLocalizedDescriptionKey: kGIDJSONSerializationErrorDescription, + }]; + _jsonSerializerFake.shouldFailJSONSerialization = YES; NSError *actualError; - NSString *result = - [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&actualError]; + NSString *result = [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims + error:&actualError]; XCTAssertNil(result, @"The result should be nil when JSON serialization fails."); - XCTAssertEqualObjects(actualError, expectedJSONError, - @"The error from serialization should be passed back to the caller."); + XCTAssertEqualObjects( + actualError, + expectedJSONError, + @"The error from serialization should be passed back to the caller." + ); } @end From 8bd3bf3e308fc3387fc60915ed65269eb6c03cc0 Mon Sep 17 00:00:00 2001 From: Akshat Gandhi <54901287+AkshatG6@users.noreply.github.com> Date: Tue, 9 Sep 2025 12:44:21 -0700 Subject: [PATCH 7/7] Updated GIDFakeJSONSerializerImpl to accept a seeded error instead of a BOOL. --- .../GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h | 9 ++++----- .../GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m | 10 +++------- .../Tests/Unit/GIDTokenClaimsInternalOptionsTest.m | 2 +- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h index fc932577..469fc36d 100644 --- a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.h @@ -22,13 +22,12 @@ NS_ASSUME_NONNULL_BEGIN @interface GIDFakeJSONSerializerImpl : NSObject /** - * A flag to control whether the serialization method should fail. + * An error to be returned by `stringWithJSONObject:error:`. * - * If set to `YES`, `stringWithJSONObject:error:` will return `nil` and - * populate the error parameter with a serialization failure error. - * If `NO` (the default), it will attempt a real serialization. + * If this property is set, `stringWithJSONObject:error:` will return `nil` and + * populate the error parameter with this error. */ -@property(nonatomic, assign) BOOL shouldFailJSONSerialization; +@property(nonatomic, nullable) NSError *serializationError; /** The dictionary passed to the serialization method. */ @property(nonatomic, readonly, nullable) NSDictionary *capturedJSONObject; diff --git a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m index eaa9a960..400c06ce 100644 --- a/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m +++ b/GoogleSignIn/Sources/GIDJSONSerializer/Fake/GIDFakeJSONSerializerImpl.m @@ -25,14 +25,10 @@ - (nullable NSString *)stringWithJSONObject:(NSDictionary *)json error:(NSError *_Nullable *_Nullable)error { _capturedJSONObject = [jsonObject copy]; - // Check the boolean flag to see if we should simulate a failure. - if (self.shouldFailJSONSerialization) { + // Check if a serialization error should be simulated. + if (self.serializationError) { if (error) { - *error = [NSError errorWithDomain:kGIDSignInErrorDomain - code:kGIDSignInErrorCodeJSONSerializationFailure - userInfo:@{ - NSLocalizedDescriptionKey:kGIDJSONSerializationErrorDescription, - }]; + *error = self.serializationError; } return nil; } diff --git a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m index fa410f65..4c90998f 100644 --- a/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m +++ b/GoogleSignIn/Tests/Unit/GIDTokenClaimsInternalOptionsTest.m @@ -97,7 +97,7 @@ - (void)testValidatedJSONStringForClaims_WhenSerializationFails_ReturnsNilAndErr userInfo:@{ NSLocalizedDescriptionKey: kGIDJSONSerializationErrorDescription, }]; - _jsonSerializerFake.shouldFailJSONSerialization = YES; + _jsonSerializerFake.serializationError = expectedJSONError; NSError *actualError; NSString *result = [_tokenClaimsInternalOptions validatedJSONStringForClaims:claims error:&actualError];