Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
30699df
Add KVO in GIDGoogleUser
Alex-4-Git Sep 20, 2022
9ac5478
Observe KVO in GIDGoogleUserTest
Alex-4-Git Sep 20, 2022
26784fe
Utilize the class extension and automatic KVO notifications
Alex-4-Git Sep 22, 2022
f2b0a5f
Merge branch 'GIDGoogleUser-restructure' into pin-AddKVOInGoogleUser
Alex-4-Git Sep 22, 2022
6e5d21a
Only update tokens if necessary
Alex-4-Git Sep 22, 2022
9f32608
Merge branch 'pin-AddKVOInGoogleUser' of https://github.com/google/Go…
Alex-4-Git Sep 22, 2022
d45e398
Remove the KVO testing
Alex-4-Git Sep 22, 2022
a56786b
Test tokens unchanged
Alex-4-Git Sep 23, 2022
58d2acf
Move fetcherAuthorizer from GIDAuthentication to GIDGoogleUser
Alex-4-Git Sep 26, 2022
f843c57
Merge branch 'GIDGoogleUser-restructure' into pin-moveFetcherAuthorizaer
Alex-4-Git Sep 26, 2022
3d29212
Move `fetcherAuthorizer` into GIDGoogleUser API
Alex-4-Git Sep 26, 2022
ca97c30
Create GIDAppAuthFetcherAuthorizationWithEMMSupport class
Alex-4-Git Sep 28, 2022
ad40f6c
Minor improvements.
Alex-4-Git Sep 29, 2022
bd3257d
Update GIDAppAuthFetcherAuthorizationWithEMMSupport.m
Alex-4-Git Sep 29, 2022
f87f063
Create fetcherAuthorizer at initialization
Alex-4-Git Sep 29, 2022
9f9f85a
Fix test failures
Alex-4-Git Sep 29, 2022
c622c97
Suppress the deprecation warnings.
Alex-4-Git Sep 29, 2022
51bb424
Added Utility class GIDEMMSupport to handle EMM related issues.
Alex-4-Git Oct 1, 2022
befe977
Minor improvement.
Alex-4-Git Oct 3, 2022
33705b4
Move `doWithFreshTokens:` into GIDGoogleUser API
Alex-4-Git Oct 3, 2022
0102dc6
Merge branch 'GIDGoogleUser-restructure' into pin-moveDoWithFreshTokens
Alex-4-Git Oct 4, 2022
846f4ce
Minor improvement
Alex-4-Git Oct 4, 2022
70bc182
Add unit tests
Alex-4-Git Oct 6, 2022
ee6f7a4
Minor improvements and move GIDEMMSupportTest to a separate PR.
Alex-4-Git Oct 6, 2022
b9f312a
Resolve comments
Alex-4-Git Oct 7, 2022
6d8575d
Minor improvement.
Alex-4-Git Oct 10, 2022
ded0a37
Resolve comments
Alex-4-Git Oct 11, 2022
7f4aab8
Update authState with OIDAuthorizationResponse
Alex-4-Git Oct 11, 2022
6ee3073
Add unit test for GIDGoogleUser old encoding format
Alex-4-Git Oct 12, 2022
c065b29
Improve testing class GIDGoogleUserOldFormat
Alex-4-Git Oct 13, 2022
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Google LLC
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -14,15 +14,16 @@
* limitations under the License.
*/

#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDAuthentication.h"
#import <Foundation/Foundation.h>

@class OIDAuthState;

NS_ASSUME_NONNULL_BEGIN

// Internal methods for the class that are not part of the public API.
@interface GIDAuthentication ()
// Internal class for GIDGoogleUser NSCoding backward compatibility.
@interface GIDAuthentication : NSObject <NSSecureCoding>

// A representation of the state of the OAuth session for this instance.
@property(nonatomic, readonly) OIDAuthState *authState;
@property(nonatomic) OIDAuthState* authState;

- (instancetype)initWithAuthState:(OIDAuthState *)authState;

Expand Down
240 changes: 20 additions & 220 deletions GoogleSignIn/Sources/GIDAuthentication.m
Original file line number Diff line number Diff line change
@@ -1,240 +1,41 @@
// Copyright 2021 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/GIDAuthentication.h"

