From 1463c4a206f6ec1d34bd7e691222875a7b90c5a9 Mon Sep 17 00:00:00 2001 From: pinlu Date: Wed, 4 Jan 2023 09:54:14 -0800 Subject: [PATCH 01/13] Add GIDAuthorizationProcessor API and implementation. --- .../API/GIDAuthorizationFlowProcessor.h | 50 +++++++ .../GIDAuthorizationFlowProcessor.h | 32 +++++ .../GIDAuthorizationFlowProcessor.m | 130 ++++++++++++++++++ GoogleSignIn/Sources/GIDSignIn.m | 13 +- GoogleSignIn/Sources/GIDSignIn_Private.h | 2 + 5 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h create mode 100644 GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h create mode 100644 GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h new file mode 100644 index 00000000..a970d71e --- /dev/null +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h @@ -0,0 +1,50 @@ +/* + * 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 + +NS_ASSUME_NONNULL_BEGIN + +@class GIDSignInInternalOptions; +@class OIDAuthorizationResponse; + +@protocol GIDAuthorizationFlowProcessor + +/// The state of the authorization flow. +@property(nonatomic, readonly, getter=isStarted) BOOL start; + +/// Starts the authorization flow. +/// +/// @param options The @GIDSignInInternalOptions object to provide serverClientID, hostedDomain, +/// clientID, scopes, loginHint and extraParams. +/// @param completion The block that is called on completion asynchronously. +- (void)startWithOptions:(GIDSignInInternalOptions *)options + completion:(void (^)(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *_Nullable error))completion; + +/// Handles the custom scheme URL opened by SFSafariViewController to cleanup UI on iOS 10. +/// +/// @param URL The redirect URL invoked by the server. +/// @return YES if the passed URL matches the expected redirect URL and was consumed, NO otherwise. +- (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)url; + +/// Cancels the authorization flow. +- (void)cancelAuthenticationFlow; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h new file mode 100644 index 00000000..7d399669 --- /dev/null +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h @@ -0,0 +1,32 @@ +/* + * 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 + +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h" + +@class OIDServiceConfiguration; + +NS_ASSUME_NONNULL_BEGIN + +@interface GIDAuthorizationFlowProcessor : NSObject + +- (instancetype)initWithAppAuthConfiguration:(OIDServiceConfiguration *)appAuthConfiguration; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m new file mode 100644 index 00000000..55cdf653 --- /dev/null +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m @@ -0,0 +1,130 @@ +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h" + +#import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDConfiguration.h" + +#import "GoogleSignIn/Sources/GIDEMMSupport.h" +#import "GoogleSignIn/Sources/GIDSignInCallbackSchemes.h" +#import "GoogleSignIn/Sources/GIDSignInInternalOptions.h" +#import "GoogleSignIn/Sources/GIDSignInPreferences.h" + +#ifdef SWIFT_PACKAGE +@import AppAuth; +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +// Expected path in the URL scheme to be handled. +static NSString *const kBrowserCallbackPath = @"/oauth2callback"; + +// The EMM support version +static NSString *const kEMMVersion = @"1"; + +// Parameters for the auth and token exchange endpoints. +static NSString *const kAudienceParameter = @"audience"; + +static NSString *const kIncludeGrantedScopesParameter = @"include_granted_scopes"; +static NSString *const kLoginHintParameter = @"login_hint"; +static NSString *const kHostedDomainParameter = @"hd"; + +@implementation GIDAuthorizationFlowProcessor { + // AppAuth external user-agent session state. + id _currentAuthorizationFlow; + // AppAuth configuration object. + OIDServiceConfiguration *_appAuthConfiguration; +} + +@synthesize start; + +# pragma mark - Public API + +- (BOOL)isStarted { + return _currentAuthorizationFlow != nil; +} + +- (instancetype)initWithAppAuthConfiguration:(OIDServiceConfiguration *)appAuthConfiguration { + self = [super self]; + if (self) { + _appAuthConfiguration = appAuthConfiguration; + } + return self; +} + +- (void)startWithOptions:(GIDSignInInternalOptions *)options + completion:(void (^)(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *_Nullable error))completion { + GIDSignInCallbackSchemes *schemes = + [[GIDSignInCallbackSchemes alloc] initWithClientIdentifier:options.configuration.clientID]; + NSURL *redirectURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@:%@", + [schemes clientIdentifierScheme], + kBrowserCallbackPath]]; + NSString *emmSupport; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + emmSupport = [[self class] isOperatingSystemAtLeast9] ? kEMMVersion : nil; +#elif TARGET_OS_MACCATALYST || TARGET_OS_OSX + emmSupport = nil; +#endif // TARGET_OS_MACCATALYST || TARGET_OS_OSX + + NSMutableDictionary *additionalParameters = [@{} mutableCopy]; + additionalParameters[kIncludeGrantedScopesParameter] = @"true"; + if (options.configuration.serverClientID) { + additionalParameters[kAudienceParameter] = options.configuration.serverClientID; + } + if (options.loginHint) { + additionalParameters[kLoginHintParameter] = options.loginHint; + } + if (options.configuration.hostedDomain) { + additionalParameters[kHostedDomainParameter] = options.configuration.hostedDomain; + } + +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + [additionalParameters addEntriesFromDictionary: + [GIDEMMSupport parametersWithParameters:options.extraParams + emmSupport:emmSupport + isPasscodeInfoRequired:NO]]; +#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST + [additionalParameters addEntriesFromDictionary:options.extraParams]; +#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST + additionalParameters[kSDKVersionLoggingParameter] = GIDVersion(); + additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment(); + + OIDAuthorizationRequest *request = + [[OIDAuthorizationRequest alloc] initWithConfiguration:_appAuthConfiguration + clientId:options.configuration.clientID + scopes:options.scopes + redirectURL:redirectURL + responseType:OIDResponseTypeCode + additionalParameters:additionalParameters]; + _currentAuthorizationFlow = [OIDAuthorizationService + presentAuthorizationRequest:request +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + presentingViewController:options.presentingViewController +#elif TARGET_OS_OSX + presentingWindow:options.presentingWindow +#endif // TARGET_OS_OSX + callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *_Nullable error) { + completion(authorizationResponse, error); + }]; +} + +- (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)url { + return [_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]; +} + +- (void)cancelAuthenticationFlow { + [_currentAuthorizationFlow cancel]; +} + +# pragma mark - Helpers + +- (BOOL)isOperatingSystemAtLeast9 { + NSProcessInfo *processInfo = [NSProcessInfo processInfo]; + return [processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] && + [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = 9}]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index 7904608d..0bd680d9 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -21,6 +21,8 @@ #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDProfileData.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignInResult.h" +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h" +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h" #import "GoogleSignIn/Sources/GIDHTTPFetcher/API/GIDHTTPFetcher.h" #import "GoogleSignIn/Sources/GIDHTTPFetcher/Implementations/GIDHTTPFetcher.h" #import "GoogleSignIn/Sources/GIDEMMSupport.h" @@ -462,12 +464,19 @@ + (GIDSignIn *)sharedInstance { - (id)initPrivate { id keychainHandler = [[GIDKeychainHandler alloc] init]; id httpFetcher = [[GIDHTTPFetcher alloc] init]; + + + // Start from here after taking GIDAppAuthConfiguration out of GIDSignIn.m. + id authorizationFlowProcessor = + [[GIDAuthorizationFlowProcessor alloc] init]; return [self initWithKeychainHandler:keychainHandler - httpFetcher:httpFetcher]; + httpFetcher:httpFetcher + authorizationFlowProcessor:authorizationFlowProcessor]; } - (instancetype)initWithKeychainHandler:(id)keychainHandler - httpFetcher:(id)httpFetcher{ + httpFetcher:(id)httpFetcher + authorizationFlowProcessor:(id)authorizationFlowProcessor { self = [super init]; if (self) { // Get the bundle of the current executable. diff --git a/GoogleSignIn/Sources/GIDSignIn_Private.h b/GoogleSignIn/Sources/GIDSignIn_Private.h index 287e8cbd..0e7e53fa 100644 --- a/GoogleSignIn/Sources/GIDSignIn_Private.h +++ b/GoogleSignIn/Sources/GIDSignIn_Private.h @@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol GIDHTTPFetcher; @protocol GIDKeychainHandler; +@protocol GIDAuthorizationFlowProcessor; /// Represents a completion block that takes a `GIDSignInResult` on success or an error if the /// operation was unsuccessful. @@ -52,6 +53,7 @@ typedef void (^GIDDisconnectCompletion)(NSError *_Nullable error); /// The designated initializer. - (instancetype)initWithKeychainHandler:(id)keychainHandler httpFetcher:(id)HTTPFetcher + authorizationFlowProcessor:(id)authorizationFlowProcessor NS_DESIGNATED_INITIALIZER; /// Authenticates with extra options. From 7003139fc495ddc3b370ad3ab88e6e1c7ddd64a7 Mon Sep 17 00:00:00 2001 From: pinlu Date: Thu, 5 Jan 2023 13:44:59 -0800 Subject: [PATCH 02/13] Use GIDAuthorizationFlowProcessor in GIDSignIn --- .../API/GIDAuthorizationFlowProcessor.h | 2 +- .../GIDAuthorizationFlowProcessor.h | 2 - .../GIDAuthorizationFlowProcessor.m | 45 +++----- GoogleSignIn/Sources/GIDSignIn.m | 106 ++++-------------- GoogleSignIn/Sources/GIDSignInPreferences.h | 6 + GoogleSignIn/Sources/GIDSignInPreferences.m | 10 +- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 7 +- 7 files changed, 56 insertions(+), 122 deletions(-) diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h index a970d71e..bad65188 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h @@ -32,6 +32,7 @@ NS_ASSUME_NONNULL_BEGIN /// clientID, scopes, loginHint and extraParams. /// @param completion The block that is called on completion asynchronously. - (void)startWithOptions:(GIDSignInInternalOptions *)options + emmSupport:(NSString *)emmSupport completion:(void (^)(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error))completion; @@ -47,4 +48,3 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END - diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h index 7d399669..9320e72a 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h @@ -24,8 +24,6 @@ NS_ASSUME_NONNULL_BEGIN @interface GIDAuthorizationFlowProcessor : NSObject -- (instancetype)initWithAppAuthConfiguration:(OIDServiceConfiguration *)appAuthConfiguration; - @end NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m index 55cdf653..055a9a73 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m @@ -15,12 +15,6 @@ NS_ASSUME_NONNULL_BEGIN -// Expected path in the URL scheme to be handled. -static NSString *const kBrowserCallbackPath = @"/oauth2callback"; - -// The EMM support version -static NSString *const kEMMVersion = @"1"; - // Parameters for the auth and token exchange endpoints. static NSString *const kAudienceParameter = @"audience"; @@ -43,15 +37,8 @@ - (BOOL)isStarted { return _currentAuthorizationFlow != nil; } -- (instancetype)initWithAppAuthConfiguration:(OIDServiceConfiguration *)appAuthConfiguration { - self = [super self]; - if (self) { - _appAuthConfiguration = appAuthConfiguration; - } - return self; -} - - (void)startWithOptions:(GIDSignInInternalOptions *)options + emmSupport:(NSString *)emmSupport completion:(void (^)(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error))completion { GIDSignInCallbackSchemes *schemes = @@ -59,12 +46,6 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options NSURL *redirectURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@:%@", [schemes clientIdentifierScheme], kBrowserCallbackPath]]; - NSString *emmSupport; -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - emmSupport = [[self class] isOperatingSystemAtLeast9] ? kEMMVersion : nil; -#elif TARGET_OS_MACCATALYST || TARGET_OS_OSX - emmSupport = nil; -#endif // TARGET_OS_MACCATALYST || TARGET_OS_OSX NSMutableDictionary *additionalParameters = [@{} mutableCopy]; additionalParameters[kIncludeGrantedScopesParameter] = @"true"; @@ -88,9 +69,15 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options #endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST additionalParameters[kSDKVersionLoggingParameter] = GIDVersion(); additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment(); + + NSURL *authorizationEndpointURL = [GIDSignInPreferences authorizationEndpointURL]; + NSURL *tokenEndpointURL = [GIDSignInPreferences tokenEndpointURL]; + OIDServiceConfiguration *appAuthConfiguration = + [[OIDServiceConfiguration alloc] initWithAuthorizationEndpoint:authorizationEndpointURL + tokenEndpoint:tokenEndpointURL]; OIDAuthorizationRequest *request = - [[OIDAuthorizationRequest alloc] initWithConfiguration:_appAuthConfiguration + [[OIDAuthorizationRequest alloc] initWithConfiguration:appAuthConfiguration clientId:options.configuration.clientID scopes:options.scopes redirectURL:redirectURL @@ -110,19 +97,17 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options } - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)url { - return [_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]; + if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) { + _currentAuthorizationFlow = nil; + return YES; + } else { + return NO; + } } - (void)cancelAuthenticationFlow { [_currentAuthorizationFlow cancel]; -} - -# pragma mark - Helpers - -- (BOOL)isOperatingSystemAtLeast9 { - NSProcessInfo *processInfo = [NSProcessInfo processInfo]; - return [processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] && - [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = 9}]; + _currentAuthorizationFlow = nil; } @end diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index 3502b325..b5a3e0e0 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -82,9 +82,6 @@ // The URL template for the URL to revoke the token. static NSString *const kRevokeTokenURLTemplate = @"https://%@/o/oauth2/revoke"; -// Expected path in the URL scheme to be handled. -static NSString *const kBrowserCallbackPath = @"/oauth2callback"; - // Expected path for EMM callback. static NSString *const kEMMCallbackPath = @"/emmcallback"; @@ -126,9 +123,6 @@ static NSString *const kAudienceParameter = @"audience"; // See b/11669751 . static NSString *const kOpenIDRealmParameter = @"openid.realm"; -static NSString *const kIncludeGrantedScopesParameter = @"include_granted_scopes"; -static NSString *const kLoginHintParameter = @"login_hint"; -static NSString *const kHostedDomainParameter = @"hd"; // Minimum time to expiration for a restored access token. static const NSTimeInterval kMinimumRestoredAccessTokenTimeToExpire = 600.0; @@ -158,8 +152,6 @@ @implementation GIDSignIn { // represent a sign in continuation. GIDSignInInternalOptions *_currentOptions; - // AppAuth external user-agent session state. - id _currentAuthorizationFlow; // Flag to indicate that the auth flow is restarting. BOOL _restarting; @@ -167,6 +159,9 @@ @implementation GIDSignIn { // The class to fetches data from a url end point. id _httpFetcher; + + // The class to control the authorization flow. + id _authorizationFlowProcessor; } #pragma mark - Public methods @@ -178,8 +173,7 @@ @implementation GIDSignIn { - (BOOL)handleURL:(NSURL *)url { // Check if the callback path matches the expected one for a URL from Safari/Chrome/SafariVC. if ([url.path isEqual:kBrowserCallbackPath]) { - if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) { - _currentAuthorizationFlow = nil; + if ([_authorizationFlowProcessor resumeExternalUserAgentFlowWithURL:url]) { return YES; } return NO; @@ -251,12 +245,12 @@ - (void)signInWithPresentingViewController:(UIViewController *)presentingViewCon additionalScopes:(nullable NSArray *)additionalScopes completion:(nullable GIDSignInCompletion)completion { GIDSignInInternalOptions *options = - [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration - presentingViewController:presentingViewController - loginHint:hint - addScopesFlow:NO - scopes:additionalScopes - completion:completion]; + [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration + presentingViewController:presentingViewController + loginHint:hint + addScopesFlow:NO + scopes:additionalScopes + completion:completion]; [self signInWithOptions:options]; } @@ -329,12 +323,12 @@ - (void)signInWithPresentingWindow:(NSWindow *)presentingWindow additionalScopes:(nullable NSArray *)additionalScopes completion:(nullable GIDSignInCompletion)completion { GIDSignInInternalOptions *options = - [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration - presentingWindow:presentingWindow - loginHint:hint - addScopesFlow:NO - scopes:additionalScopes - completion:completion]; + [GIDSignInInternalOptions defaultOptionsWithConfiguration:_configuration + presentingWindow:presentingWindow + loginHint:hint + addScopesFlow:NO + scopes:additionalScopes + completion:completion]; [self signInWithOptions:options]; } @@ -457,11 +451,8 @@ + (GIDSignIn *)sharedInstance { - (id)initPrivate { id keychainHandler = [[GIDKeychainHandler alloc] init]; id httpFetcher = [[GIDHTTPFetcher alloc] init]; - - - // Start from here after taking GIDAppAuthConfiguration out of GIDSignIn.m. id authorizationFlowProcessor = - [[GIDAuthorizationFlowProcessor alloc] init]; + [[GIDAuthorizationFlowProcessor alloc] init]; return [self initWithKeychainHandler:keychainHandler httpFetcher:httpFetcher authorizationFlowProcessor:authorizationFlowProcessor]; @@ -499,6 +490,7 @@ - (instancetype)initWithKeychainHandler:(id)keychainHandler _keychainHandler = keychainHandler; _httpFetcher = httpFetcher; + _authorizationFlowProcessor = authorizationFlowProcessor; } return self; } @@ -524,7 +516,6 @@ - (void)signInWithOptions:(GIDSignInInternalOptions *)options { // Explicitly throw exception for missing client ID here. This must come before // scheme check because schemes rely on reverse client IDs. [self assertValidParameters]; - [self assertValidPresentingViewController]; // If the application does not support the required URL schemes tell the developer so. @@ -563,64 +554,17 @@ - (void)signInWithOptions:(GIDSignInInternalOptions *)options { #pragma mark - Authentication flow - (void)authenticateInteractivelyWithOptions:(GIDSignInInternalOptions *)options { - GIDSignInCallbackSchemes *schemes = - [[GIDSignInCallbackSchemes alloc] initWithClientIdentifier:options.configuration.clientID]; - NSURL *redirectURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@:%@", - [schemes clientIdentifierScheme], - kBrowserCallbackPath]]; NSString *emmSupport; #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST emmSupport = [[self class] isOperatingSystemAtLeast9] ? kEMMVersion : nil; #elif TARGET_OS_MACCATALYST || TARGET_OS_OSX emmSupport = nil; #endif // TARGET_OS_MACCATALYST || TARGET_OS_OSX - - NSMutableDictionary *additionalParameters = [@{} mutableCopy]; - additionalParameters[kIncludeGrantedScopesParameter] = @"true"; - if (options.configuration.serverClientID) { - additionalParameters[kAudienceParameter] = options.configuration.serverClientID; - } - if (options.loginHint) { - additionalParameters[kLoginHintParameter] = options.loginHint; - } - if (options.configuration.hostedDomain) { - additionalParameters[kHostedDomainParameter] = options.configuration.hostedDomain; - } - -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - [additionalParameters addEntriesFromDictionary: - [GIDEMMSupport parametersWithParameters:options.extraParams - emmSupport:emmSupport - isPasscodeInfoRequired:NO]]; -#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST - [additionalParameters addEntriesFromDictionary:options.extraParams]; -#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST - additionalParameters[kSDKVersionLoggingParameter] = GIDVersion(); - additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment(); - NSURL *authorizationEndpointURL = [GIDSignInPreferences authorizationEndpointURL]; - NSURL *tokenEndpointURL = [GIDSignInPreferences tokenEndpointURL]; - OIDServiceConfiguration *appAuthConfiguration = - [[OIDServiceConfiguration alloc] initWithAuthorizationEndpoint:authorizationEndpointURL - tokenEndpoint:tokenEndpointURL]; - - OIDAuthorizationRequest *request = - [[OIDAuthorizationRequest alloc] initWithConfiguration:appAuthConfiguration - clientId:options.configuration.clientID - scopes:options.scopes - redirectURL:redirectURL - responseType:OIDResponseTypeCode - additionalParameters:additionalParameters]; - - _currentAuthorizationFlow = [OIDAuthorizationService - presentAuthorizationRequest:request -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - presentingViewController:options.presentingViewController -#elif TARGET_OS_OSX - presentingWindow:options.presentingWindow -#endif // TARGET_OS_OSX - callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, - NSError *_Nullable error) { + [_authorizationFlowProcessor startWithOptions:options + emmSupport:(NSString *)emmSupport + completion:^(OIDAuthorizationResponse *authorizationResponse, + NSError *error) { [self processAuthorizationResponse:authorizationResponse error:error emmSupport:emmSupport]; @@ -689,7 +633,6 @@ - (void)processAuthorizationResponse:(OIDAuthorizationResponse *)authorizationRe // Perform authentication with the provided options. - (void)authenticateWithOptions:(GIDSignInInternalOptions *)options { - // If this is an interactive flow, we're not going to try to restore any saved auth state. if (options.interactive) { [self authenticateInteractivelyWithOptions:options]; @@ -910,12 +853,11 @@ - (BOOL)handleDevicePolicyAppURL:(NSURL *)url { return NO; } #endif // TARGET_OS_OSX - if (!_currentAuthorizationFlow) { + if (!_authorizationFlowProcessor.isStarted) { return NO; } _restarting = YES; - [_currentAuthorizationFlow cancel]; - _currentAuthorizationFlow = nil; + [_authorizationFlowProcessor cancelAuthenticationFlow]; _restarting = NO; NSDictionary *extraParameters = @{ kEMMRestartAuthParameter : @"1" }; // In iOS 13 the presentation of ASWebAuthenticationSession needs an anchor window, diff --git a/GoogleSignIn/Sources/GIDSignInPreferences.h b/GoogleSignIn/Sources/GIDSignInPreferences.h index 3d630f77..5a7654eb 100644 --- a/GoogleSignIn/Sources/GIDSignInPreferences.h +++ b/GoogleSignIn/Sources/GIDSignInPreferences.h @@ -18,9 +18,15 @@ NS_ASSUME_NONNULL_BEGIN +// The name of the query parameter used for logging the SDK version. extern NSString *const kSDKVersionLoggingParameter; + +// The name of the query parameter used for logging the Apple execution environment. extern NSString *const kEnvironmentLoggingParameter; +// Expected path in the URL scheme to be handled. +extern NSString *const kBrowserCallbackPath; + NSString* GIDVersion(void); NSString* GIDEnvironment(void); diff --git a/GoogleSignIn/Sources/GIDSignInPreferences.m b/GoogleSignIn/Sources/GIDSignInPreferences.m index 7c4aa159..366f3bbb 100644 --- a/GoogleSignIn/Sources/GIDSignInPreferences.m +++ b/GoogleSignIn/Sources/GIDSignInPreferences.m @@ -16,16 +16,14 @@ NS_ASSUME_NONNULL_BEGIN +NSString *const kSDKVersionLoggingParameter = @"gpsdk"; +NSString *const kEnvironmentLoggingParameter = @"gidenv"; +NSString *const kBrowserCallbackPath = @"/oauth2callback"; + static NSString *const kLSOServer = @"accounts.google.com"; static NSString *const kTokenServer = @"oauth2.googleapis.com"; static NSString *const kUserInfoServer = @"www.googleapis.com"; -// The name of the query parameter used for logging the SDK version. -NSString *const kSDKVersionLoggingParameter = @"gpsdk"; - -// The name of the query parameter used for logging the Apple execution environment. -NSString *const kEnvironmentLoggingParameter = @"gidenv"; - // Supported Apple execution environments static NSString *const kAppleEnvironmentUnknown = @"unknown"; static NSString *const kAppleEnvironmentIOS = @"ios"; diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index fd13de71..7cb5fee1 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -26,6 +26,7 @@ // Test module imports @import GoogleSignIn; +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h" #import "GoogleSignIn/Sources/GIDEMMSupport.h" #import "GoogleSignIn/Sources/GIDGoogleUser_Private.h" #import "GoogleSignIn/Sources/GIDSignIn_Private.h" @@ -311,8 +312,12 @@ - (void)setUp { _httpFetcher = [[GIDFakeHTTPFetcher alloc] init]; + GIDAuthorizationFlowProcessor * authorizationFlowProcessor = + [[GIDAuthorizationFlowProcessor alloc] init]; + _signIn = [[GIDSignIn alloc] initWithKeychainHandler:_keychainHandler - httpFetcher:_httpFetcher]; + httpFetcher:_httpFetcher + authorizationFlowProcessor:authorizationFlowProcessor]; _hint = nil; __weak GIDSignInTest *weakSelf = self; From 0e80a9db3826b9b3c937eded1865b4528d64d4a2 Mon Sep 17 00:00:00 2001 From: pinlu Date: Thu, 5 Jan 2023 17:07:45 -0800 Subject: [PATCH 03/13] Add test for GIDAuthorizationFlowProcessor. --- .../API/GIDAuthorizationFlowProcessor.h | 4 +- .../GIDAuthorizationFlowProcessor.m | 28 +++- .../Unit/GIDAuthorizationFlowProcessorTest.m | 123 ++++++++++++++++++ 3 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h index bad65188..365270f9 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h @@ -32,13 +32,13 @@ NS_ASSUME_NONNULL_BEGIN /// clientID, scopes, loginHint and extraParams. /// @param completion The block that is called on completion asynchronously. - (void)startWithOptions:(GIDSignInInternalOptions *)options - emmSupport:(NSString *)emmSupport + emmSupport:(nullable NSString *)emmSupport completion:(void (^)(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error))completion; /// Handles the custom scheme URL opened by SFSafariViewController to cleanup UI on iOS 10. /// -/// @param URL The redirect URL invoked by the server. +/// @param url The redirect URL invoked by the server. /// @return YES if the passed URL matches the expected redirect URL and was consumed, NO otherwise. - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)url; diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m index 055a9a73..e8cecee6 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m @@ -1,3 +1,19 @@ +/* + * 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/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h" #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDConfiguration.h" @@ -38,14 +54,14 @@ - (BOOL)isStarted { } - (void)startWithOptions:(GIDSignInInternalOptions *)options - emmSupport:(NSString *)emmSupport + emmSupport:(nullable NSString *)emmSupport completion:(void (^)(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error))completion { GIDSignInCallbackSchemes *schemes = [[GIDSignInCallbackSchemes alloc] initWithClientIdentifier:options.configuration.clientID]; - NSURL *redirectURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@:%@", - [schemes clientIdentifierScheme], - kBrowserCallbackPath]]; + NSString *urlString = [NSString stringWithFormat:@"%@:%@", + [schemes clientIdentifierScheme], kBrowserCallbackPath]; + NSURL *redirectURL = [NSURL URLWithString:urlString]; NSMutableDictionary *additionalParameters = [@{} mutableCopy]; additionalParameters[kIncludeGrantedScopesParameter] = @"true"; @@ -90,8 +106,8 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options #elif TARGET_OS_OSX presentingWindow:options.presentingWindow #endif // TARGET_OS_OSX - callback:^(OIDAuthorizationResponse *_Nullable authorizationResponse, - NSError *_Nullable error) { + callback:^(OIDAuthorizationResponse *authorizationResponse, + NSError *error) { completion(authorizationResponse, error); }]; } diff --git a/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m b/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m new file mode 100644 index 00000000..cd7f945b --- /dev/null +++ b/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m @@ -0,0 +1,123 @@ +// 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/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h" + +#import "GoogleSignIn/Sources/GIDSignInInternalOptions.h" +#import "GoogleSignIn/Tests/Unit/OIDAuthorizationResponse+Testing.h" + +#import + +#ifdef SWIFT_PACKAGE +@import AppAuth; +@import OCMock; +#else +#import +#import +#endif + +static NSString *const kFakeURL = @"www.fakeURL.com"; +static NSString *const kErrorDomain = @"ERROR_DOMAIN"; +static NSInteger const kErrorCode = 400; + +@interface GIDAuthorizationFlowProcessorTest : XCTestCase { + GIDAuthorizationFlowProcessor *_authorizationFlowProcessor; + id _authorizationServiceMock; + id _externalUserAgentSession; +} + +@end + +@implementation GIDAuthorizationFlowProcessorTest + +- (void)setUp { + [super setUp]; + + _authorizationFlowProcessor = [[GIDAuthorizationFlowProcessor alloc] init]; + _externalUserAgentSession = OCMProtocolMock(@protocol(OIDExternalUserAgentSession)); + _authorizationServiceMock = OCMClassMock([OIDAuthorizationService class]); + OIDAuthorizationResponse *response = [OIDAuthorizationResponse testInstance]; + NSError *error = [self error]; + OCMStub([_authorizationServiceMock + presentAuthorizationRequest:[OCMArg any] +#if TARGET_OS_IOS || TARGET_OS_MACCATALYST + presentingViewController:[OCMArg any] +#elif TARGET_OS_OSX + presentingWindow:[OCMArg any] +#endif // TARGET_OS_OSX + callback:([OCMArg invokeBlockWithArgs:response, error, nil]) + ]).andReturn(_externalUserAgentSession); +} + +- (void)testStartAndCancelAuthorizationFlow { + XCTestExpectation *expectation = [self expectationWithDescription:@"completion is invoked."]; + GIDSignInInternalOptions *options = [[GIDSignInInternalOptions alloc] init]; + [_authorizationFlowProcessor startWithOptions:options + emmSupport:nil + completion:^(OIDAuthorizationResponse *authorizationResponse, + NSError *error) { + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(_authorizationFlowProcessor.isStarted); + + [_authorizationFlowProcessor cancelAuthenticationFlow]; + XCTAssertFalse(_authorizationFlowProcessor.isStarted); +} + +- (void)testStartAndResumeAuthorizationFlow_success { + OCMStub([_externalUserAgentSession resumeExternalUserAgentFlowWithURL:[OCMArg any]]) + .andReturn(YES); + XCTestExpectation *expectation = [self expectationWithDescription:@"completion is invoked."]; + GIDSignInInternalOptions *options = [[GIDSignInInternalOptions alloc] init]; + [_authorizationFlowProcessor startWithOptions:options + emmSupport:nil + completion:^(OIDAuthorizationResponse *authorizationResponse, + NSError *error) { + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(_authorizationFlowProcessor.isStarted); + + NSURL *url = [[NSURL alloc] initWithString:kFakeURL]; + [_authorizationFlowProcessor resumeExternalUserAgentFlowWithURL:url]; + XCTAssertFalse(_authorizationFlowProcessor.isStarted); +} + +- (void)testStartAndResumeAuthorizationFlow_fail { + OCMStub([_externalUserAgentSession resumeExternalUserAgentFlowWithURL:[OCMArg any]]) + .andReturn(NO); + XCTestExpectation *expectation = [self expectationWithDescription:@"completion is invoked."]; + GIDSignInInternalOptions *options = [[GIDSignInInternalOptions alloc] init]; + [_authorizationFlowProcessor startWithOptions:options + emmSupport:nil + completion:^(OIDAuthorizationResponse *authorizationResponse, + NSError *error) { + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; + XCTAssertTrue(_authorizationFlowProcessor.isStarted); + + NSURL *url = [[NSURL alloc] initWithString:kFakeURL]; + [_authorizationFlowProcessor resumeExternalUserAgentFlowWithURL:url]; + XCTAssertTrue(_authorizationFlowProcessor.isStarted); +} + +#pragma mark - Helpers + +- (NSError *)error { + return [NSError errorWithDomain:kErrorDomain code:kErrorCode userInfo:nil]; +} + +@end From ec80960856cc51e0894d2bc6c17052d3b8f54c04 Mon Sep 17 00:00:00 2001 From: pinlu Date: Fri, 6 Jan 2023 10:22:38 -0800 Subject: [PATCH 04/13] Improve style. --- .../API/GIDAuthorizationFlowProcessor.h | 1 + .../GIDAuthorizationFlowProcessor.m | 5 +---- GoogleSignIn/Sources/GIDSignIn.m | 2 +- .../Tests/Unit/GIDAuthorizationFlowProcessorTest.m | 14 +++++++------- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h index 365270f9..e68367b2 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h @@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN /// /// @param options The @GIDSignInInternalOptions object to provide serverClientID, hostedDomain, /// clientID, scopes, loginHint and extraParams. +/// @param emmSupport The EMM support info string. /// @param completion The block that is called on completion asynchronously. - (void)startWithOptions:(GIDSignInInternalOptions *)options emmSupport:(nullable NSString *)emmSupport diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m index e8cecee6..dff26169 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.m @@ -45,8 +45,6 @@ @implementation GIDAuthorizationFlowProcessor { OIDServiceConfiguration *_appAuthConfiguration; } -@synthesize start; - # pragma mark - Public API - (BOOL)isStarted { @@ -74,7 +72,6 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options if (options.configuration.hostedDomain) { additionalParameters[kHostedDomainParameter] = options.configuration.hostedDomain; } - #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST [additionalParameters addEntriesFromDictionary: [GIDEMMSupport parametersWithParameters:options.extraParams @@ -91,7 +88,6 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options OIDServiceConfiguration *appAuthConfiguration = [[OIDServiceConfiguration alloc] initWithAuthorizationEndpoint:authorizationEndpointURL tokenEndpoint:tokenEndpointURL]; - OIDAuthorizationRequest *request = [[OIDAuthorizationRequest alloc] initWithConfiguration:appAuthConfiguration clientId:options.configuration.clientID @@ -99,6 +95,7 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options redirectURL:redirectURL responseType:OIDResponseTypeCode additionalParameters:additionalParameters]; + _currentAuthorizationFlow = [OIDAuthorizationService presentAuthorizationRequest:request #if TARGET_OS_IOS || TARGET_OS_MACCATALYST diff --git a/GoogleSignIn/Sources/GIDSignIn.m b/GoogleSignIn/Sources/GIDSignIn.m index b5a3e0e0..8fddd8e4 100644 --- a/GoogleSignIn/Sources/GIDSignIn.m +++ b/GoogleSignIn/Sources/GIDSignIn.m @@ -452,7 +452,7 @@ - (id)initPrivate { id keychainHandler = [[GIDKeychainHandler alloc] init]; id httpFetcher = [[GIDHTTPFetcher alloc] init]; id authorizationFlowProcessor = - [[GIDAuthorizationFlowProcessor alloc] init]; + [[GIDAuthorizationFlowProcessor alloc] init]; return [self initWithKeychainHandler:keychainHandler httpFetcher:httpFetcher authorizationFlowProcessor:authorizationFlowProcessor]; diff --git a/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m b/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m index cd7f945b..ffa9dc61 100644 --- a/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m +++ b/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m @@ -60,7 +60,7 @@ - (void)setUp { ]).andReturn(_externalUserAgentSession); } -- (void)testStartAndCancelAuthorizationFlow { +- (void)testStartAndCancelAuthorizationFlow_success { XCTestExpectation *expectation = [self expectationWithDescription:@"completion is invoked."]; GIDSignInInternalOptions *options = [[GIDSignInInternalOptions alloc] init]; [_authorizationFlowProcessor startWithOptions:options @@ -77,8 +77,6 @@ - (void)testStartAndCancelAuthorizationFlow { } - (void)testStartAndResumeAuthorizationFlow_success { - OCMStub([_externalUserAgentSession resumeExternalUserAgentFlowWithURL:[OCMArg any]]) - .andReturn(YES); XCTestExpectation *expectation = [self expectationWithDescription:@"completion is invoked."]; GIDSignInInternalOptions *options = [[GIDSignInInternalOptions alloc] init]; [_authorizationFlowProcessor startWithOptions:options @@ -90,14 +88,14 @@ - (void)testStartAndResumeAuthorizationFlow_success { [self waitForExpectationsWithTimeout:1 handler:nil]; XCTAssertTrue(_authorizationFlowProcessor.isStarted); + OCMStub([_externalUserAgentSession resumeExternalUserAgentFlowWithURL:[OCMArg any]]) + .andReturn(YES); NSURL *url = [[NSURL alloc] initWithString:kFakeURL]; [_authorizationFlowProcessor resumeExternalUserAgentFlowWithURL:url]; XCTAssertFalse(_authorizationFlowProcessor.isStarted); } -- (void)testStartAndResumeAuthorizationFlow_fail { - OCMStub([_externalUserAgentSession resumeExternalUserAgentFlowWithURL:[OCMArg any]]) - .andReturn(NO); +- (void)testStartAndFailToResumeAuthorizationFlow { XCTestExpectation *expectation = [self expectationWithDescription:@"completion is invoked."]; GIDSignInInternalOptions *options = [[GIDSignInInternalOptions alloc] init]; [_authorizationFlowProcessor startWithOptions:options @@ -108,7 +106,9 @@ - (void)testStartAndResumeAuthorizationFlow_fail { }]; [self waitForExpectationsWithTimeout:1 handler:nil]; XCTAssertTrue(_authorizationFlowProcessor.isStarted); - + + OCMStub([_externalUserAgentSession resumeExternalUserAgentFlowWithURL:[OCMArg any]]) + .andReturn(NO); NSURL *url = [[NSURL alloc] initWithString:kFakeURL]; [_authorizationFlowProcessor resumeExternalUserAgentFlowWithURL:url]; XCTAssertTrue(_authorizationFlowProcessor.isStarted); From 3564b013c2a554729ca7ac97ecf37d19103f7f40 Mon Sep 17 00:00:00 2001 From: pinlu Date: Sat, 7 Jan 2023 08:39:03 -0800 Subject: [PATCH 05/13] Add GIDFakeAuthorizationFlowProcessor --- .../Fakes/GIDFakeAuthorizationFlowProcessor.h | 45 +++++++++++++++++++ .../Fakes/GIDFakeAuthorizationFlowProcessor.m | 34 ++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h create mode 100644 GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h new file mode 100644 index 00000000..f03a302e --- /dev/null +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h @@ -0,0 +1,45 @@ +/* + * 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 + +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h" + +NS_ASSUME_NONNULL_BEGIN + +/// The block which provides the response for the method `startWithOptions:emmSupport:completion:`. +/// +/// @param data The OIDAuthorizationResponse object returned if succeed, +/// @param error The error returned if failed. +typedef void(^GIDAuthorizationFlowProcessorFakeResponseProviderBlock) + (OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error); + +/// The block to set up data based on the input request for the method +/// `startWithOptions:emmSupport:completion:`. +/// +/// @param responseProvider The block which provides the response. +typedef void (^GIDAuthorizationFlowProcessorTestBlock) + (GIDAuthorizationFlowProcessorFakeResponseProviderBlock responseProvider); + +@interface GIDFakeAuthorizationFlowProcessor : NSObject + +/// Set the test block which provides the response value. +- (void)setTestBlock:(GIDAuthorizationFlowProcessorTestBlock)block; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m new file mode 100644 index 00000000..835adc7e --- /dev/null +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m @@ -0,0 +1,34 @@ +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h" + +@interface GIDFakeAuthorizationFlowProcessor () + +@property(nonatomic) GIDAuthorizationFlowProcessorTestBlock testBlock; + +@end + +@implementation GIDFakeAuthorizationFlowProcessor + +- (void)startWithOptions:(GIDSignInInternalOptions *)options + emmSupport:(nullable NSString *)emmSupport + completion:(void (^)(OIDAuthorizationResponse *_Nullable authorizationResponse, + NSError *_Nullable error))completion { + NSAssert(self.testBlock != nil, @"Set the test block before invoking this method."); + self.testBlock(^(OIDAuthorizationResponse *authorizationResponse, NSError *error) { + completion(authorizationResponse, error); + }); +} + +/// Not used in test for now. +- (BOOL)isStarted { + return YES; +} + +/// Not used in test for now. +- (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)url { + return YES; +} + +/// Not used in test for now. +- (void)cancelAuthenticationFlow {} + +@end From 69aa8208bc7b3eaa66cc407809b5f9a9346a1a7d Mon Sep 17 00:00:00 2001 From: pinlu Date: Wed, 11 Jan 2023 16:02:45 -0800 Subject: [PATCH 06/13] Blocked by another PR to create and test OIDAuthorizationRequest. --- .../Fakes/GIDFakeAuthorizationFlowProcessor.h | 15 +++- .../Fakes/GIDFakeAuthorizationFlowProcessor.m | 17 ++-- .../Unit/GIDAuthorizationFlowProcessorTest.m | 1 - GoogleSignIn/Tests/Unit/GIDSignInTest.m | 80 ++++++++++--------- 4 files changed, 62 insertions(+), 51 deletions(-) diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h index f03a302e..62c9a270 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h @@ -18,16 +18,22 @@ #import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h" +#ifdef SWIFT_PACKAGE +@import AppAuth; +#else +#import +#endif + NS_ASSUME_NONNULL_BEGIN /// The block which provides the response for the method `startWithOptions:emmSupport:completion:`. /// -/// @param data The OIDAuthorizationResponse object returned if succeed, +/// @param authorizationResponse The OIDAuthorizationResponse object returned if succeeded. /// @param error The error returned if failed. typedef void(^GIDAuthorizationFlowProcessorFakeResponseProviderBlock) (OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error); -/// The block to set up data based on the input request for the method +/// The block to set up response value for the method /// `startWithOptions:emmSupport:completion:`. /// /// @param responseProvider The block which provides the response. @@ -36,8 +42,9 @@ typedef void (^GIDAuthorizationFlowProcessorTestBlock) @interface GIDFakeAuthorizationFlowProcessor : NSObject -/// Set the test block which provides the response value. -- (void)setTestBlock:(GIDAuthorizationFlowProcessorTestBlock)block; +/// The test block which provides the response value. +@property(nonatomic) GIDAuthorizationFlowProcessorTestBlock testBlock; + @end diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m index 835adc7e..49ac355a 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m @@ -1,11 +1,5 @@ #import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h" -@interface GIDFakeAuthorizationFlowProcessor () - -@property(nonatomic) GIDAuthorizationFlowProcessorTestBlock testBlock; - -@end - @implementation GIDFakeAuthorizationFlowProcessor - (void)startWithOptions:(GIDSignInInternalOptions *)options @@ -13,22 +7,25 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options completion:(void (^)(OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error))completion { NSAssert(self.testBlock != nil, @"Set the test block before invoking this method."); + + self.testBlock(^(OIDAuthorizationResponse *authorizationResponse, NSError *error) { completion(authorizationResponse, error); }); } -/// Not used in test for now. - (BOOL)isStarted { + NSAssert(NO, @"Not implemented."); return YES; } -/// Not used in test for now. - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)url { + NSAssert(NO, @"Not implemented."); return YES; } -/// Not used in test for now. -- (void)cancelAuthenticationFlow {} +- (void)cancelAuthenticationFlow { + NSAssert(NO, @"Not implemented."); +} @end diff --git a/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m b/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m index 0d250421..b1c5c756 100644 --- a/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m +++ b/GoogleSignIn/Tests/Unit/GIDAuthorizationFlowProcessorTest.m @@ -92,7 +92,6 @@ - (void)testStartAndResumeAuthorizationFlow_success { emmSupport:nil completion:^(OIDAuthorizationResponse *authorizationResponse, NSError *error) { -< XCTAssertEqualObjects(authorizationResponse.accessToken, self->_fakeResponse.accessToken); XCTAssertEqualObjects(authorizationResponse.authorizationCode, diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index d0fc72fa..5b5a5a84 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -26,7 +26,7 @@ // Test module imports @import GoogleSignIn; -#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/GIDAuthorizationFlowProcessor.h" +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h" #import "GoogleSignIn/Sources/GIDEMMSupport.h" #import "GoogleSignIn/Sources/GIDGoogleUser_Private.h" #import "GoogleSignIn/Sources/GIDSignIn_Private.h" @@ -36,7 +36,6 @@ #import "GoogleSignIn/Sources/GIDHTTPFetcher/Implementations/GIDHTTPFetcher.h" #import "GoogleSignIn/Sources/GIDProfileDataFetcher/Implementations/GIDProfileDataFetcher.h" - #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h" #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST @@ -203,6 +202,9 @@ @interface GIDSignInTest : XCTestCase { // Fake for |GIDHTTPFetcher|. GIDFakeHTTPFetcher *_httpFetcher; + // Fake for |GIDAuthorizationFlowProcessor| + GIDFakeAuthorizationFlowProcessor *_authorizationFlowProcessor; + #if TARGET_OS_IOS || TARGET_OS_MACCATALYST // Mock |UIViewController|. id _presentingViewController; @@ -289,14 +291,14 @@ - (void)setUp { OCMStub([_authorization initWithAuthState:OCMOCK_ANY]).andReturn(_authorization); _user = OCMStrictClassMock([GIDGoogleUser class]); _oidAuthorizationService = OCMStrictClassMock([OIDAuthorizationService class]); - OCMStub([_oidAuthorizationService - presentAuthorizationRequest:SAVE_TO_ARG_BLOCK(self->_savedAuthorizationRequest) -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - presentingViewController:SAVE_TO_ARG_BLOCK(self->_savedPresentingViewController) -#elif TARGET_OS_OSX - presentingWindow:SAVE_TO_ARG_BLOCK(self->_savedPresentingWindow) -#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - callback:COPY_TO_ARG_BLOCK(self->_savedAuthorizationCallback)]); +// OCMStub([_oidAuthorizationService +// presentAuthorizationRequest:SAVE_TO_ARG_BLOCK(self->_savedAuthorizationRequest) +//#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +// presentingViewController:SAVE_TO_ARG_BLOCK(self->_savedPresentingViewController) +//#elif TARGET_OS_OSX +// presentingWindow:SAVE_TO_ARG_BLOCK(self->_savedPresentingWindow) +//#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST +// callback:COPY_TO_ARG_BLOCK(self->_savedAuthorizationCallback)]); OCMStub([self->_oidAuthorizationService performTokenRequest:SAVE_TO_ARG_BLOCK(self->_savedTokenRequest) callback:COPY_TO_ARG_BLOCK(self->_savedTokenCallback)]); @@ -314,15 +316,14 @@ - (void)setUp { _httpFetcher = [[GIDFakeHTTPFetcher alloc] init]; - GIDAuthorizationFlowProcessor * authorizationFlowProcessor = - [[GIDAuthorizationFlowProcessor alloc] init]; + _authorizationFlowProcessor = [[GIDFakeAuthorizationFlowProcessor alloc] init]; id profileDataFetcher = [[GIDProfileDataFetcher alloc] init]; _signIn = [[GIDSignIn alloc] initWithKeychainHandler:_keychainHandler httpFetcher:_httpFetcher profileDataFetcher:profileDataFetcher - authorizationFlowProcessor:authorizationFlowProcessor]; + authorizationFlowProcessor:_authorizationFlowProcessor]; _hint = nil; @@ -343,7 +344,7 @@ - (void)tearDown { OCMVerifyAll(_tokenResponse); OCMVerifyAll(_tokenRequest); OCMVerifyAll(_user); - OCMVerifyAll(_oidAuthorizationService); +// OCMVerifyAll(_oidAuthorizationService); #if TARGET_OS_IOS || TARGET_OS_MACCATALYST OCMVerifyAll(_presentingViewController); @@ -1215,6 +1216,34 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow refreshToken:kRefreshToken codeVerifier:nil additionalParameters:tokenResponse.request.additionalParameters]; + + // Simulate auth endpoint response + GIDAuthorizationFlowProcessorTestBlock testBlock; + if (modalCancel) { + NSError *error = [NSError errorWithDomain:OIDGeneralErrorDomain + code:OIDErrorCodeUserCanceledAuthorizationFlow + userInfo:nil]; + testBlock = ^(GIDAuthorizationFlowProcessorFakeResponseProviderBlock responseProvider) { + responseProvider(nil, error); + }; + } else { + testBlock = ^(GIDAuthorizationFlowProcessorFakeResponseProviderBlock responseProvider) { + responseProvider(authResponse, nil); + }; + } + _authorizationFlowProcessor.testBlock = testBlock; + + // maybeFetchToken + if (!(authError || modalCancel)) { + [[[_authState expect] andReturn:nil] lastTokenResponse]; +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + // Corresponds to EMM support + [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + [[[_authState expect] andReturn:nil] lastTokenResponse]; + [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; + [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; + } if (restoredSignIn) { // maybeFetchToken @@ -1280,34 +1309,13 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow XCTAssertEqualObjects(params[@"include_granted_scopes"], @"true"); XCTAssertEqualObjects(params[kSDKVersionLoggingParameter], GIDVersion()); XCTAssertEqualObjects(params[kEnvironmentLoggingParameter], GIDEnvironment()); - XCTAssertNotNil(_savedAuthorizationCallback); +// XCTAssertNotNil(_savedAuthorizationCallback); #if TARGET_OS_IOS || TARGET_OS_MACCATALYST XCTAssertEqual(_savedPresentingViewController, _presentingViewController); #elif TARGET_OS_OSX XCTAssertEqual(_savedPresentingWindow, _presentingWindow); #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - // maybeFetchToken - if (!(authError || modalCancel)) { - [[[_authState expect] andReturn:nil] lastTokenResponse]; -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - // Corresponds to EMM support - [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; -#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST - [[[_authState expect] andReturn:nil] lastTokenResponse]; - [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; - [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; - } - - // Simulate auth endpoint response - if (modalCancel) { - NSError *error = [NSError errorWithDomain:OIDGeneralErrorDomain - code:OIDErrorCodeUserCanceledAuthorizationFlow - userInfo:nil]; - _savedAuthorizationCallback(nil, error); - } else { - _savedAuthorizationCallback(authResponse, nil); - } if (authError || modalCancel) { return; From 2d60ea94ea402f3eb14ede8c9f401a529d92eb48 Mon Sep 17 00:00:00 2001 From: pinlu Date: Wed, 18 Jan 2023 11:29:30 -0800 Subject: [PATCH 07/13] Fix GIDSignInTest --- .../Fakes/GIDFakeAuthorizationFlowProcessor.h | 4 +- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 74 ++++++++----------- 2 files changed, 31 insertions(+), 47 deletions(-) diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h index 62c9a270..25b91e9d 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h @@ -26,14 +26,14 @@ NS_ASSUME_NONNULL_BEGIN -/// The block which provides the response for the method `startWithOptions:emmSupport:completion:`. +/// The block type providing the response for the method `startWithOptions:emmSupport:completion:`. /// /// @param authorizationResponse The OIDAuthorizationResponse object returned if succeeded. /// @param error The error returned if failed. typedef void(^GIDAuthorizationFlowProcessorFakeResponseProviderBlock) (OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error); -/// The block to set up response value for the method +/// The block type setting up response value for the method /// `startWithOptions:emmSupport:completion:`. /// /// @param responseProvider The block which provides the response. diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 1a56b228..50a30c3a 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -281,14 +281,14 @@ - (void)setUp { OCMStub([_authorization initWithAuthState:OCMOCK_ANY]).andReturn(_authorization); _user = OCMStrictClassMock([GIDGoogleUser class]); _oidAuthorizationService = OCMStrictClassMock([OIDAuthorizationService class]); - OCMStub([_oidAuthorizationService - presentAuthorizationRequest:OCMOCK_ANY -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - presentingViewController:SAVE_TO_ARG_BLOCK(self->_savedPresentingViewController) -#elif TARGET_OS_OSX - presentingWindow:SAVE_TO_ARG_BLOCK(self->_savedPresentingWindow) -#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - callback:COPY_TO_ARG_BLOCK(self->_savedAuthorizationCallback)]); +// OCMStub([_oidAuthorizationService +// presentAuthorizationRequest:OCMOCK_ANY +//#if TARGET_OS_IOS || TARGET_OS_MACCATALYST +// presentingViewController:SAVE_TO_ARG_BLOCK(self->_savedPresentingViewController) +//#elif TARGET_OS_OSX +// presentingWindow:SAVE_TO_ARG_BLOCK(self->_savedPresentingWindow) +//#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST +// callback:COPY_TO_ARG_BLOCK(self->_savedAuthorizationCallback)]); OCMStub([self->_oidAuthorizationService performTokenRequest:SAVE_TO_ARG_BLOCK(self->_savedTokenRequest) callback:COPY_TO_ARG_BLOCK(self->_savedTokenCallback)]); @@ -595,12 +595,6 @@ - (void)testSignOut { XCTAssertNil([_keychainHandler loadAuthState]); } -- (void)testNotHandleWrongScheme { - XCTAssertFalse([_signIn handleURL:[NSURL URLWithString:kWrongSchemeURL]], - @"should not handle URL"); - XCTAssertFalse(_completionCalled, @"should not call delegate"); -} - - (void)testNotHandleWrongPath { XCTAssertFalse([_signIn handleURL:[NSURL URLWithString:kWrongPathURL]], @"should not handle URL"); XCTAssertFalse(_completionCalled, @"should not call delegate"); @@ -1038,8 +1032,8 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow refreshToken:kRefreshToken codeVerifier:nil additionalParameters:tokenResponse.request.additionalParameters]; - - // Simulate auth endpoint response + + // Set the response for the auth endpoint. GIDAuthorizationFlowProcessorTestBlock authorizationFlowTestBlock; if (modalCancel) { NSError *error = [NSError errorWithDomain:OIDGeneralErrorDomain @@ -1057,29 +1051,17 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow } _authorizationFlowProcessor.testBlock = authorizationFlowTestBlock; - // maybeFetchToken - if (!(authError || modalCancel)) { - [[[_authState expect] andReturn:nil] lastTokenResponse]; -#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST - // Corresponds to EMM support - [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; -#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST - [[[_authState expect] andReturn:nil] lastTokenResponse]; - [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; - [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; - } - // Set the response for `GIDProfileDataFetcher`. - GIDProfileDataFetcherTestBlock testBlock = ^(GIDProfileDataFetcherFakeResponseProvider - responseProvider) { - GIDProfileData *profileData = [GIDProfileData testInstance]; - responseProvider(profileData, nil); + GIDProfileDataFetcherTestBlock profileDataFetcherTestBlock = + ^(GIDProfileDataFetcherFakeResponseProvider responseProvider) { + GIDProfileData *profileData = [GIDProfileData testInstance]; + responseProvider(profileData, nil); }; - _profileDataFetcher.testBlock = testBlock; + _profileDataFetcher.testBlock = profileDataFetcherTestBlock; if (restoredSignIn) { - // maybeFetchToken + // Mock `maybeFetchToken:` method in `restorePreviousSignIn:` flow. [[[_authState expect] andReturn:tokenResponse] lastTokenResponse]; [[[_authState expect] andReturn:tokenResponse] lastTokenResponse]; if (oldAccessToken) { @@ -1115,6 +1097,18 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST completion:completion]; } else { + // Mock `maybeFetchToken:` method in Sign in flow. + if (!(authError || modalCancel)) { + [[[_authState expect] andReturn:nil] lastTokenResponse]; + #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST + // Corresponds to EMM support + [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; + #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST + [[[_authState expect] andReturn:nil] lastTokenResponse]; + [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; + [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; + } + #if TARGET_OS_IOS || TARGET_OS_MACCATALYST [_signIn signInWithPresentingViewController:_presentingViewController #elif TARGET_OS_OSX @@ -1123,21 +1117,11 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow hint:_hint completion:completion]; } - - [_authState verify]; - - XCTAssertNotNil(_savedAuthorizationCallback); - -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - XCTAssertEqual(_savedPresentingViewController, _presentingViewController); -#elif TARGET_OS_OSX - XCTAssertEqual(_savedPresentingWindow, _presentingWindow); -#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - if (authError || modalCancel) { return; } + [_authState verify]; } From 89649b130bb40fcb943cbc915fc216607315290c Mon Sep 17 00:00:00 2001 From: pinlu Date: Wed, 18 Jan 2023 14:45:16 -0800 Subject: [PATCH 08/13] Improve style. --- .../Fakes/GIDFakeAuthorizationFlowProcessor.h | 12 ++++------ .../Fakes/GIDFakeAuthorizationFlowProcessor.m | 23 ++++++++++++++++++- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 17 ++++---------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h index 25b91e9d..ac4a5d98 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h @@ -18,18 +18,14 @@ #import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h" -#ifdef SWIFT_PACKAGE -@import AppAuth; -#else -#import -#endif +@class OIDAuthorizationResponse; NS_ASSUME_NONNULL_BEGIN /// The block type providing the response for the method `startWithOptions:emmSupport:completion:`. /// -/// @param authorizationResponse The OIDAuthorizationResponse object returned if succeeded. -/// @param error The error returned if failed. +/// @param authorizationResponse The `OIDAuthorizationResponse` object returned on success. +/// @param error The error returned on failure. typedef void(^GIDAuthorizationFlowProcessorFakeResponseProviderBlock) (OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error); @@ -40,12 +36,12 @@ typedef void(^GIDAuthorizationFlowProcessorFakeResponseProviderBlock) typedef void (^GIDAuthorizationFlowProcessorTestBlock) (GIDAuthorizationFlowProcessorFakeResponseProviderBlock responseProvider); +/// The fake implementation of the protocol `GIDAuthorizationFlowProcessor`. @interface GIDFakeAuthorizationFlowProcessor : NSObject /// The test block which provides the response value. @property(nonatomic) GIDAuthorizationFlowProcessorTestBlock testBlock; - @end NS_ASSUME_NONNULL_END diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m index 49ac355a..57376f98 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m @@ -1,5 +1,27 @@ +/* + * 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/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h" +#ifdef SWIFT_PACKAGE +@import AppAuth; +#else +#import +#endif + @implementation GIDFakeAuthorizationFlowProcessor - (void)startWithOptions:(GIDSignInInternalOptions *)options @@ -8,7 +30,6 @@ - (void)startWithOptions:(GIDSignInInternalOptions *)options NSError *_Nullable error))completion { NSAssert(self.testBlock != nil, @"Set the test block before invoking this method."); - self.testBlock(^(OIDAuthorizationResponse *authorizationResponse, NSError *error) { completion(authorizationResponse, error); }); diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 50a30c3a..7817f0c1 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -26,6 +26,7 @@ // Test module imports @import GoogleSignIn; +#import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/API/GIDAuthorizationFlowProcessor.h" #import "GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h" #import "GoogleSignIn/Sources/GIDEMMSupport.h" #import "GoogleSignIn/Sources/GIDGoogleUser_Private.h" @@ -192,7 +193,7 @@ @interface GIDSignInTest : XCTestCase { // Fake for `GIDHTTPFetcher`. GIDFakeHTTPFetcher *_httpFetcher; - // Fake for `GIDAuthorizationFlowProcessor` + // Fake for `GIDAuthorizationFlowProcessor`. GIDFakeAuthorizationFlowProcessor *_authorizationFlowProcessor; // Fake for `GIDProfileDataFetcher`. @@ -281,14 +282,6 @@ - (void)setUp { OCMStub([_authorization initWithAuthState:OCMOCK_ANY]).andReturn(_authorization); _user = OCMStrictClassMock([GIDGoogleUser class]); _oidAuthorizationService = OCMStrictClassMock([OIDAuthorizationService class]); -// OCMStub([_oidAuthorizationService -// presentAuthorizationRequest:OCMOCK_ANY -//#if TARGET_OS_IOS || TARGET_OS_MACCATALYST -// presentingViewController:SAVE_TO_ARG_BLOCK(self->_savedPresentingViewController) -//#elif TARGET_OS_OSX -// presentingWindow:SAVE_TO_ARG_BLOCK(self->_savedPresentingWindow) -//#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST -// callback:COPY_TO_ARG_BLOCK(self->_savedAuthorizationCallback)]); OCMStub([self->_oidAuthorizationService performTokenRequest:SAVE_TO_ARG_BLOCK(self->_savedTokenRequest) callback:COPY_TO_ARG_BLOCK(self->_savedTokenCallback)]); @@ -1054,9 +1047,9 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow // Set the response for `GIDProfileDataFetcher`. GIDProfileDataFetcherTestBlock profileDataFetcherTestBlock = ^(GIDProfileDataFetcherFakeResponseProvider responseProvider) { - GIDProfileData *profileData = [GIDProfileData testInstance]; - responseProvider(profileData, nil); - }; + GIDProfileData *profileData = [GIDProfileData testInstance]; + responseProvider(profileData, nil); + }; _profileDataFetcher.testBlock = profileDataFetcherTestBlock; From 7e76eaec48ee485284988d34e7de7810741c210d Mon Sep 17 00:00:00 2001 From: pinlu Date: Wed, 18 Jan 2023 14:54:09 -0800 Subject: [PATCH 09/13] More cleanup. --- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 7817f0c1..5e3d593e 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -228,23 +228,9 @@ @interface GIDSignInTest : XCTestCase { // The configuration to be used when testing `GIDSignIn`. GIDConfiguration *_configuration; - // The login hint to be used when testing `GIDSignIn`. - NSString *_hint; - // The completion to be used when testing `GIDSignIn`. GIDSignInCompletion _completion; -#if TARGET_OS_IOS || TARGET_OS_MACCATALYST - // The saved presentingViewController from the authorization request. - UIViewController *_savedPresentingViewController; -#elif TARGET_OS_OSX - // The saved presentingWindow from the authorization request. - NSWindow *_savedPresentingWindow; -#endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - - // The saved authorization callback. - OIDAuthorizationCallback _savedAuthorizationCallback; - // The saved token request. OIDTokenRequest *_savedTokenRequest; @@ -308,8 +294,6 @@ - (void)setUp { profileDataFetcher:_profileDataFetcher authorizationFlowProcessor:_authorizationFlowProcessor]; - _hint = nil; - __weak GIDSignInTest *weakSelf = self; _completion = ^(GIDSignInResult *_Nullable signInResult, NSError * _Nullable error) { GIDSignInTest *strongSelf = weakSelf; @@ -772,7 +756,7 @@ - (void)testPresentingViewControllerException { #elif TARGET_OS_OSX XCTAssertThrows([_signIn signInWithPresentingWindow:_presentingWindow #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - hint:_hint + hint:nil completion:_completion]); } @@ -807,7 +791,7 @@ - (void)testSchemesNotSupportedException { #elif TARGET_OS_OSX [_signIn signInWithPresentingWindow:_presentingWindow #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - hint:_hint + hint:nil completion:_completion]; } @catch (NSException *exception) { threw = YES; @@ -1107,7 +1091,7 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow #elif TARGET_OS_OSX [_signIn signInWithPresentingWindow:_presentingWindow #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST - hint:_hint + hint:nil completion:completion]; } From df7f7802968909a29c186c005223774ef7fabc1a Mon Sep 17 00:00:00 2001 From: pinlu Date: Thu, 19 Jan 2023 15:57:24 -0800 Subject: [PATCH 10/13] Improve style. --- .../Fakes/GIDFakeAuthorizationFlowProcessor.h | 2 +- .../Fakes/GIDFakeAuthorizationFlowProcessor.m | 1 - GoogleSignIn/Tests/Unit/GIDSignInTest.m | 13 ++++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h index ac4a5d98..f8ce6128 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.h @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN /// The block type providing the response for the method `startWithOptions:emmSupport:completion:`. /// /// @param authorizationResponse The `OIDAuthorizationResponse` object returned on success. -/// @param error The error returned on failure. +/// @param error An `NSError` returned on failure. typedef void(^GIDAuthorizationFlowProcessorFakeResponseProviderBlock) (OIDAuthorizationResponse *_Nullable authorizationResponse, NSError *_Nullable error); diff --git a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m index 57376f98..7077fd44 100644 --- a/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m +++ b/GoogleSignIn/Sources/GIDAuthorizationFlowProcessor/Implementations/Fakes/GIDFakeAuthorizationFlowProcessor.m @@ -41,7 +41,6 @@ - (BOOL)isStarted { } - (BOOL)resumeExternalUserAgentFlowWithURL:(NSURL *)url { - NSAssert(NO, @"Not implemented."); return YES; } diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 5e3d593e..0bc9b0e6 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -104,9 +104,7 @@ static NSString * const kFakeUserFamilyName = @"username"; static NSString * const kFakeUserPictureURL = @"fake_user_picture_url"; -static NSString * const kContinueURL = @"com.google.UnitTests:/oauth2callback"; -static NSString * const kContinueURLWithClientID = @"FakeClientID:/oauth2callback"; -static NSString * const kWrongSchemeURL = @"wrong.app:/oauth2callback"; +static NSString * const kRightPathURL = @"com.google.UnitTests:/oauth2callback"; static NSString * const kWrongPathURL = @"com.google.UnitTests:/wrong_path"; static NSString * const kEMMRestartAuthURL = @@ -572,6 +570,11 @@ - (void)testSignOut { XCTAssertNil([_keychainHandler loadAuthState]); } +- (void)testHandleRightPath { + XCTAssertTrue([_signIn handleURL:[NSURL URLWithString:kRightPathURL]]); + XCTAssertFalse(_completionCalled, @"should not call delegate"); +} + - (void)testNotHandleWrongPath { XCTAssertFalse([_signIn handleURL:[NSURL URLWithString:kWrongPathURL]], @"should not handle URL"); XCTAssertFalse(_completionCalled, @"should not call delegate"); @@ -1077,10 +1080,10 @@ - (void)OAuthLoginWithAddScopesFlow:(BOOL)addScopesFlow // Mock `maybeFetchToken:` method in Sign in flow. if (!(authError || modalCancel)) { [[[_authState expect] andReturn:nil] lastTokenResponse]; - #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST // Corresponds to EMM support [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; - #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST +#endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST [[[_authState expect] andReturn:nil] lastTokenResponse]; [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; [[[_authState expect] andReturn:authResponse] lastAuthorizationResponse]; From 0ae1e7fc46be4a439a18dd1bc526217b0e7e0889 Mon Sep 17 00:00:00 2001 From: henryhl22321 <130406709+henryhl22321@users.noreply.github.com> Date: Wed, 17 May 2023 15:34:56 -0700 Subject: [PATCH 11/13] Uncommented the existing verification line. --- GoogleSignIn/Tests/Unit/GIDSignInTest.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GoogleSignIn/Tests/Unit/GIDSignInTest.m b/GoogleSignIn/Tests/Unit/GIDSignInTest.m index 0bc9b0e6..96256288 100644 --- a/GoogleSignIn/Tests/Unit/GIDSignInTest.m +++ b/GoogleSignIn/Tests/Unit/GIDSignInTest.m @@ -309,7 +309,7 @@ - (void)tearDown { OCMVerifyAll(_tokenResponse); OCMVerifyAll(_tokenRequest); OCMVerifyAll(_user); -// OCMVerifyAll(_oidAuthorizationService); + OCMVerifyAll(_oidAuthorizationService); #if TARGET_OS_IOS || TARGET_OS_MACCATALYST OCMVerifyAll(_presentingViewController); From 0b143d5734b821b6380dd04daa82f4183d93bf4d Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Thu, 20 Apr 2023 09:39:33 -0700 Subject: [PATCH 12/13] Remove macos-11 runner --- .github/workflows/unit_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 09b1d6f6..221650b3 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11, macos-12] + os: [macos-12] podspec: [GoogleSignIn.podspec, GoogleSignInSwiftSupport.podspec] flag: [ "", @@ -40,7 +40,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-11, macos-12] + os: [macos-12] sdk: ['macosx', 'iphonesimulator'] include: - sdk: 'macosx' From 21745e4ae9a2107b3cd33d00cc819e223c14862b Mon Sep 17 00:00:00 2001 From: Matthew Mathias Date: Wed, 3 May 2023 15:41:32 -0700 Subject: [PATCH 13/13] Update name of return button The UI automation test previously used 'go' but it appears the button has been renamed to 'return'. --- .../DaysUntilBirthdayUITests_iOS.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift b/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift index 607acbcb..6644e786 100644 --- a/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift +++ b/Samples/Swift/DaysUntilBirthday/DaysUntilBirthdayUITests(iOS)/DaysUntilBirthdayUITests_iOS.swift @@ -141,16 +141,16 @@ extension DaysUntilBirthdayUITests_iOS { guard sampleApp .keyboards .element - .buttons["go"] + .buttons["return"] .waitForExistence(timeout: timeout) else { - XCTFail("Failed to find 'go' button") + XCTFail("Failed to find 'return' button") return false } sampleApp .secureTextFields["Enter your password"] .typeText(Credential.password.rawValue) - sampleApp.keyboards.element.buttons["go"].tap() + sampleApp.keyboards.element.buttons["return"].tap() return true }