PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Pods/NXOAuth2Client/Sources/OAuth2Client/NXOAuth2Connection.m

https://gitlab.com/duongbadu/Instagram-app-iOS-Coursera
Objective C | 590 lines | 441 code | 119 blank | 30 comment | 118 complexity | c83b573f58af9016448370a2f765c700 MD5 | raw file
  1. //
  2. // NXOAuth2Connection.m
  3. // OAuth2Client
  4. //
  5. // Created by Ullrich Schäfer on 27.08.10.
  6. //
  7. // Copyright 2010 nxtbgthng. All rights reserved.
  8. //
  9. // Licenced under the new BSD-licence.
  10. // See README.md in this repository for
  11. // the full licence.
  12. //
  13. #import "NSURL+NXOAuth2.h"
  14. #import "NSData+NXOAuth2.h"
  15. #import "NXOAuth2PostBodyStream.h"
  16. #import "NXOAuth2ConnectionDelegate.h"
  17. #import "NXOAuth2Client.h"
  18. #import "NXOAuth2AccessToken.h"
  19. #import "NXOAuth2Connection.h"
  20. @interface NXOAuth2Client (Private)
  21. - (void)removeConnectionFromWaitingQueue:(NXOAuth2Connection *)connection;
  22. @end
  23. NSString * const NXOAuth2ConnectionDidStartNotification = @"NXOAuth2ConnectionDidStartNotification";
  24. NSString * const NXOAuth2ConnectionDidEndNotification = @"NXOAuth2ConnectionDidEndNotification";
  25. @interface NXOAuth2Connection ()
  26. - (NSURLConnection *)createConnection;
  27. - (NSString *)descriptionForRequest:(NSURLRequest *)request;
  28. - (void)applyParameters:(NSDictionary *)parameters onRequest:(NSMutableURLRequest *)request;
  29. - (BOOL)trustsAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
  30. forHostname:(NSString *)hostname
  31. withTrustMode:(NXOAuth2TrustMode)trustMode;
  32. - (BOOL)isServerCertificateForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
  33. andHostname:(NSString *)hostname
  34. matchingCertificate:(NSData *)derCertData;
  35. @property (nonatomic, unsafe_unretained, readonly) id<NXOAuth2TrustDelegate> trustDelegate;
  36. @end
  37. @implementation NXOAuth2Connection
  38. #pragma mark Lifecycle
  39. - (id)initWithRequest:(NSMutableURLRequest *)aRequest
  40. requestParameters:(NSDictionary *)someRequestParameters
  41. oauthClient:(NXOAuth2Client *)aClient
  42. sendingProgressHandler:(NXOAuth2ConnectionSendingProgressHandler)aSendingProgressHandler
  43. responseHandler:(NXOAuth2ConnectionResponseHandler)aResponseHandler;
  44. {
  45. self = [self initWithRequest:aRequest requestParameters:someRequestParameters oauthClient:aClient delegate:nil];
  46. if (self) {
  47. sendingProgressHandler = [aSendingProgressHandler copy];
  48. responseHandler = [aResponseHandler copy];
  49. }
  50. return self;
  51. }
  52. - (id)initWithRequest:(NSMutableURLRequest *)aRequest
  53. requestParameters:(NSDictionary *)someRequestParameters
  54. oauthClient:(NXOAuth2Client *)aClient
  55. delegate:(NSObject<NXOAuth2ConnectionDelegate> *)aDelegate;
  56. {
  57. self = [super init];
  58. if (self) {
  59. sendConnectionDidEndNotification = NO;
  60. delegate = aDelegate; // assign only
  61. client = aClient;
  62. request = [aRequest copy];
  63. requestParameters = [someRequestParameters copy];
  64. connection = [self createConnection];
  65. savesData = YES;
  66. }
  67. return self;
  68. }
  69. - (void)dealloc;
  70. {
  71. if (sendConnectionDidEndNotification) [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2ConnectionDidEndNotification object:self];
  72. sendConnectionDidEndNotification = NO;
  73. [connection cancel];
  74. }
  75. #pragma mark Accessors
  76. @synthesize delegate;
  77. @synthesize data;
  78. @synthesize context, userInfo;
  79. @synthesize savesData;
  80. @synthesize client;
  81. @synthesize response;
  82. - (id<NXOAuth2TrustDelegate>)trustDelegate;
  83. {
  84. // if a client is set and implemnts the trustModeForHostname: it is preferred
  85. // in making trust desicions.
  86. // The second choice
  87. if (client && [client.delegate conformsToProtocol:@protocol(NXOAuth2TrustDelegate)]) {
  88. return (id<NXOAuth2TrustDelegate>)client.delegate;
  89. } else if ([delegate conformsToProtocol:@protocol(NXOAuth2TrustDelegate)]) {
  90. return (id<NXOAuth2TrustDelegate>)delegate;
  91. }
  92. return nil;
  93. }
  94. - (NSInteger)statusCode;
  95. {
  96. if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
  97. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  98. return httpResponse.statusCode;
  99. }
  100. return 0;
  101. }
  102. - (long long)expectedContentLength;
  103. {
  104. return response.expectedContentLength;
  105. }
  106. - (NSString *)description;
  107. {
  108. return [NSString stringWithFormat:@"NXOAuth2Connection <%@>", request.URL];
  109. }
  110. #pragma mark Public
  111. - (void)cancel;
  112. {
  113. if (sendConnectionDidEndNotification) [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2ConnectionDidEndNotification object:self];
  114. sendConnectionDidEndNotification = NO;
  115. [connection cancel];
  116. [client removeConnectionFromWaitingQueue:self];
  117. }
  118. - (void)retry;
  119. {
  120. response = nil;
  121. [connection cancel];
  122. connection = [self createConnection];
  123. }
  124. #pragma mark Private
  125. - (NSURLConnection *)createConnection;
  126. {
  127. // if the request is a token refresh request don't sign it and don't check for the expiration of the token (we know that already)
  128. NSString *oauthAuthorizationHeader = nil;
  129. if (client.accessToken &&
  130. ![[requestParameters objectForKey:@"grant_type"] isEqualToString:@"refresh_token"]) {
  131. // if token is expired don't bother starting this connection.
  132. NSDate *tenSecondsAgo = [NSDate dateWithTimeIntervalSinceNow:(-10)];
  133. NSDate *tokenExpiresAt = client.accessToken.expiresAt;
  134. if (client.accessToken.refreshToken && [tenSecondsAgo earlierDate:tokenExpiresAt] == tokenExpiresAt) {
  135. [self cancel];
  136. [client refreshAccessTokenAndRetryConnection:self];
  137. return nil;
  138. }
  139. NSString *tokenType = client.accessToken.tokenType;
  140. if (tokenType == nil) {
  141. tokenType = client.tokenType;
  142. }
  143. if (tokenType == nil) {
  144. tokenType = @"OAuth";
  145. }
  146. oauthAuthorizationHeader = [NSString stringWithFormat:@"%@ %@", tokenType, client.accessToken.accessToken];
  147. }
  148. NSMutableURLRequest *startRequest = [request mutableCopy];
  149. [self applyParameters:requestParameters onRequest:startRequest];
  150. if (oauthAuthorizationHeader) {
  151. [startRequest setValue:oauthAuthorizationHeader forHTTPHeaderField:@"Authorization"];
  152. }
  153. if (client.userAgent && ![startRequest valueForHTTPHeaderField:@"User-Agent"]) {
  154. [startRequest setValue:client.userAgent forHTTPHeaderField:@"User-Agent"];
  155. }
  156. if (client.acceptType) {
  157. [startRequest setValue:client.acceptType forHTTPHeaderField:@"Accept"];
  158. }
  159. NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:startRequest delegate:self startImmediately:NO]; // don't start yet
  160. [aConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; // let's first schedule it in the current runloop. (see http://github.com/soundcloud/cocoa-api-wrapper/issues#issue/2 )
  161. [aConnection start]; // now start
  162. if (!sendConnectionDidEndNotification) [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2ConnectionDidStartNotification object:self];
  163. sendConnectionDidEndNotification = YES;
  164. return aConnection;
  165. }
  166. - (NSString *)descriptionForRequest:(NSURLRequest *)aRequest;
  167. {
  168. NSString *range = [aRequest valueForHTTPHeaderField:@"Range"];
  169. if (!range) {
  170. return aRequest.URL.absoluteString;
  171. }
  172. return [NSString stringWithFormat:@"%@ [%@]", aRequest.URL.absoluteString, range];
  173. }
  174. - (void)applyParameters:(NSDictionary *)parameters onRequest:(NSMutableURLRequest *)aRequest;
  175. {
  176. if (!parameters) return;
  177. NSString *httpMethod = [aRequest HTTPMethod];
  178. if ([httpMethod caseInsensitiveCompare:@"POST"] != NSOrderedSame
  179. && [httpMethod caseInsensitiveCompare:@"PUT"] != NSOrderedSame) {
  180. aRequest.URL = [aRequest.URL nxoauth2_URLByAddingParameters:parameters];
  181. } else {
  182. NSString *contentType = [aRequest valueForHTTPHeaderField:@"Content-Type"];
  183. if (!contentType || [contentType isEqualToString:@"multipart/form-data"]) {
  184. // sends the POST/PUT request as multipart/form-data as default
  185. NSInputStream *postBodyStream = [[NXOAuth2PostBodyStream alloc] initWithParameters:parameters];
  186. contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",[(NXOAuth2PostBodyStream *)postBodyStream boundary]];
  187. NSString *contentLength = [NSString stringWithFormat:@"%lld", [(NXOAuth2PostBodyStream *)postBodyStream length]];
  188. [aRequest setValue:contentType forHTTPHeaderField:@"Content-Type"];
  189. [aRequest setValue:contentLength forHTTPHeaderField:@"Content-Length"];
  190. [aRequest setHTTPBodyStream:postBodyStream];
  191. } else if ([contentType isEqualToString:@"application/x-www-form-urlencoded"]) {
  192. // sends the POST/PUT request as application/x-www-form-urlencoded
  193. NSString *query = [[aRequest.URL nxoauth2_URLByAddingParameters:parameters] query];
  194. [aRequest setHTTPBody:[query dataUsingEncoding:NSUTF8StringEncoding]];
  195. }
  196. }
  197. }
  198. - (BOOL)trustsAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
  199. forHostname:(NSString *)hostname
  200. withTrustMode:(NXOAuth2TrustMode)trustMode;
  201. {
  202. if (trustMode & NXOAuth2TrustModeAnyCertificate) {
  203. return YES;
  204. }
  205. if (trustMode & NXOAuth2TrustModeSystem) {
  206. SecTrustResultType trustEvalResult = kSecTrustResultInvalid;
  207. OSStatus ossTrust = SecTrustEvaluate(challenge.protectionSpace.serverTrust, &trustEvalResult);
  208. if (ossTrust != errSecSuccess) {
  209. NSLog(@"Trust evaluation failed for domain %@. Rejecting cert.", hostname);
  210. return NO;
  211. }
  212. // TODO: The result might also be kSecTrustResultConfirm
  213. // But to be safe we ignore this for now
  214. // if it is kSecTrustResultConfirm, there could be another delegate
  215. // method that allows to show a delegate UI
  216. if (trustEvalResult == kSecTrustResultProceed ||
  217. trustEvalResult == kSecTrustResultUnspecified) {
  218. return YES;
  219. }
  220. }
  221. if (trustMode & NXOAuth2TrustModeSpecificCertificate) {
  222. NSAssert([self.trustDelegate respondsToSelector:@selector(connection:trustedCertificatesForHostname:)],
  223. @"For NXOAuth2TrustModeSpecificCertificate the delegate needs to implement oauthConnection:trustedCertificatesDERDataForHostname:");
  224. NSArray *trustedCerts = [self.trustDelegate connection:self trustedCertificatesForHostname:hostname];
  225. for (NSData* trustedCert in trustedCerts) {
  226. if ([self isServerCertificateForAuthenticationChallenge:challenge
  227. andHostname:hostname
  228. matchingCertificate:trustedCert]) {
  229. return YES;
  230. }
  231. }
  232. }
  233. return NO;
  234. }
  235. - (BOOL)isServerCertificateForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
  236. andHostname:(NSString *)hostname
  237. matchingCertificate:(NSData *)derCertData;
  238. {
  239. if (derCertData == nil) {
  240. return NO;
  241. }
  242. SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
  243. SecCertificateRef anchorCert = SecCertificateCreateWithData(NULL,(__bridge CFDataRef)derCertData);
  244. if(anchorCert == nil) {
  245. return NO;
  246. }
  247. CFArrayRef allTrustedCert = (__bridge CFArrayRef)[NSArray arrayWithObject:(__bridge id)anchorCert];
  248. CFRelease(anchorCert);
  249. SecTrustSetAnchorCertificates(serverTrust, allTrustedCert);
  250. SecTrustSetAnchorCertificatesOnly(serverTrust, YES);
  251. SecTrustResultType checkResult;
  252. OSStatus ossTrust = SecTrustEvaluate(serverTrust, &checkResult);
  253. if (ossTrust != errSecSuccess) {
  254. return NO;
  255. }
  256. if (checkResult == kSecTrustResultProceed || checkResult == kSecTrustResultUnspecified) {
  257. return YES;
  258. } else if (checkResult == kSecTrustResultRecoverableTrustFailure) {
  259. // In this case me check if any of the certs is our trusted cert.
  260. OSStatus errGetTrustResult = noErr;
  261. NSMutableArray *certificates = [NSMutableArray array];
  262. #if TARGET_OS_IPHONE || MAC_OS_X_VERSION_10_7
  263. // The iOS/10.7+ way of getting the certs.
  264. for (CFIndex index = 0; index < SecTrustGetCertificateCount(serverTrust); index++) {
  265. SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, index);
  266. [certificates addObject:(__bridge id)certificate];
  267. }
  268. #else
  269. // OS X way of getting to the certs.
  270. CSSM_TP_APPLE_EVIDENCE_INFO *statusChain;
  271. CFArrayRef cfCertificates;
  272. errGetTrustResult = SecTrustGetResult(serverTrust, &checkResult, &cfCertificates, &statusChain);
  273. certificates = (__bridge NSArray*)cfCertificates;
  274. #endif
  275. if (errGetTrustResult == noErr) {
  276. // find if any cert in the chain matches the provided cert.
  277. for (id certificate in certificates) {
  278. CFDataRef certData = SecCertificateCopyData((__bridge SecCertificateRef)certificate);
  279. NSString *certificateChecksum = [(__bridge_transfer NSData *)certData nx_SHA1Hexdigest];
  280. NSString *anchorCertChecksum = [derCertData nx_SHA1Hexdigest];
  281. if ([anchorCertChecksum isEqualToString:certificateChecksum]) {
  282. return YES;
  283. }
  284. }
  285. return NO;
  286. } else {
  287. return NO;
  288. }
  289. }
  290. return NO;
  291. }
  292. #pragma mark -
  293. #pragma mark SCPostBodyStream Delegate
  294. - (void)stream:(NXOAuth2PostBodyStream *)stream didSendBytes:(unsigned long long)deliveredBytes ofTotal:(unsigned long long)totalBytes;
  295. {
  296. if ([delegate respondsToSelector:@selector(oauthConnection:didSendBytes:ofTotal:)]){
  297. [delegate oauthConnection:self didSendBytes:deliveredBytes ofTotal:totalBytes];
  298. }
  299. }
  300. #pragma mark NSURLConnectionDelegate
  301. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)theResponse;
  302. {
  303. #if (NXOAuth2ConnectionDebug)
  304. NSLog(@"%.0fms (RESP) - %@", -[startDate timeIntervalSinceNow]*1000.0, [self descriptionForRequest:request]);
  305. #endif
  306. response = theResponse;
  307. if (savesData) {
  308. if (!data) {
  309. data = [[NSMutableData alloc] init];
  310. } else {
  311. [data setLength:0];
  312. }
  313. }
  314. NSString *authenticateHeader = nil;
  315. if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
  316. NSDictionary *headerFields = [(NSHTTPURLResponse *)response allHeaderFields];
  317. for (NSString *headerKey in headerFields.allKeys) {
  318. if ([[headerKey lowercaseString] isEqualToString:@"www-authenticate"]) {
  319. authenticateHeader = [headerFields objectForKey:headerKey];
  320. break;
  321. }
  322. }
  323. }
  324. if (/*self.statusCode == 401 // TODO: check for status code once the bug returning 500 is fixed
  325. &&*/ client.accessToken.refreshToken != nil
  326. && authenticateHeader
  327. && [authenticateHeader rangeOfString:@"expired_token"].location != NSNotFound) {
  328. [self cancel];
  329. [client refreshAccessTokenAndRetryConnection:self];
  330. } else {
  331. if ([delegate respondsToSelector:@selector(oauthConnection:didReceiveData:)]) {
  332. [delegate oauthConnection:self didReceiveData:data]; // inform the delegate that we start with empty data
  333. }
  334. if ([delegate respondsToSelector:@selector(oauthConnection:didReceiveResponse:)]) {
  335. [delegate oauthConnection:self didReceiveResponse:theResponse];
  336. }
  337. }
  338. }
  339. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)someData;
  340. {
  341. if (savesData) {
  342. [data appendData:someData];
  343. }
  344. if ([delegate respondsToSelector:@selector(oauthConnection:didReceiveData:)]) {
  345. [delegate oauthConnection:self didReceiveData:someData];
  346. }
  347. }
  348. - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
  349. {
  350. #if (NXOAuth2ConnectionDebug)
  351. NSLog(@"%.0fms (SUCC) - %@", -[startDate timeIntervalSinceNow]*1000.0, [self descriptionForRequest:request]);
  352. #endif
  353. if (sendConnectionDidEndNotification) [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2ConnectionDidEndNotification object:self];
  354. sendConnectionDidEndNotification = NO;
  355. if(self.statusCode < 400) {
  356. if ([delegate respondsToSelector:@selector(oauthConnection:didFinishWithData:)]) {
  357. [delegate oauthConnection:self didFinishWithData:data];
  358. }
  359. if (responseHandler) responseHandler(response, data, nil);
  360. } else {
  361. if (self.statusCode == 401) {
  362. // check if token is still valid
  363. NSString *authenticateHeader = nil;
  364. if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
  365. NSDictionary *headerFields = [(NSHTTPURLResponse *)response allHeaderFields];
  366. for (NSString *headerKey in headerFields.allKeys) {
  367. if ([[headerKey lowercaseString] isEqualToString:@"www-authenticate"]) {
  368. authenticateHeader = [headerFields objectForKey:headerKey];
  369. break;
  370. }
  371. }
  372. }
  373. if (authenticateHeader
  374. && [authenticateHeader rangeOfString:@"invalid_token"].location != NSNotFound) {
  375. client.accessToken = nil;
  376. }
  377. }
  378. NSString *localizedError = [NSString stringWithFormat:NSLocalizedString(@"HTTP Error: %d", @"NXOAuth2HTTPErrorDomain description"), self.statusCode];
  379. NSDictionary *errorUserInfo = [NSDictionary dictionaryWithObject:localizedError forKey:NSLocalizedDescriptionKey];
  380. NSError *error = [NSError errorWithDomain:NXOAuth2HTTPErrorDomain
  381. code:self.statusCode
  382. userInfo:errorUserInfo];
  383. if ([delegate respondsToSelector:@selector(oauthConnection:didFailWithError:)]) {
  384. [delegate oauthConnection:self didFailWithError:error];
  385. }
  386. if (responseHandler) responseHandler (response, data, error);
  387. }
  388. }
  389. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
  390. {
  391. #if (NXOAuth2ConnectionDebug)
  392. NSLog(@"%.0fms (FAIL) - %@ (%@ %i)", -[startDate timeIntervalSinceNow]*1000.0, [self descriptionForRequest:request], [error domain], [error code]);
  393. #endif
  394. if (sendConnectionDidEndNotification) [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2ConnectionDidEndNotification object:self];
  395. sendConnectionDidEndNotification = NO;
  396. if ([delegate respondsToSelector:@selector(oauthConnection:didFailWithError:)]) {
  397. [delegate oauthConnection:self didFailWithError:error];
  398. }
  399. if (responseHandler) responseHandler (response, data, error);
  400. }
  401. - (NSURLRequest *)connection:(NSURLConnection *)aConnection willSendRequest:(NSURLRequest *)aRequest redirectResponse:(NSURLResponse *)aRedirectResponse;
  402. {
  403. if (!aRedirectResponse) {
  404. #if (NXOAuth2ConnectionDebug)
  405. NSLog(@"%.0fms (WILL) - %@", -[startDate timeIntervalSinceNow]*1000.0, [self descriptionForRequest:aRequest]);
  406. #endif
  407. return aRequest; // if not redirecting do nothing
  408. }
  409. if ([delegate respondsToSelector:@selector(oauthConnection:didReceiveRedirectToURL:)]) {
  410. [delegate oauthConnection:self didReceiveRedirectToURL:aRequest.URL];
  411. }
  412. #if (NXOAuth2ConnectionDebug)
  413. NSLog(@"%.0fms (REDI) - %@ > %@", -[startDate timeIntervalSinceNow]*1000.0, aRedirectResponse.URL.absoluteString, [self descriptionForRequest:aRequest]);
  414. #endif
  415. BOOL hostChanged = [aRequest.URL.host caseInsensitiveCompare:aRedirectResponse.URL.host] != NSOrderedSame;
  416. BOOL schemeChanged = [aRequest.URL.scheme caseInsensitiveCompare:aRedirectResponse.URL.scheme] != NSOrderedSame;
  417. BOOL schemeChangedToHTTPS = schemeChanged && ([aRequest.URL.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame);
  418. NSMutableURLRequest *mutableRequest = [aRequest mutableCopy];
  419. mutableRequest.HTTPMethod = request.HTTPMethod;
  420. if (hostChanged || (schemeChanged && !schemeChangedToHTTPS)) {
  421. [mutableRequest setValue:nil forHTTPHeaderField:@"Authorization"]; // strip Authorization information
  422. return mutableRequest;
  423. } else {
  424. // iOS 5 automaticaly strips the authorization 'token' from the header.
  425. // Thus we have to add the OAuth2 'token' again.
  426. [mutableRequest setValue:[NSString stringWithFormat:@"%@ %@", client.accessToken.tokenType, client.accessToken.accessToken]
  427. forHTTPHeaderField:@"Authorization"];
  428. }
  429. return mutableRequest;
  430. }
  431. - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
  432. {
  433. if ([delegate respondsToSelector:@selector(oauthConnection:didSendBytes:ofTotal:)]) {
  434. [delegate oauthConnection:self didSendBytes:totalBytesWritten ofTotal:totalBytesExpectedToWrite];
  435. }
  436. if (sendingProgressHandler) sendingProgressHandler(totalBytesWritten, totalBytesExpectedToWrite);
  437. }
  438. - (NSInputStream *)connection:(NSURLConnection *)connection needNewBodyStream:(NSURLRequest *)aRequest;
  439. {
  440. return [[NXOAuth2PostBodyStream alloc] initWithParameters:requestParameters];
  441. }
  442. - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
  443. {
  444. return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
  445. }
  446. - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
  447. {
  448. if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
  449. NSString *hostname = challenge.protectionSpace.host;
  450. NXOAuth2TrustMode effectiveTrustMode = NXOAuth2TrustModeSystem;
  451. if ([self.trustDelegate respondsToSelector:@selector(connection:trustModeForHostname:)]) {
  452. effectiveTrustMode = [self.trustDelegate connection:self trustModeForHostname:hostname];
  453. }
  454. BOOL shouldTrustCerificate = [self trustsAuthenticationChallenge:challenge
  455. forHostname:hostname
  456. withTrustMode:effectiveTrustMode];
  457. if (shouldTrustCerificate) {
  458. [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]
  459. forAuthenticationChallenge:challenge];
  460. } else {
  461. [challenge.sender cancelAuthenticationChallenge:challenge];
  462. }
  463. } else {
  464. if ( [challenge previousFailureCount] == 0 ) {
  465. [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
  466. } else {
  467. [[challenge sender] cancelAuthenticationChallenge:challenge];
  468. }
  469. }
  470. }
  471. @end