#import "GoogleSignIn/Sources/GIDAuthentication_Private.h"

#import "GoogleSignIn/Sources/GIDSignInPreferences.h"

#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
#import "GoogleSignIn/Sources/GIDEMMErrorHandler.h"
#import "GoogleSignIn/Sources/GIDMDMPasscodeState.h"
#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST

#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h"
/*
* Copyright 2022 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/GIDAuthentication.h"

#ifdef SWIFT_PACKAGE
@import AppAuth;
#else
#import <AppAuth/OIDAuthState.h>
#import <AppAuth/OIDAuthorizationRequest.h>
#import <AppAuth/OIDAuthorizationResponse.h>
#import <AppAuth/OIDAuthorizationService.h>
#import <AppAuth/OIDError.h>
#import <AppAuth/OIDIDToken.h>
#import <AppAuth/OIDTokenRequest.h>
#import <AppAuth/OIDTokenResponse.h>
#import <AppAuth/AppAuth.h>
#endif

NS_ASSUME_NONNULL_BEGIN

// Minimal time interval before expiration for the access token or it needs to be refreshed.
NSTimeInterval kMinimalTimeToExpire = 60.0;

// Key constants used for encode and decode.
static NSString *const kAuthStateKey = @"authState";

// Additional parameter names for EMM.
static NSString *const kEMMSupportParameterName = @"emm_support";
static NSString *const kEMMOSVersionParameterName = @"device_os";
static NSString *const kEMMPasscodeInfoParameterName = @"emm_passcode_info";

// Old UIDevice system name for iOS.
static NSString *const kOldIOSSystemName = @"iPhone OS";

// New UIDevice system name for iOS.
static NSString *const kNewIOSSystemName = @"iOS";

@implementation GIDAuthentication {
// A queue for pending authentication handlers so we don't fire multiple requests in parallel.
// Access to this ivar should be synchronized.
NSMutableArray *_authenticationHandlerQueue;
}
@implementation GIDAuthentication

- (instancetype)initWithAuthState:(OIDAuthState *)authState {
if (!authState) {
return nil;
}
self = [super init];
if (self) {
_authenticationHandlerQueue = [[NSMutableArray alloc] init];
_authState = authState;
}
return self;
}

#pragma mark - Public property accessors

- (NSString *)clientID {
return _authState.lastAuthorizationResponse.request.clientID;
}

- (NSString *)accessToken {
return _authState.lastTokenResponse.accessToken;
}

- (NSDate *)accessTokenExpirationDate {
return _authState.lastTokenResponse.accessTokenExpirationDate;
}

- (NSString *)refreshToken {
return _authState.refreshToken;
}

- (nullable NSString *)idToken {
return _authState.lastTokenResponse.idToken;
}

- (nullable NSDate *)idTokenExpirationDate {
return [[[OIDIDToken alloc] initWithIDTokenString:self.idToken] expiresAt];
}

#pragma mark - Public methods

- (void)doWithFreshTokens:(GIDAuthenticationCompletion)completion {
if (!([self.accessTokenExpirationDate timeIntervalSinceNow] < kMinimalTimeToExpire ||
(self.idToken && [self.idTokenExpirationDate timeIntervalSinceNow] < kMinimalTimeToExpire))) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(self, nil);
});
return;
}
@synchronized (_authenticationHandlerQueue) {
// Push the handler into the callback queue.
[_authenticationHandlerQueue addObject:[completion copy]];
if (_authenticationHandlerQueue.count > 1) {
// This is not the first handler in the queue, no fetch is needed.
return;
}
}
// This is the first handler in the queue, a fetch is needed.
NSMutableDictionary *additionalParameters = [@{} mutableCopy];
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
[additionalParameters addEntriesFromDictionary:
[GIDAuthentication updatedEMMParametersWithParameters:
_authState.lastTokenResponse.request.additionalParameters]];
#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST
[additionalParameters addEntriesFromDictionary:
_authState.lastTokenResponse.request.additionalParameters];
#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
additionalParameters[kSDKVersionLoggingParameter] = GIDVersion();
additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment();

OIDTokenRequest *tokenRefreshRequest =
[_authState tokenRefreshRequestWithAdditionalParameters:additionalParameters];
[OIDAuthorizationService performTokenRequest:tokenRefreshRequest
originalAuthorizationResponse:_authState.lastAuthorizationResponse
callback:^(OIDTokenResponse *_Nullable tokenResponse,
NSError *_Nullable error) {
if (tokenResponse) {
[self willChangeValueForKey:NSStringFromSelector(@selector(accessToken))];
[self willChangeValueForKey:NSStringFromSelector(@selector(accessTokenExpirationDate))];
[self willChangeValueForKey:NSStringFromSelector(@selector(idToken))];
[self willChangeValueForKey:NSStringFromSelector(@selector(idTokenExpirationDate))];
[self->_authState updateWithTokenResponse:tokenResponse error:nil];
[self didChangeValueForKey:NSStringFromSelector(@selector(accessToken))];
[self didChangeValueForKey:NSStringFromSelector(@selector(accessTokenExpirationDate))];
[self didChangeValueForKey:NSStringFromSelector(@selector(idToken))];
[self didChangeValueForKey:NSStringFromSelector(@selector(idTokenExpirationDate))];
} else {
if (error.domain == OIDOAuthTokenErrorDomain) {
[self->_authState updateWithAuthorizationError:error];
}
}
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
[GIDAuthentication handleTokenFetchEMMError:error completion:^(NSError *_Nullable error) {
// Process the handler queue to call back.
NSArray *authenticationHandlerQueue;
@synchronized(self->_authenticationHandlerQueue) {
authenticationHandlerQueue = [self->_authenticationHandlerQueue copy];
[self->_authenticationHandlerQueue removeAllObjects];
}
for (GIDAuthenticationCompletion completion in authenticationHandlerQueue) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(error ? nil : self, error);
});
}
}];
#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST
NSArray *authenticationHandlerQueue;
@synchronized(self->_authenticationHandlerQueue) {
authenticationHandlerQueue = [self->_authenticationHandlerQueue copy];
[self->_authenticationHandlerQueue removeAllObjects];
}
for (GIDAuthenticationCompletion completion in authenticationHandlerQueue) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(error ? nil : self, error);
});
}
#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
}];
}

#pragma mark - Private methods

#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST

+ (NSDictionary *)parametersWithParameters:(NSDictionary *)parameters
emmSupport:(nullable NSString *)emmSupport
isPasscodeInfoRequired:(BOOL)isPasscodeInfoRequired {
if (!emmSupport) {
return parameters;
}
NSMutableDictionary *allParameters = [(parameters ?: @{}) mutableCopy];
allParameters[kEMMSupportParameterName] = emmSupport;
UIDevice *device = [UIDevice currentDevice];
NSString *systemName = device.systemName;
if ([systemName isEqualToString:kOldIOSSystemName]) {
systemName = kNewIOSSystemName;
}
allParameters[kEMMOSVersionParameterName] =
[NSString stringWithFormat:@"%@ %@", systemName, device.systemVersion];
if (isPasscodeInfoRequired) {
allParameters[kEMMPasscodeInfoParameterName] = [GIDMDMPasscodeState passcodeState].info;
}
return allParameters;
}

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

+ (void)handleTokenFetchEMMError:(nullable NSError *)error
completion:(void (^)(NSError *_Nullable))completion {
NSDictionary *errorJSON = error.userInfo[OIDOAuthErrorResponseErrorKey];
if (errorJSON) {
__block BOOL handled = NO;
handled = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:errorJSON
completion:^() {
if (handled) {
completion([NSError errorWithDomain:kGIDSignInErrorDomain
code:kGIDSignInErrorCodeEMM
userInfo:error.userInfo]);
} else {
completion(error);
}
}];
} else {
completion(error);
}
}

#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST

#pragma mark - NSSecureCoding

+ (BOOL)supportsSecureCoding {
Expand All @@ -244,14 +45,13 @@ + (BOOL)supportsSecureCoding {
- (nullable instancetype)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
_authenticationHandlerQueue = [[NSMutableArray alloc] init];
_authState = [decoder decodeObjectOfClass:[OIDAuthState class] forKey:kAuthStateKey];
}
return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:_authState forKey:kAuthStateKey];
[encoder encodeObject:self.authState forKey:kAuthStateKey];
}

@end
Expand Down
Loading