OIDExternalUserAgentIOSCustomBrowser.m 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /*! @file OIDExternalUserAgentIOSCustomBrowser.m
  2. @brief AppAuth iOS SDK
  3. @copyright
  4. Copyright 2018 Google LLC
  5. @copydetails
  6. Licensed under the Apache License, Version 2.0 (the "License");
  7. you may not use this file except in compliance with the License.
  8. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  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 <TargetConditionals.h>
  17. #if TARGET_OS_IOS || TARGET_OS_MACCATALYST
  18. #import "OIDExternalUserAgentIOSCustomBrowser.h"
  19. #import <UIKit/UIKit.h>
  20. #import "OIDAuthorizationRequest.h"
  21. #import "OIDAuthorizationService.h"
  22. #import "OIDErrorUtilities.h"
  23. #import "OIDURLQueryComponent.h"
  24. #if !TARGET_OS_MACCATALYST
  25. NS_ASSUME_NONNULL_BEGIN
  26. @implementation OIDExternalUserAgentIOSCustomBrowser
  27. + (instancetype)CustomBrowserChrome {
  28. // Chrome iOS documentation: https://developer.chrome.com/multidevice/ios/links
  29. OIDCustomBrowserURLTransformation transform = [[self class] URLTransformationSchemeSubstitutionHTTPS:@"googlechromes" HTTP:@"googlechrome"];
  30. NSURL *appStoreURL =
  31. [NSURL URLWithString:@"https://itunes.apple.com/us/app/chrome/id535886823"];
  32. return [[[self class] alloc] initWithURLTransformation:transform
  33. canOpenURLScheme:@"googlechromes"
  34. appStoreURL:appStoreURL];
  35. }
  36. + (instancetype)CustomBrowserFirefox {
  37. // Firefox iOS documentation: https://github.com/mozilla-mobile/firefox-ios-open-in-client
  38. OIDCustomBrowserURLTransformation transform =
  39. [[self class] URLTransformationSchemeConcatPrefix:@"firefox://open-url?url="];
  40. NSURL *appStoreURL =
  41. [NSURL URLWithString:@"https://itunes.apple.com/us/app/firefox-web-browser/id989804926"];
  42. return [[[self class] alloc] initWithURLTransformation:transform
  43. canOpenURLScheme:@"firefox"
  44. appStoreURL:appStoreURL];
  45. }
  46. + (instancetype)CustomBrowserOpera {
  47. OIDCustomBrowserURLTransformation transform =
  48. [[self class] URLTransformationSchemeSubstitutionHTTPS:@"opera-https" HTTP:@"opera-http"];
  49. NSURL *appStoreURL =
  50. [NSURL URLWithString:@"https://itunes.apple.com/us/app/opera-mini-web-browser/id363729560"];
  51. return [[[self class] alloc] initWithURLTransformation:transform
  52. canOpenURLScheme:@"opera-https"
  53. appStoreURL:appStoreURL];
  54. }
  55. + (instancetype)CustomBrowserSafari {
  56. OIDCustomBrowserURLTransformation transformNOP = ^NSURL *(NSURL *requestURL) {
  57. return requestURL;
  58. };
  59. OIDExternalUserAgentIOSCustomBrowser *transform =
  60. [[[self class] alloc] initWithURLTransformation:transformNOP];
  61. return transform;
  62. }
  63. + (OIDCustomBrowserURLTransformation)
  64. URLTransformationSchemeSubstitutionHTTPS:(NSString *)browserSchemeHTTPS
  65. HTTP:(nullable NSString *)browserSchemeHTTP {
  66. OIDCustomBrowserURLTransformation transform = ^NSURL *(NSURL *requestURL) {
  67. // Replace the URL Scheme with the Chrome equivalent.
  68. NSString *newScheme = nil;
  69. if ([requestURL.scheme isEqualToString:@"https"]) {
  70. newScheme = browserSchemeHTTPS;
  71. } else if ([requestURL.scheme isEqualToString:@"http"]) {
  72. if (!browserSchemeHTTP) {
  73. NSAssert(false, @"No HTTP scheme registered for browser");
  74. return nil;
  75. }
  76. newScheme = browserSchemeHTTP;
  77. }
  78. // Replaces the URI scheme with the custom scheme
  79. NSURLComponents *components = [NSURLComponents componentsWithURL:requestURL
  80. resolvingAgainstBaseURL:YES];
  81. components.scheme = newScheme;
  82. return components.URL;
  83. };
  84. return transform;
  85. }
  86. + (OIDCustomBrowserURLTransformation)URLTransformationSchemeConcatPrefix:(NSString *)URLprefix {
  87. OIDCustomBrowserURLTransformation transform = ^NSURL *(NSURL *requestURL) {
  88. NSString *requestURLString = [requestURL absoluteString];
  89. NSMutableCharacterSet *allowedParamCharacters =
  90. [OIDURLQueryComponent URLParamValueAllowedCharacters];
  91. NSString *encodedUrl = [requestURLString stringByAddingPercentEncodingWithAllowedCharacters:allowedParamCharacters];
  92. NSString *newURL = [NSString stringWithFormat:@"%@%@", URLprefix, encodedUrl];
  93. return [NSURL URLWithString:newURL];
  94. };
  95. return transform;
  96. }
  97. - (nullable instancetype)initWithURLTransformation:
  98. (OIDCustomBrowserURLTransformation)URLTransformation {
  99. return [self initWithURLTransformation:URLTransformation canOpenURLScheme:nil appStoreURL:nil];
  100. }
  101. - (nullable instancetype)
  102. initWithURLTransformation:(OIDCustomBrowserURLTransformation)URLTransformation
  103. canOpenURLScheme:(nullable NSString *)canOpenURLScheme
  104. appStoreURL:(nullable NSURL *)appStoreURL {
  105. self = [super init];
  106. if (self) {
  107. _URLTransformation = URLTransformation;
  108. _canOpenURLScheme = canOpenURLScheme;
  109. _appStoreURL = appStoreURL;
  110. }
  111. return self;
  112. }
  113. - (BOOL)presentExternalUserAgentRequest:(nonnull id<OIDExternalUserAgentRequest>)request
  114. session:(nonnull id<OIDExternalUserAgentSession>)session {
  115. // If the app store URL is set, checks if the app is installed and if not opens the app store.
  116. if (_appStoreURL && _canOpenURLScheme) {
  117. // Verifies existence of LSApplicationQueriesSchemes Info.plist key.
  118. NSArray __unused* canOpenURLs =
  119. [[NSBundle mainBundle] objectForInfoDictionaryKey:@"LSApplicationQueriesSchemes"];
  120. NSAssert(canOpenURLs, @"plist missing LSApplicationQueriesSchemes key");
  121. NSAssert1([canOpenURLs containsObject:_canOpenURLScheme],
  122. @"plist missing LSApplicationQueriesSchemes entry for '%@'", _canOpenURLScheme);
  123. // Opens AppStore if app isn't installed
  124. NSString *testURLString = [NSString stringWithFormat:@"%@://example.com", _canOpenURLScheme];
  125. NSURL *testURL = [NSURL URLWithString:testURLString];
  126. if (![[UIApplication sharedApplication] canOpenURL:testURL]) {
  127. [[UIApplication sharedApplication] openURL:_appStoreURL];
  128. return NO;
  129. }
  130. }
  131. // Transforms the request URL and opens it.
  132. NSURL *requestURL = [request externalUserAgentRequestURL];
  133. requestURL = _URLTransformation(requestURL);
  134. BOOL openedInBrowser = [[UIApplication sharedApplication] openURL:requestURL];
  135. return openedInBrowser;
  136. }
  137. - (void)dismissExternalUserAgentAnimated:(BOOL)animated
  138. completion:(nonnull void (^)(void))completion {
  139. completion();
  140. }
  141. @end
  142. NS_ASSUME_NONNULL_END
  143. #endif // !TARGET_OS_MACCATALYST
  144. #endif // TARGET_OS_IOS || TARGET_OS_MACCATALYST