Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
9 changes: 5 additions & 4 deletions GoogleSignIn/Sources/GIDEMMSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ NS_ASSUME_NONNULL_BEGIN
completion:(void (^)(NSError *_Nullable))completion;

/// Gets a new set of URL parameters that contains updated EMM-related URL parameters if needed.
+ (NSDictionary *)updatedEMMParametersWithParameters:(NSDictionary *)parameters;
+ (NSDictionary<NSString *,NSString *> *)updatedEMMParametersWithParameters:
(NSDictionary *)parameters;

/// Gets a new set of URL parameters that also contains EMM-related URL parameters if needed.
+ (NSDictionary *)parametersWithParameters:(NSDictionary *)parameters
emmSupport:(nullable NSString *)emmSupport
isPasscodeInfoRequired:(BOOL)isPasscodeInfoRequired;
+ (NSDictionary<NSString *,NSString *> *)parametersWithParameters:(NSDictionary *)parameters
emmSupport:(nullable NSString *)emmSupport
isPasscodeInfoRequired:(BOOL)isPasscodeInfoRequired;

@end

Expand Down
36 changes: 30 additions & 6 deletions GoogleSignIn/Sources/GIDEMMSupport.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,16 @@ + (void)handleTokenFetchEMMError:(nullable NSError *)error
}
}

+ (NSDictionary *)updatedEMMParametersWithParameters:(NSDictionary *)parameters {
+ (NSDictionary<NSString *,NSString *> *)updatedEMMParametersWithParameters:
(NSDictionary *)parameters {
return [self parametersWithParameters:parameters
emmSupport:parameters[kEMMSupportParameterName]
isPasscodeInfoRequired:parameters[kEMMPasscodeInfoParameterName] != nil];
}


+ (NSDictionary *)parametersWithParameters:(NSDictionary *)parameters
emmSupport:(nullable NSString *)emmSupport
isPasscodeInfoRequired:(BOOL)isPasscodeInfoRequired {
+ (NSDictionary<NSString *,NSString *> *)parametersWithParameters:(NSDictionary *)parameters
emmSupport:(nullable NSString *)emmSupport
isPasscodeInfoRequired:(BOOL)isPasscodeInfoRequired {
if (!emmSupport) {
return parameters;
}
Expand All @@ -109,7 +109,7 @@ + (NSDictionary *)parametersWithParameters:(NSDictionary *)parameters
if (isPasscodeInfoRequired) {
allParameters[kEMMPasscodeInfoParameterName] = [GIDMDMPasscodeState passcodeState].info;
}
return allParameters;
return [GIDEMMSupport dictionaryWithStringValuesFromDictionary:allParameters];
}

#pragma mark - GTMAuthSessionDelegate
Expand All @@ -128,6 +128,30 @@ - (void)updateErrorForAuthSession:(GTMAuthSession *)authSession
}];
}

#pragma mark - Private Helpers

+ (NSDictionary<NSString *, NSString *> *)
dictionaryWithStringValuesFromDictionary:(NSDictionary *)originalDictionary {
NSMutableDictionary<NSString *, NSString *> *stringifiedDictionary =
[NSMutableDictionary dictionaryWithCapacity:originalDictionary.count];

[originalDictionary enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
if ([value isKindOfClass:[NSString class]]) {
stringifiedDictionary[key] = value;
return;
}
if ([value isKindOfClass:[NSNumber class]]) {
if (CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) {
stringifiedDictionary[key] = [value boolValue] ? @"true" : @"false";
} else {
stringifiedDictionary[key] = [value stringValue];
}
return;
}
}];
return stringifiedDictionary;
}

@end

NS_ASSUME_NONNULL_END
Expand Down
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
Loading
Loading