GIDAuthorizationFlow.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /*
  2. * Copyright 2025 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "GIDAuthorizationFlow.h"
  17. #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDSignIn.h"
  18. #import "GoogleSignIn/Sources/Public/GoogleSignIn/GIDConfiguration.h"
  19. #import "GoogleSignIn/Sources/GIDAuthorizationFlow/Implementations/Operations/GIDSaveAuthOperation.h"
  20. #import "GoogleSignIn/Sources/GIDAuthorizationFlow/Implementations/Operations/GIDTokenFetchOperation.h"
  21. #import "GoogleSignIn/Sources/GIDAuthorizationFlow/Implementations/Operations/GIDDecodeIDTokenOperation.h"
  22. #import "GoogleSignIn/Sources/GIDAuthorizationFlow/Implementations/Operations/GIDAuthorizationCompletionOperation.h"
  23. #import "GoogleSignIn/Sources/GIDEMMErrorHandler.h"
  24. #import "GoogleSignIn/Sources/GIDEMMSupport.h"
  25. #import "GoogleSignIn/Sources/GIDSignInInternalOptions.h"
  26. #import "GoogleSignIn/Sources/GIDSignInPreferences.h"
  27. #import "GoogleSignIn/Sources/GIDSignInConstants.h"
  28. #import "GoogleSignIn/Sources/GIDSignInCallbackSchemes.h"
  29. #ifdef SWIFT_PACKAGE
  30. @import AppAuth;
  31. #else
  32. #import <AppAuth/OIDAuthState.h>
  33. #endif
  34. @interface GIDAuthorizationFlow ()
  35. @property(nonatomic) BOOL restarting;
  36. @end
  37. @implementation GIDAuthorizationFlow
  38. - (instancetype)initWithSignInOptions:(GIDSignInInternalOptions *)options
  39. authState:(OIDAuthState *)authState
  40. profileData:(nullable GIDProfileData *)profileData
  41. googleUser:(nullable GIDGoogleUser *)googleUser
  42. externalUserAgentSession:(nullable id<OIDExternalUserAgentSession>)userAgentSession
  43. emmSupport:(nullable NSString *)emmSupport
  44. error:(nullable NSError *)error {
  45. self = [super init];
  46. if (self) {
  47. _options = options;
  48. _authState = authState;
  49. _profileData = profileData;
  50. _googleUser = googleUser;
  51. _currentUserAgentSession = userAgentSession;
  52. _error = error;
  53. _emmSupport = emmSupport;
  54. }
  55. return self;
  56. }
  57. #pragma mark - Authorize
  58. - (void)authorize {
  59. GIDTokenFetchOperation *tokenFetch =
  60. [[GIDTokenFetchOperation alloc] initWithAuthState:self.authState
  61. options:self.options
  62. emmSupport:self.emmSupport
  63. error:self.error];
  64. GIDDecodeIDTokenOperation *idToken = [[GIDDecodeIDTokenOperation alloc] init];
  65. [idToken addDependency:tokenFetch];
  66. GIDSaveAuthOperation *saveAuth = [[GIDSaveAuthOperation alloc] init];
  67. [saveAuth addDependency:idToken];
  68. GIDAuthorizationCompletionOperation *authCompletion =
  69. [[GIDAuthorizationCompletionOperation alloc] initWithAuthorizationFlow:self];
  70. NSArray<NSOperation *> *operations = @[tokenFetch, idToken, saveAuth, authCompletion];
  71. [NSOperationQueue.mainQueue addOperations:operations waitUntilFinished:NO];
  72. }
  73. - (void)authorizeInteractively {
  74. NSString *emmSupport;
  75. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  76. emmSupport = [[self class] isOperatingSystemAtLeast9] ? kEMMVersion : nil;
  77. #elif TARGET_OS_MACCATALYST || TARGET_OS_OSX
  78. emmSupport = nil;
  79. #endif // TARGET_OS_MACCATALYST || TARGET_OS_OSX
  80. [self authorizationRequestWithOptions:self.options
  81. completion:^(OIDAuthorizationRequest * _Nullable request,
  82. NSError * _Nullable error) {
  83. self->_currentUserAgentSession =
  84. [OIDAuthorizationService presentAuthorizationRequest:request
  85. #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
  86. presentingViewController:self.options.presentingViewController
  87. #elif TARGET_OS_OSX
  88. presentingWindow:options.presentingWindow
  89. #endif // TARGET_OS_OSX
  90. callback:
  91. ^(OIDAuthorizationResponse *_Nullable authorizationResponse,
  92. NSError *_Nullable error) {
  93. [self processAuthorizationResponse:authorizationResponse
  94. error:error
  95. emmSupport:emmSupport];
  96. }];
  97. }];
  98. }
  99. #pragma mark - Authorization Request
  100. - (void)authorizationRequestWithOptions:(GIDSignInInternalOptions *)options
  101. completion:
  102. (void (^)(OIDAuthorizationRequest *_Nullable request, NSError *_Nullable error))completion {
  103. NSMutableDictionary<NSString *, NSString *> *additionalParameters =
  104. [self additionalParametersFromOptions:options];
  105. OIDAuthorizationRequest *request = [self authorizationRequestWithOptions:options
  106. additionalParameters:additionalParameters];
  107. // TODO: Add app check steps as well
  108. completion(request, nil);
  109. }
  110. - (OIDAuthorizationRequest *)
  111. authorizationRequestWithOptions:(GIDSignInInternalOptions *)options
  112. additionalParameters:(NSDictionary<NSString *, NSString *> *)additionalParameters {
  113. OIDAuthorizationRequest *request;
  114. if (options.nonce) {
  115. request = [[OIDAuthorizationRequest alloc] initWithConfiguration:self.serviceConfiguration
  116. clientId:options.configuration.clientID
  117. scopes:options.scopes
  118. redirectURL:[self redirectURLWithOptions:options]
  119. responseType:OIDResponseTypeCode
  120. nonce:options.nonce
  121. additionalParameters:additionalParameters];
  122. } else {
  123. request = [[OIDAuthorizationRequest alloc] initWithConfiguration:self.serviceConfiguration
  124. clientId:options.configuration.clientID
  125. scopes:options.scopes
  126. redirectURL:[self redirectURLWithOptions:options]
  127. responseType:OIDResponseTypeCode
  128. additionalParameters:additionalParameters];
  129. }
  130. return request;
  131. }
  132. #pragma mark - Authorization Response
  133. - (void)processAuthorizationResponse:(nullable OIDAuthorizationResponse *)authorizationResponse
  134. error:(nullable NSError *)error
  135. emmSupport:(nullable NSString *)emmSupport {
  136. if (self.restarting) {
  137. // The auth flow is restarting, so the work here would be performed in the next round.
  138. self.restarting = NO;
  139. return;
  140. }
  141. self.emmSupport = emmSupport;
  142. if (authorizationResponse) {
  143. if (authorizationResponse.authorizationCode.length) {
  144. self.authState = [[OIDAuthState alloc] initWithAuthorizationResponse:authorizationResponse];
  145. } else {
  146. // There was a failure; convert to appropriate error code.
  147. NSString *errorString;
  148. GIDSignInErrorCode errorCode = kGIDSignInErrorCodeUnknown;
  149. NSDictionary<NSString *, NSObject *> *params = authorizationResponse.additionalParameters;
  150. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  151. if (self.emmSupport) {
  152. BOOL isEMMError = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:params
  153. completion:^{
  154. // TODO: What to do here?
  155. }];
  156. if (isEMMError) {
  157. errorCode = kGIDSignInErrorCodeEMM;
  158. }
  159. }
  160. #endif // TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  161. errorString = (NSString *)params[kOAuth2ErrorKeyName];
  162. if ([errorString isEqualToString:kOAuth2AccessDenied]) {
  163. errorCode = kGIDSignInErrorCodeCanceled;
  164. }
  165. self.error = [self errorWithString:errorString code:errorCode];
  166. }
  167. } else {
  168. NSString *errorString = [error localizedDescription];
  169. GIDSignInErrorCode errorCode = kGIDSignInErrorCodeUnknown;
  170. if (error.code == OIDErrorCodeUserCanceledAuthorizationFlow ||
  171. error.code == OIDErrorCodeProgramCanceledAuthorizationFlow) {
  172. // The user has canceled the flow at the iOS modal dialog.
  173. errorString = kUserCanceledError;
  174. errorCode = kGIDSignInErrorCodeCanceled;
  175. }
  176. self.error = [self errorWithString:errorString code:errorCode];
  177. }
  178. GIDTokenFetchOperation *tokenFetch =
  179. [[GIDTokenFetchOperation alloc] initWithAuthState:self.authState
  180. options:self.options
  181. emmSupport:self.emmSupport
  182. error:self.error];
  183. GIDDecodeIDTokenOperation *decodeID = [[GIDDecodeIDTokenOperation alloc] init];
  184. [decodeID addDependency:tokenFetch];
  185. GIDSaveAuthOperation *saveAuth = [[GIDSaveAuthOperation alloc] init];
  186. [saveAuth addDependency:decodeID];
  187. GIDAuthorizationCompletionOperation *authCompletion =
  188. [[GIDAuthorizationCompletionOperation alloc] initWithAuthorizationFlow:self];
  189. [authCompletion addDependency:saveAuth];
  190. NSArray<NSOperation *> *operations = @[tokenFetch, decodeID, saveAuth, authCompletion];
  191. [NSOperationQueue.mainQueue addOperations:operations waitUntilFinished:NO];
  192. }
  193. #pragma mark - Errors
  194. // TODO: Move this to its own type
  195. - (NSError *)errorWithString:(NSString *)errorString code:(GIDSignInErrorCode)code {
  196. if (errorString == nil) {
  197. errorString = @"Unknown error";
  198. }
  199. NSDictionary<NSString *, NSString *> *errorDict = @{ NSLocalizedDescriptionKey : errorString };
  200. return [NSError errorWithDomain:kGIDSignInErrorDomain
  201. code:code
  202. userInfo:errorDict];
  203. }
  204. #pragma mark - Utilities
  205. - (NSMutableDictionary<NSString *, NSString *> *)
  206. additionalParametersFromOptions:(GIDSignInInternalOptions *)options {
  207. NSString *emmSupport;
  208. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  209. emmSupport = [[self class] isOperatingSystemAtLeast9] ? kEMMVersion : nil;
  210. #elif TARGET_OS_MACCATALYST || TARGET_OS_OSX
  211. emmSupport = nil;
  212. #endif // TARGET_OS_MACCATALYST || TARGET_OS_OSX
  213. NSMutableDictionary<NSString *, NSString *> *additionalParameters =
  214. [[NSMutableDictionary alloc] init];
  215. additionalParameters[kIncludeGrantedScopesParameter] = @"true";
  216. if (options.configuration.serverClientID) {
  217. additionalParameters[kAudienceParameter] = options.configuration.serverClientID;
  218. }
  219. if (options.loginHint) {
  220. additionalParameters[kLoginHintParameter] = options.loginHint;
  221. }
  222. if (options.configuration.hostedDomain) {
  223. additionalParameters[kHostedDomainParameter] = options.configuration.hostedDomain;
  224. }
  225. #if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
  226. [additionalParameters addEntriesFromDictionary:
  227. [GIDEMMSupport parametersWithParameters:options.extraParams
  228. emmSupport:emmSupport
  229. isPasscodeInfoRequired:NO]];
  230. #elif TARGET_OS_OSX || TARGET_OS_MACCATALYST
  231. [additionalParameters addEntriesFromDictionary:options.extraParams];
  232. #endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST
  233. additionalParameters[kSDKVersionLoggingParameter] = GIDVersion();
  234. additionalParameters[kEnvironmentLoggingParameter] = GIDEnvironment();
  235. return additionalParameters;
  236. }
  237. + (BOOL)isOperatingSystemAtLeast9 {
  238. NSProcessInfo *processInfo = [NSProcessInfo processInfo];
  239. return [processInfo respondsToSelector:@selector(isOperatingSystemAtLeastVersion:)] &&
  240. [processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = 9}];
  241. }
  242. - (NSURL *)redirectURLWithOptions:(GIDSignInInternalOptions *)options {
  243. GIDSignInCallbackSchemes *schemes =
  244. [[GIDSignInCallbackSchemes alloc] initWithClientIdentifier:options.configuration.clientID];
  245. NSString *redirect =
  246. [NSString stringWithFormat:@"%@:%@", [schemes clientIdentifierScheme], kBrowserCallbackPath];
  247. NSURL *redirectURL = [NSURL URLWithString:redirect];
  248. return redirectURL;
  249. }
  250. @end