Skip to content
Merged
75 changes: 75 additions & 0 deletions GoogleSignIn/Sources/GIDClaim.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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/Public/GoogleSignIn/GIDClaim.h"

NSString * const kAuthTimeClaimName = @"auth_time";

// Private interface to declare the internal initializer
@interface GIDClaim ()

- (instancetype)initWithName:(NSString *)name
essential:(BOOL)essential NS_DESIGNATED_INITIALIZER;

@end

@implementation GIDClaim

// Private designated initializer
- (instancetype)initWithName:(NSString *)name essential:(BOOL)essential {
self = [super init];
if (self) {
_name = [name copy];
_essential = essential;
}
return self;
}

#pragma mark - Factory Methods

+ (instancetype)authTimeClaim {
return [[self alloc] initWithName:kAuthTimeClaimName essential:NO];
}

+ (instancetype)essentialAuthTimeClaim {
return [[self alloc] initWithName:kAuthTimeClaimName essential:YES];
}

#pragma mark - NSObject

- (BOOL)isEqual:(id)object {
// 1. Check if the other object is the same instance in memory.
if (self == object) {
return YES;
}

// 2. Check if the other object is not a GIDTokenClaim instance.
if (![object isKindOfClass:[GIDClaim class]]) {
return NO;
}

// 3. Compare the properties that define equality.
GIDClaim *other = (GIDClaim *)object;
return [self.name isEqualToString:other.name] &&
self.isEssential == other.isEssential;
}

- (NSUInteger)hash {
// The hash value should be based on the same properties used in isEqual:
return self.name.hash ^ @(self.isEssential).hash;
}

@end
55 changes: 55 additions & 0 deletions GoogleSignIn/Sources/GIDClaimsInternalOptions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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 <Foundation/Foundation.h>

@class GIDClaim;

NS_ASSUME_NONNULL_BEGIN

extern NSString *const kGIDClaimErrorDescription;
extern NSString *const kGIDClaimEssentialPropertyKeyName;
extern NSString *const kGIDClaimKeyName;

@protocol GIDJSONSerializer;

/**
* An internal utility class for processing and serializing the `NSSet` of `GIDClaim` objects
* into the `JSON` format required for an `OIDAuthorizationRequest`.
*/
@interface GIDClaimsInternalOptions : NSObject

- (instancetype)init;

- (instancetype)initWithJSONSerializer:
(id<GIDJSONSerializer>)jsonSerializer NS_DESIGNATED_INITIALIZER;

/**
* Processes the `NSSet` of `GIDClaim` objects, handling ambiguous claims,
* and returns a `JSON` string.
*
* @param claims The `NSSet` of `GIDClaim` 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<GIDClaim *> *)claims
error:(NSError *_Nullable *_Nullable)error;

@end

NS_ASSUME_NONNULL_END
91 changes: 91 additions & 0 deletions GoogleSignIn/Sources/GIDClaimsInternalOptions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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/GIDClaimsInternalOptions.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/GIDClaim.h"

NSString * const kGIDClaimErrorDescription =
@"The claim was requested as both essential and non-essential. "
@"Please provide only one version.";
NSString * const kGIDClaimEssentialPropertyKey = @"essential";
NSString * const kGIDClaimKeyName = @"id_token";

@interface GIDClaimsInternalOptions ()
@property(nonatomic, readonly) id<GIDJSONSerializer> jsonSerializer;
@end

@implementation GIDClaimsInternalOptions

- (instancetype)init {
return [self initWithJSONSerializer:[[GIDJSONSerializerImpl alloc] init]];
}

- (instancetype)initWithJSONSerializer:(id<GIDJSONSerializer>)jsonSerializer {
if (self = [super init]) {
_jsonSerializer = jsonSerializer;
}
return self;
}

- (nullable NSString *)validatedJSONStringForClaims:(nullable NSSet<GIDClaim *> *)claims
error:(NSError *_Nullable *_Nullable)error {
if (!claims || claims.count == 0) {
return nil;
}

// === Step 1: Check for claims with ambiguous essential property. ===
NSMutableDictionary<NSString *, GIDClaim *> *validClaims =
[[NSMutableDictionary alloc] init];

for (GIDClaim *currentClaim in claims) {
GIDClaim *existingClaim = validClaims[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:kGIDClaimErrorDescription
}];
}
return nil;
}
validClaims[currentClaim.name] = currentClaim;
}

// === Step 2: Build the dictionary structure required for OIDC JSON ===
NSMutableDictionary<NSString *, NSDictionary *> *claimsDictionary =
[[NSMutableDictionary alloc] init];
for (GIDClaim *claim in validClaims.allValues) {
if (claim.isEssential) {
claimsDictionary[claim.name] = @{ kGIDClaimEssentialPropertyKey: @YES };
} else {
claimsDictionary[claim.name] = @{ kGIDClaimEssentialPropertyKey: @NO };
}
}
NSDictionary<NSString *, id> *finalRequestDictionary =
@{ kGIDClaimKeyName: claimsDictionary };

// === Step 3: Serialize the final dictionary into a JSON string ===
return [_jsonSerializer stringWithJSONObject:finalRequestDictionary error:error];
}

@end
38 changes: 38 additions & 0 deletions GoogleSignIn/Sources/GIDJSONSerializer/API/GIDJSONSerializer.h
Original file line number Diff line number Diff line change
@@ -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 <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

/**
* A protocol for serializing an `NSDictionary` into a JSON string.
*/
@protocol GIDJSONSerializer <NSObject>

/**
* 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<NSString *, id> *)jsonObject
error:(NSError *_Nullable *_Nullable)error;

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 <GIDJSONSerializer>

/**
* An error to be returned by `stringWithJSONObject:error:`.
*
* If this property is set, `stringWithJSONObject:error:` will return `nil` and
* populate the error parameter with this error.
*/
@property(nonatomic, nullable) NSError *serializationError;

/** The dictionary passed to the serialization method. */
@property(nonatomic, readonly, nullable) NSDictionary<NSString *, id> *capturedJSONObject;

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -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/Fake/GIDFakeJSONSerializerImpl.h"

#import "GoogleSignIn/Sources/GIDJSONSerializer/Implementation/GIDJSONSerializerImpl.h"
#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h"

@implementation GIDFakeJSONSerializerImpl

- (nullable NSString *)stringWithJSONObject:(NSDictionary<NSString *, id> *)jsonObject
error:(NSError *_Nullable *_Nullable)error {
_capturedJSONObject = [jsonObject copy];

// Check if a serialization error should be simulated.
if (self.serializationError) {
if (error) {
*error = self.serializationError;
}
return nil;
}

// If not failing, fall back to the real serialization path.
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject
options:0
error:error];
if (!jsonData) {
return nil;
}
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

@end
Original file line number Diff line number Diff line change
@@ -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 <GIDJSONSerializer>
@end

NS_ASSUME_NONNULL_END
Loading
Loading