Skip to content
36 changes: 22 additions & 14 deletions GoogleSignIn/Sources/GIDProfileData.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@

#import "GoogleSignIn/Sources/GIDProfileData_Private.h"

#ifdef SWIFT_PACKAGE
@import AppAuth;
#else
#import <AppAuth/AppAuth.h>
#endif

NS_ASSUME_NONNULL_BEGIN

// Key constants used for encode and decode.
Expand Down Expand Up @@ -109,21 +115,23 @@ + (BOOL)supportsSecureCoding {
}

- (nullable instancetype)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
_email = [decoder decodeObjectOfClass:[NSString class] forKey:kEmailKey];
_name = [decoder decodeObjectOfClass:[NSString class] forKey:kNameKey];
_givenName = [decoder decodeObjectOfClass:[NSString class] forKey:kGivenNameKey];
_familyName = [decoder decodeObjectOfClass:[NSString class] forKey:kFamilyNameKey];
_imageURL = [decoder decodeObjectOfClass:[NSURL class] forKey:kImageURLKey];

// Check to see if this is an old archive, if so, try decoding the old image URL string key.
if ([decoder containsValueForKey:kOldImageURLStringKey]) {
_imageURL = [NSURL URLWithString:[decoder decodeObjectOfClass:[NSString class]
forKey:kOldImageURLStringKey]];
}
NSString *email = [decoder decodeObjectOfClass:[NSString class] forKey:kEmailKey];
NSString *name = [decoder decodeObjectOfClass:[NSString class] forKey:kNameKey];
NSString *givenName = [decoder decodeObjectOfClass:[NSString class] forKey:kGivenNameKey];
NSString *familyName = [decoder decodeObjectOfClass:[NSString class] forKey:kFamilyNameKey];
NSURL *imageURL = [decoder decodeObjectOfClass:[NSURL class] forKey:kImageURLKey];

// Check to see if this is an old archive, if so, try decoding the old image URL string key.
if ([decoder containsValueForKey:kOldImageURLStringKey]) {
imageURL = [NSURL URLWithString:[decoder decodeObjectOfClass:[NSString class]
forKey:kOldImageURLStringKey]];
}
return self;

return [self initWithEmail:email
name:name
givenName:givenName
familyName:familyName
imageURL:imageURL];
}

