| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge.h" |
| |
| #import <Foundation/Foundation.h> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/mac/foundation_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "ios/chrome/browser/signin/feature_flags.h" |
| #include "ios/net/cookies/system_cookie_util.h" |
| #include "ios/web/public/browser_state.h" |
| #include "ios/web/public/features.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| @interface GaiaAuthFetcherIOSURLSessionDelegate |
| : NSObject <NSURLSessionTaskDelegate> |
| |
| // Gaia auth fetcher bridge. |
| @property(nonatomic, assign) GaiaAuthFetcherIOSNSURLSessionBridge* bridge; |
| // Session for the multilogin request. |
| @property(nonatomic, strong) NSURLSession* requestSession; |
| |
| @end |
| |
| @implementation GaiaAuthFetcherIOSURLSessionDelegate |
| |
| @synthesize bridge = _bridge; |
| @synthesize requestSession = _requestSession; |
| |
| #pragma mark - NSURLSessionTaskDelegate |
| |
| - (void)URLSession:(NSURLSession*)session |
| task:(NSURLSessionTask*)task |
| willPerformHTTPRedirection:(NSHTTPURLResponse*)response |
| newRequest:(NSURLRequest*)request |
| completionHandler:(void (^)(NSURLRequest*))completionHandler { |
| // If there is a redirect, the cookies from the redirect need to be stored. |
| DCHECK(self.requestSession == session); |
| if (self.bridge) { |
| self.bridge->SetCanonicalCookiesFromResponse(response); |
| completionHandler(request); |
| } else { |
| // No need to continue the redirect if there is no more bridge instance. |
| completionHandler(NULL); |
| } |
| } |
| |
| #pragma mark - Private |
| |
| - (void)requestCompletedWithData:(NSData*)data |
| response:(NSURLResponse*)response |
| error:(NSError*)error { |
| if (!self.bridge) |
| return; |
| NSHTTPURLResponse* responseWithHeaders = |
| base::mac::ObjCCastStrict<NSHTTPURLResponse>(response); |
| if (error) { |
| self.bridge->OnURLFetchFailure(net::ERR_FAILED, |
| responseWithHeaders.statusCode); |
| } else { |
| self.bridge->SetCanonicalCookiesFromResponse(responseWithHeaders); |
| NSString* result = [[NSString alloc] initWithData:data |
| encoding:NSUTF8StringEncoding]; |
| self.bridge->OnURLFetchSuccess(base::SysNSStringToUTF8(result), |
| responseWithHeaders.statusCode); |
| } |
| } |
| |
| @end |
| |
| GaiaAuthFetcherIOSNSURLSessionBridge::GaiaAuthFetcherIOSNSURLSessionBridge( |
| GaiaAuthFetcherIOSBridge::GaiaAuthFetcherIOSBridgeDelegate* delegate, |
| web::BrowserState* browser_state) |
| : GaiaAuthFetcherIOSBridge(delegate, browser_state) { |
| DCHECK(base::FeatureList::IsEnabled(kUseNSURLSessionForGaiaSigninRequests)); |
| DCHECK(base::FeatureList::IsEnabled(web::features::kWKHTTPSystemCookieStore)); |
| url_session_delegate_ = [[GaiaAuthFetcherIOSURLSessionDelegate alloc] init]; |
| url_session_delegate_.bridge = this; |
| } |
| |
| GaiaAuthFetcherIOSNSURLSessionBridge::~GaiaAuthFetcherIOSNSURLSessionBridge() { |
| url_session_delegate_.bridge = nullptr; |
| } |
| |
| void GaiaAuthFetcherIOSNSURLSessionBridge::FetchPendingRequest() { |
| network::mojom::CookieManager* cookie_manager = |
| GetBrowserState()->GetCookieManager(); |
| net::CookieOptions options; |
| options.set_include_httponly(); |
| options.set_same_site_cookie_mode( |
| net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); |
| cookie_manager->GetCookieList( |
| GetRequest().url, options, |
| base::BindOnce( |
| &GaiaAuthFetcherIOSNSURLSessionBridge::FetchPendingRequestWithCookies, |
| base::Unretained(this))); |
| } |
| |
| void GaiaAuthFetcherIOSNSURLSessionBridge::Cancel() { |
| [url_session_data_task_ cancel]; |
| OnURLFetchFailure(net::ERR_ABORTED, 0); |
| } |
| |
| void GaiaAuthFetcherIOSNSURLSessionBridge::SetCanonicalCookiesFromResponse( |
| NSHTTPURLResponse* response) { |
| NSArray* cookies = |
| [NSHTTPCookie cookiesWithResponseHeaderFields:response.allHeaderFields |
| forURL:response.URL]; |
| network::mojom::CookieManager* cookie_manager = |
| GetBrowserState()->GetCookieManager(); |
| for (NSHTTPCookie* cookie : cookies) { |
| cookie_manager->SetCanonicalCookie( |
| net::CanonicalCookieFromSystemCookie(cookie, base::Time::Now()), |
| /*secure_source=*/true, |
| /*modify_http_only=*/true, base::DoNothing()); |
| } |
| } |
| |
| void GaiaAuthFetcherIOSNSURLSessionBridge::FetchPendingRequestWithCookies( |
| const std::vector<net::CanonicalCookie>& cookies) { |
| DCHECK(!url_session_); |
| url_session_ = CreateNSURLSession(url_session_delegate_); |
| url_session_delegate_.requestSession = url_session_; |
| DCHECK(!url_session_data_task_); |
| __weak __typeof(url_session_delegate_) weakDelegate = url_session_delegate_; |
| url_session_data_task_ = |
| [url_session_ dataTaskWithRequest:GetNSURLRequest() |
| completionHandler:^(NSData* data, NSURLResponse* response, |
| NSError* error) { |
| [weakDelegate requestCompletedWithData:data |
| response:response |
| error:error]; |
| }]; |
| NSMutableArray* http_cookies = |
| [[NSMutableArray alloc] initWithCapacity:cookies.size()]; |
| for (const net::CanonicalCookie& cookie : cookies) { |
| [http_cookies addObject:net::SystemCookieFromCanonicalCookie(cookie)]; |
| } |
| [url_session_.configuration.HTTPCookieStorage |
| storeCookies:http_cookies |
| forTask:url_session_data_task_]; |
| |
| [url_session_data_task_ resume]; |
| } |
| |
| NSURLSession* GaiaAuthFetcherIOSNSURLSessionBridge::CreateNSURLSession( |
| id<NSURLSessionTaskDelegate> url_session_delegate) { |
| NSURLSessionConfiguration* session_configuration = |
| NSURLSessionConfiguration.ephemeralSessionConfiguration; |
| session_configuration.HTTPShouldSetCookies = YES; |
| return [NSURLSession sessionWithConfiguration:session_configuration |
| delegate:url_session_delegate |
| delegateQueue:NSOperationQueue.mainQueue]; |
| } |