No Description

FIROAuthProvider.m 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. * Copyright 2017 Google
  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. #include <CommonCrypto/CommonCrypto.h>
  17. #import "FIROAuthProvider.h"
  18. #import "FIRApp.h"
  19. #import "FIRAuthBackend.h"
  20. #import "FIRAuth_Internal.h"
  21. #import "FIRAuthErrorUtils.h"
  22. #import "FIRAuthGlobalWorkQueue.h"
  23. #import "FIRAuthRequestConfiguration.h"
  24. #import "FIRAuthWebUtils.h"
  25. #import "FIROAuthCredential_Internal.h"
  26. #import "FIROAuthCredential.h"
  27. #import "FIROptions.h"
  28. #if TARGET_OS_IOS
  29. #import "FIRAuthURLPresenter.h"
  30. #endif
  31. NS_ASSUME_NONNULL_BEGIN
  32. /** @typedef FIRHeadfulLiteURLCallBack
  33. @brief The callback invoked at the end of the flow to fetch a headful-lite URL.
  34. @param headfulLiteURL The headful lite URL.
  35. @param error The error that occured while fetching the headful-lite, if any.
  36. */
  37. typedef void (^FIRHeadfulLiteURLCallBack)(NSURL *_Nullable headfulLiteURL,
  38. NSError *_Nullable error);
  39. /** @var kHeadfulLiteURLStringFormat
  40. @brief The format of the URL used to open the headful lite page during sign-in.
  41. */
  42. NSString *const kHeadfulLiteURLStringFormat = @"https://%@/__/auth/handler?%@";
  43. /** @var kauthTypeSignInWithRedirect
  44. @brief The auth type to be specified in the sign-in request with redirect request and response.
  45. */
  46. static NSString *const kAuthTypeSignInWithRedirect = @"signInWithRedirect";
  47. @implementation FIROAuthProvider {
  48. /** @var _auth
  49. @brief The auth instance used for launching the URL presenter.
  50. */
  51. FIRAuth *_auth;
  52. /** @var _callbackScheme
  53. @brief The callback URL scheme used for headful-lite sign-in.
  54. */
  55. NSString *_callbackScheme;
  56. }
  57. + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
  58. IDToken:(NSString *)IDToken
  59. accessToken:(nullable NSString *)accessToken
  60. pendingToken:(nullable NSString *)pendingToken {
  61. return [[FIROAuthCredential alloc] initWithProviderID:providerID
  62. IDToken:IDToken
  63. accessToken:accessToken
  64. pendingToken:pendingToken];
  65. }
  66. + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
  67. IDToken:(NSString *)IDToken
  68. accessToken:(nullable NSString *)accessToken {
  69. return [[FIROAuthCredential alloc] initWithProviderID:providerID
  70. IDToken:IDToken
  71. accessToken:accessToken
  72. pendingToken:nil];
  73. }
  74. + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
  75. accessToken:(NSString *)accessToken {
  76. return [[FIROAuthCredential alloc] initWithProviderID:providerID
  77. IDToken:nil
  78. accessToken:accessToken
  79. pendingToken:nil];
  80. }
  81. + (instancetype)providerWithProviderID:(NSString *)providerID {
  82. return [[self alloc]initWithProviderID:providerID auth:[FIRAuth auth]];
  83. }
  84. + (instancetype)providerWithProviderID:(NSString *)providerID auth:(FIRAuth *)auth {
  85. return [[self alloc] initWithProviderID:providerID auth:auth];
  86. }
  87. #if TARGET_OS_IOS
  88. - (void)getCredentialWithUIDelegate:(nullable id<FIRAuthUIDelegate>)UIDelegate
  89. completion:(nullable FIRAuthCredentialCallback)completion {
  90. if (![FIRAuthWebUtils isCallbackSchemeRegisteredForCustomURLScheme:self->_callbackScheme]) {
  91. [NSException raise:NSInternalInconsistencyException
  92. format:@"Please register custom URL scheme '%@' in the app's Info.plist file.",
  93. self->_callbackScheme];
  94. }
  95. __weak __typeof__(self) weakSelf = self;
  96. __weak FIRAuth *weakAuth = _auth;
  97. __weak NSString *weakProviderID = _providerID;
  98. dispatch_async(FIRAuthGlobalWorkQueue(), ^{
  99. FIRAuthCredentialCallback callbackOnMainThread = ^(FIRAuthCredential *_Nullable credential,
  100. NSError *_Nullable error) {
  101. if (completion) {
  102. dispatch_async(dispatch_get_main_queue(), ^{
  103. completion(credential, error);
  104. });
  105. }
  106. };
  107. NSString *eventID = [FIRAuthWebUtils randomStringWithLength:10];
  108. NSString *sessionID = [FIRAuthWebUtils randomStringWithLength:10];
  109. __strong __typeof__(self) strongSelf = weakSelf;
  110. [strongSelf getHeadFulLiteURLWithEventID:eventID
  111. sessionID:sessionID
  112. completion:^(NSURL *_Nullable headfulLiteURL,
  113. NSError *_Nullable error) {
  114. if (error) {
  115. callbackOnMainThread(nil, error);
  116. return;
  117. }
  118. FIRAuthURLCallbackMatcher callbackMatcher = ^BOOL(NSURL *_Nullable callbackURL) {
  119. return [FIRAuthWebUtils isExpectedCallbackURL:callbackURL
  120. eventID:eventID
  121. authType:kAuthTypeSignInWithRedirect
  122. callbackScheme:strongSelf->_callbackScheme];
  123. };
  124. __strong FIRAuth *strongAuth = weakAuth;
  125. [strongAuth.authURLPresenter presentURL:headfulLiteURL
  126. UIDelegate:UIDelegate
  127. callbackMatcher:callbackMatcher
  128. completion:^(NSURL *_Nullable callbackURL,
  129. NSError *_Nullable error) {
  130. if (error) {
  131. callbackOnMainThread(nil, error);
  132. return;
  133. }
  134. NSString *OAuthResponseURLString =
  135. [strongSelf OAuthResponseForURL:callbackURL error:&error];
  136. if (error) {
  137. callbackOnMainThread(nil, error);
  138. return;
  139. }
  140. __strong NSString *strongProviderID = weakProviderID;
  141. FIROAuthCredential *credential =
  142. [[FIROAuthCredential alloc] initWithProviderID:strongProviderID
  143. sessionID:sessionID
  144. OAuthResponseURLString:OAuthResponseURLString];
  145. callbackOnMainThread(credential, nil);
  146. }];
  147. }];
  148. });
  149. }
  150. #endif // TARGET_OS_IOS
  151. #pragma mark - Internal Methods
  152. /** @fn initWithProviderID:auth:
  153. @brief returns an instance of @c FIROAuthProvider assocaited with the provided auth instance.
  154. @param auth The Auth instance to be associated with the OAuthProvider instance.
  155. @return An Instance of @c FIROAuthProvider.
  156. */
  157. - (nullable instancetype)initWithProviderID:(NSString *)providerID auth:(FIRAuth *)auth {
  158. self = [super init];
  159. if (self) {
  160. _auth = auth;
  161. _providerID = providerID;
  162. _callbackScheme = [[[_auth.app.options.clientID componentsSeparatedByString:@"."]
  163. reverseObjectEnumerator].allObjects componentsJoinedByString:@"."];
  164. }
  165. return self;
  166. }
  167. /** @fn OAuthResponseForURL:error:
  168. @brief Parses the redirected URL and returns a string representation of the OAuth response URL.
  169. @param URL The url to be parsed for an OAuth response URL.
  170. @param error The error that occurred if any.
  171. @return The OAuth response if successful.
  172. */
  173. - (nullable NSString *)OAuthResponseForURL:(NSURL *)URL error:(NSError *_Nullable *_Nullable)error {
  174. NSDictionary<NSString *, NSString *> *URLQueryItems =
  175. [FIRAuthWebUtils dictionaryWithHttpArgumentsString:URL.query];
  176. NSURL *deepLinkURL = [NSURL URLWithString:URLQueryItems[@"deep_link_id"]];
  177. URLQueryItems =
  178. [FIRAuthWebUtils dictionaryWithHttpArgumentsString:deepLinkURL.query];
  179. NSString *queryItemLink = URLQueryItems[@"link"];
  180. if (queryItemLink) {
  181. return queryItemLink;
  182. }
  183. if (!error) {
  184. return nil;
  185. }
  186. NSData *errorData = [URLQueryItems[@"firebaseError"] dataUsingEncoding:NSUTF8StringEncoding];
  187. NSError *jsonError;
  188. NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData:errorData
  189. options:0
  190. error:&jsonError];
  191. if (jsonError) {
  192. *error = [FIRAuthErrorUtils JSONSerializationErrorWithUnderlyingError:jsonError];
  193. return nil;
  194. }
  195. *error = [FIRAuthErrorUtils URLResponseErrorWithCode:errorDict[@"code"]
  196. message:errorDict[@"message"]];
  197. if (!*error) {
  198. NSString *reason;
  199. if(errorDict[@"code"] && errorDict[@"message"]) {
  200. reason = [NSString stringWithFormat:@"[%@] - %@",errorDict[@"code"], errorDict[@"message"]];
  201. }
  202. *error = [FIRAuthErrorUtils webSignInUserInteractionFailureWithReason:reason];
  203. }
  204. return nil;
  205. }
  206. /** @fn getHeadFulLiteURLWithEventID:completion:
  207. @brief Constructs a URL used for opening a headful-lite flow using a given event
  208. ID and session ID.
  209. @param eventID The event ID used for this purpose.
  210. @param sessionID The session ID used when completing the headful lite flow.
  211. @param completion The callback invoked after the URL has been constructed or an error
  212. has been encountered.
  213. */
  214. - (void)getHeadFulLiteURLWithEventID:(NSString *)eventID
  215. sessionID:(NSString *)sessionID
  216. completion:(FIRHeadfulLiteURLCallBack)completion {
  217. __weak __typeof__(self) weakSelf = self;
  218. [FIRAuthWebUtils fetchAuthDomainWithRequestConfiguration:_auth.requestConfiguration
  219. completion:^(NSString *_Nullable authDomain,
  220. NSError *_Nullable error) {
  221. if (error) {
  222. if (completion) {
  223. completion(nil, error);
  224. }
  225. return;
  226. }
  227. __strong __typeof__(self) strongSelf = weakSelf;
  228. NSString *bundleID = [NSBundle mainBundle].bundleIdentifier;
  229. NSString *clienID = strongSelf->_auth.app.options.clientID;
  230. NSString *apiKey = strongSelf->_auth.requestConfiguration.APIKey;
  231. NSMutableDictionary *urlArguments = [@{
  232. @"apiKey" : apiKey,
  233. @"authType" : @"signInWithRedirect",
  234. @"ibi" : bundleID ?: @"",
  235. @"clientId" : clienID,
  236. @"sessionId" : [strongSelf hashforString:sessionID],
  237. @"v" : [FIRAuthBackend authUserAgent],
  238. @"eventId" : eventID,
  239. @"providerId" : strongSelf->_providerID,
  240. } mutableCopy];
  241. if (strongSelf.scopes.count) {
  242. urlArguments[@"scopes"] = [strongSelf.scopes componentsJoinedByString:@","];
  243. }
  244. if (strongSelf.customParameters.count) {
  245. NSString *customParameters = [strongSelf customParametersStringWithError:&error];
  246. if (error) {
  247. completion(nil, error);
  248. return;
  249. }
  250. if (customParameters) {
  251. urlArguments[@"customParameters"] = customParameters;
  252. }
  253. }
  254. if (strongSelf->_auth.requestConfiguration.languageCode) {
  255. urlArguments[@"hl"] = strongSelf->_auth.requestConfiguration.languageCode;
  256. }
  257. NSString *argumentsString = [strongSelf httpArgumentsStringForArgsDictionary:urlArguments];
  258. NSString *URLString =
  259. [NSString stringWithFormat:kHeadfulLiteURLStringFormat, authDomain, argumentsString];
  260. if (completion) {
  261. NSCharacterSet *set = [NSCharacterSet URLFragmentAllowedCharacterSet];
  262. completion([NSURL URLWithString:
  263. [URLString stringByAddingPercentEncodingWithAllowedCharacters:set]], nil);
  264. }
  265. }];
  266. }
  267. /** @fn customParametersString
  268. @brief Returns a JSON string representation of the custom parameters dictionary corresponding
  269. to the OAuthProvider.
  270. @return The JSON string representation of the custom parameters dictionary corresponding
  271. to the OAuthProvider.
  272. */
  273. - (nullable NSString *)customParametersStringWithError:(NSError *_Nullable *_Nullable)error {
  274. if (!_customParameters.count) {
  275. return nil;
  276. }
  277. if (!error) {
  278. return nil;
  279. }
  280. NSError *jsonError;
  281. NSData *customParametersJSONData =
  282. [NSJSONSerialization dataWithJSONObject:_customParameters
  283. options:0
  284. error:&jsonError];
  285. if (jsonError) {
  286. *error = [FIRAuthErrorUtils JSONSerializationErrorWithUnderlyingError:jsonError];
  287. return nil;
  288. }
  289. NSString *customParamsRawJSON =
  290. [[NSString alloc] initWithData:customParametersJSONData encoding:NSUTF8StringEncoding];
  291. return customParamsRawJSON;
  292. }
  293. /** @fn hashforString:
  294. @brief Returns the SHA256 hash representaion of a given string object.
  295. @param string The string for which a SHA256 hash is desired.
  296. @return An hexadecimal string representation of the SHA256 hash.
  297. */
  298. - (NSString *)hashforString:(NSString *)string {
  299. NSData *sessionIDData = [string dataUsingEncoding:NSUTF8StringEncoding];
  300. NSMutableData *hashOutputData = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
  301. if (CC_SHA256(sessionIDData.bytes,
  302. (CC_LONG)[sessionIDData length],
  303. hashOutputData.mutableBytes)) {
  304. }
  305. return [self hexStringFromData:hashOutputData];;
  306. }
  307. /** @fn hexStringFromData:
  308. @brief Returns the hexadecimal string representation of an NSData object.
  309. @param data The NSData object for which a hexadecical string is desired.
  310. @return The hexadecimal string representation of the supplied NSData object.
  311. */
  312. - (NSString *)hexStringFromData:(NSData *)data {
  313. const unsigned char *dataBuffer = (const unsigned char *)[data bytes];
  314. NSMutableString *string = [[NSMutableString alloc] init];
  315. for (unsigned int i = 0; i < data.length; i++){
  316. [string appendFormat:@"%02lx", (unsigned long)dataBuffer[i]];
  317. }
  318. return [string copy];
  319. }
  320. - (NSString *)httpArgumentsStringForArgsDictionary:(NSDictionary *)argsDictionary {
  321. NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:argsDictionary.count];
  322. NSString* key;
  323. for (key in argsDictionary) {
  324. NSString *description = [argsDictionary[key] description];
  325. [arguments addObject:[NSString stringWithFormat:@"%@=%@",
  326. [FIRAuthWebUtils stringByUnescapingFromURLArgument:key],
  327. [FIRAuthWebUtils stringByUnescapingFromURLArgument:description]]] ;
  328. }
  329. return [arguments componentsJoinedByString:@"&"];
  330. }
  331. @end
  332. NS_ASSUME_NONNULL_END