No Description

FIRAuthWebUtils.m 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * Copyright 2018 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. #import "FIRAuthWebUtils.h"
  17. #import "FIRAuthBackend.h"
  18. #import "FIRAuthErrorUtils.h"
  19. #import "FIRGetProjectConfigRequest.h"
  20. #import "FIRGetProjectConfigResponse.h"
  21. NS_ASSUME_NONNULL_BEGIN
  22. @implementation FIRAuthWebUtils
  23. + (NSArray<NSString *> *)supportedAuthDomains {
  24. return @[@"firebaseapp.com", @"web.app"];
  25. }
  26. + (NSString *)randomStringWithLength:(NSUInteger)length {
  27. NSMutableString *randomString = [[NSMutableString alloc] init];
  28. for (int i=0; i < length; i++) {
  29. [randomString appendString:
  30. [NSString stringWithFormat:@"%c", 'a' + arc4random_uniform('z' - 'a' + 1)]];
  31. }
  32. return randomString;
  33. }
  34. + (BOOL)isCallbackSchemeRegisteredForCustomURLScheme:(NSString *)URLScheme {
  35. NSString *expectedCustomScheme = [URLScheme lowercaseString];
  36. NSArray *urlTypes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"];
  37. for (NSDictionary *urlType in urlTypes) {
  38. NSArray *urlTypeSchemes = urlType[@"CFBundleURLSchemes"];
  39. for (NSString *urlTypeScheme in urlTypeSchemes) {
  40. if ([urlTypeScheme.lowercaseString isEqualToString:expectedCustomScheme]) {
  41. return YES;
  42. }
  43. }
  44. }
  45. return NO;
  46. }
  47. + (BOOL)isExpectedCallbackURL:(nullable NSURL *)URL
  48. eventID:(NSString *)eventID
  49. authType:(NSString *)authType
  50. callbackScheme:(NSString *)callbackScheme {
  51. if (!URL) {
  52. return NO;
  53. }
  54. NSURLComponents *actualURLComponents =
  55. [NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:NO];
  56. actualURLComponents.query = nil;
  57. actualURLComponents.fragment = nil;
  58. NSURLComponents *expectedURLComponents = [[NSURLComponents alloc] init];
  59. expectedURLComponents.scheme = callbackScheme;
  60. expectedURLComponents.host = @"firebaseauth";
  61. expectedURLComponents.path = @"/link";
  62. if (![expectedURLComponents.URL isEqual:actualURLComponents.URL]) {
  63. return NO;
  64. }
  65. NSDictionary<NSString *, NSString *> *URLQueryItems =
  66. [self dictionaryWithHttpArgumentsString:URL.query];
  67. NSURL *deeplinkURL = [NSURL URLWithString:URLQueryItems[@"deep_link_id"]];
  68. NSDictionary<NSString *, NSString *> *deeplinkQueryItems =
  69. [self dictionaryWithHttpArgumentsString:deeplinkURL.query];
  70. if ([deeplinkQueryItems[@"authType"] isEqualToString:authType] &&
  71. [deeplinkQueryItems[@"eventId"] isEqualToString:eventID]) {
  72. return YES;
  73. }
  74. return NO;
  75. }
  76. + (void)fetchAuthDomainWithRequestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration
  77. completion:(FIRFetchAuthDomainCallback)completion {
  78. FIRGetProjectConfigRequest *request =
  79. [[FIRGetProjectConfigRequest alloc] initWithRequestConfiguration:requestConfiguration];
  80. [FIRAuthBackend getProjectConfig:request
  81. callback:^(FIRGetProjectConfigResponse *_Nullable response,
  82. NSError *_Nullable error) {
  83. if (error) {
  84. completion(nil, error);
  85. return;
  86. }
  87. // Look up an authorized domain ends with one of the supportedAuthDomains.
  88. // The sequence of supportedAuthDomains matters. ("firebaseapp.com", "web.app")
  89. // The searching ends once the first valid suportedAuthDomain is found.
  90. NSString *authDomain;
  91. for (NSString *domain in response.authorizedDomains) {
  92. for (NSString *suportedAuthDomain in [self supportedAuthDomains]) {
  93. NSInteger index = domain.length - suportedAuthDomain.length;
  94. if (index >= 2) {
  95. if ([domain hasSuffix:suportedAuthDomain] && domain.length >= suportedAuthDomain.length + 2) {
  96. authDomain = domain;
  97. break;
  98. }
  99. }
  100. }
  101. if (authDomain != nil) {
  102. break;
  103. }
  104. }
  105. if (!authDomain.length) {
  106. completion(nil, [FIRAuthErrorUtils unexpectedErrorResponseWithDeserializedResponse:response]);
  107. return;
  108. }
  109. completion(authDomain, nil);
  110. }];
  111. }
  112. /** @fn queryItemValue:from:
  113. @brief Utility function to get a value from a NSURLQueryItem array.
  114. @param name The key.
  115. @param queryList The NSURLQueryItem array.
  116. @return The value for the key.
  117. */
  118. + (nullable NSString *)queryItemValue:(NSString *)name from:(NSArray<NSURLQueryItem *> *)queryList {
  119. for (NSURLQueryItem *item in queryList) {
  120. if ([item.name isEqualToString:name]) {
  121. return item.value;
  122. }
  123. }
  124. return nil;
  125. }
  126. + (NSDictionary *)dictionaryWithHttpArgumentsString:(NSString *)argString {
  127. NSMutableDictionary* ret = [NSMutableDictionary dictionary];
  128. NSArray* components = [argString componentsSeparatedByString:@"&"];
  129. NSString* component;
  130. // Use reverse order so that the first occurrence of a key replaces
  131. // those subsequent.
  132. for (component in [components reverseObjectEnumerator]) {
  133. if (component.length == 0)
  134. continue;
  135. NSRange pos = [component rangeOfString:@"="];
  136. NSString *key;
  137. NSString *val;
  138. if (pos.location == NSNotFound) {
  139. key = [self stringByUnescapingFromURLArgument:component];
  140. val = @"";
  141. } else {
  142. key = [self stringByUnescapingFromURLArgument:[component substringToIndex:pos.location]];
  143. val = [self stringByUnescapingFromURLArgument:
  144. [component substringFromIndex:pos.location + pos.length]];
  145. }
  146. // returns nil on invalid UTF8 and NSMutableDictionary raises an exception when passed nil
  147. // values.
  148. if (!key) key = @"";
  149. if (!val) val = @"";
  150. [ret setObject:val forKey:key];
  151. }
  152. return ret;
  153. }
  154. + (NSString *)stringByUnescapingFromURLArgument:(NSString *)argument {
  155. NSMutableString *resultString = [NSMutableString stringWithString:argument];
  156. [resultString replaceOccurrencesOfString:@"+"
  157. withString:@" "
  158. options:NSLiteralSearch
  159. range:NSMakeRange(0, [resultString length])];
  160. return [resultString stringByRemovingPercentEncoding];
  161. }
  162. + (NSDictionary<NSString *, NSString *> *)parseURL:(NSString *)urlString {
  163. NSString *linkURL = [NSURLComponents componentsWithString:urlString].query;
  164. if (!linkURL) {
  165. return @{};
  166. }
  167. NSArray<NSString *> *URLComponents = [linkURL componentsSeparatedByString:@"&"];
  168. NSMutableDictionary<NSString *, NSString *> *queryItems =
  169. [[NSMutableDictionary alloc] initWithCapacity:URLComponents.count];
  170. for (NSString *component in URLComponents) {
  171. NSRange equalRange = [component rangeOfString:@"="];
  172. if (equalRange.location != NSNotFound) {
  173. NSString *queryItemKey =
  174. [[component substringToIndex:equalRange.location] stringByRemovingPercentEncoding];
  175. NSString *queryItemValue =
  176. [[component substringFromIndex:equalRange.location + 1] stringByRemovingPercentEncoding];
  177. if (queryItemKey && queryItemValue) {
  178. queryItems[queryItemKey] = queryItemValue;
  179. }
  180. }
  181. }
  182. return queryItems;
  183. }
  184. @end
  185. NS_ASSUME_NONNULL_END