- (void)encodeWithCoder:(NSCoder *)encoder {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2023 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 OIDAuthState;
@class OIDIDToken;
@class GIDProfileData;

NS_ASSUME_NONNULL_BEGIN

@protocol GIDProfileDataFetcher <NSObject>

/// Fetches the latest `GIDProfileData` object.
///
/// This method either extracts profile data from `OIDIDToken` in `OIDAuthState` or fetches it
/// from the UserInfo endpoint.
///
/// @param authState The state of the current OAuth session.
/// @param completion The block that is called on completion asynchronously.
- (void)fetchProfileDataWithAuthState:(OIDAuthState *)authState
completion:(void (^)(GIDProfileData *_Nullable profileData,
NSError *_Nullable error))completion;

/// Fetches a `GIDProfileData` object from an ID token.
///
/// This method returns a `GIDProfileData` object if the ID token is a fat one. Otherwise, returns
/// nil.
///
/// @param idToken The ID token.
- (nullable GIDProfileData *)fetchProfileDataWithIDToken:(OIDIDToken *)idToken;

@end

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

#import "GoogleSignIn/Sources/GIDProfileDataFetcher/API/GIDProfileDataFetcher.h"

@protocol GIDHTTPFetcher;

NS_ASSUME_NONNULL_BEGIN

@interface GIDProfileDataFetcher : NSObject <GIDProfileDataFetcher>

/// The convenience initializer.
- (instancetype)init;

/// The initializer for unit test.
- (instancetype)initWithHTTPFetcher:(id<GIDHTTPFetcher>)httpFetcher NS_DESIGNATED_INITIALIZER;

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2023 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/GIDProfileDataFetcher/Implementations/GIDProfileDataFetcher.h"

#import "GoogleSignIn/Sources/GIDHTTPFetcher/API/GIDHTTPFetcher.h"
#import "GoogleSignIn/Sources/GIDHTTPFetcher/Implementations/GIDHTTPFetcher.h"
#import "GoogleSignIn/Sources/GIDProfileData_Private.h"
#import "GoogleSignIn/Sources/GIDSignInPreferences.h"

#ifdef SWIFT_PACKAGE
@import AppAuth;
@import GTMAppAuth;
#else
#import <AppAuth/AppAuth.h>
#import <GTMAppAuth/GTMAppAuth.h>
#endif

NS_ASSUME_NONNULL_BEGIN

// The URL template for the URL to get user info.
static NSString *const kUserInfoURLTemplate = @"https://%@/oauth2/v3/userinfo";

// Basic profile (Fat ID Token / userinfo endpoint) keys
static NSString *const kBasicProfileEmailKey = @"email";
static NSString *const kBasicProfilePictureKey = @"picture";
static NSString *const kBasicProfileNameKey = @"name";
static NSString *const kBasicProfileGivenNameKey = @"given_name";
static NSString *const kBasicProfileFamilyNameKey = @"family_name";

@implementation GIDProfileDataFetcher {
id<GIDHTTPFetcher> _httpFetcher;
}

- (instancetype)init {
GIDHTTPFetcher *httpFetcher = [[GIDHTTPFetcher alloc] init];
return [self initWithHTTPFetcher:httpFetcher];
}

- (instancetype)initWithHTTPFetcher:(id<GIDHTTPFetcher>)httpFetcher {
self = [super init];
if (self) {
_httpFetcher = httpFetcher;
}
return self;
}

- (void)fetchProfileDataWithAuthState:(OIDAuthState *)authState
completion:(void (^)(GIDProfileData *_Nullable profileData,
NSError *_Nullable error))completion {
OIDIDToken *idToken =
[[OIDIDToken alloc] initWithIDTokenString:authState.lastTokenResponse.idToken];
if (idToken) {
// If we have an ID token, try to extract profile data from it.
GIDProfileData *profileData = [self fetchProfileDataWithIDToken:idToken];
if (profileData) {
completion(profileData, nil);
return;
}
}

// If we can't retrieve profile data from the ID token, make a UserInfo endpoint request to
// fetch it.
NSString *infoString = [NSString stringWithFormat:kUserInfoURLTemplate,
[GIDSignInPreferences googleUserInfoServer]];
NSURL *infoURL = [NSURL URLWithString:infoString];
NSMutableURLRequest *infoRequest = [NSMutableURLRequest requestWithURL:infoURL];
GTMAppAuthFetcherAuthorization *authorization =
[[GTMAppAuthFetcherAuthorization alloc] initWithAuthState:authState];

[_httpFetcher fetchURLRequest:infoRequest
withAuthorizer:authorization
withComment:@"GIDSignIn: fetch basic profile info"
completion:^(NSData *data, NSError *error) {
if (error) {
completion(nil, error);
return;
}
NSError *jsonDeserializationError;
NSDictionary<NSString *, NSString *> *profileDict =
[NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers
error:&jsonDeserializationError];
if (jsonDeserializationError) {
completion(nil, jsonDeserializationError);
return;
}
GIDProfileData *profileData = [[GIDProfileData alloc]
initWithEmail:idToken.claims[kBasicProfileEmailKey]
name:profileDict[kBasicProfileNameKey]
givenName:profileDict[kBasicProfileGivenNameKey]
familyName:profileDict[kBasicProfileFamilyNameKey]
imageURL:[NSURL URLWithString:profileDict[kBasicProfilePictureKey]]];
completion(profileData, nil);
}];
}

- (nullable GIDProfileData*)fetchProfileDataWithIDToken:(OIDIDToken *)idToken {
if (!idToken ||
!idToken.claims[kBasicProfilePictureKey] ||
!idToken.claims[kBasicProfileNameKey] ||
!idToken.claims[kBasicProfileGivenNameKey] ||
!idToken.claims[kBasicProfileFamilyNameKey]) {
return nil;
}

return [[GIDProfileData alloc]
initWithEmail:idToken.claims[kBasicProfileEmailKey]
name:idToken.claims[kBasicProfileNameKey]
givenName:idToken.claims[kBasicProfileGivenNameKey]
familyName:idToken.claims[kBasicProfileFamilyNameKey]
imageURL:[NSURL URLWithString:idToken.claims[kBasicProfilePictureKey]]];
}

@end

NS_ASSUME_NONNULL_END
2 changes: 1 addition & 1 deletion GoogleSignIn/Sources/GIDProfileData_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
name:(NSString *)name
givenName:(nullable NSString *)givenName
familyName:(nullable NSString *)familyName
imageURL:(nullable NSURL *)imageURL;
imageURL:(nullable NSURL *)imageURL NS_DESIGNATED_INITIALIZER;

@end

Expand Down
Loading