title: CFHTTPMessageAddAuthentication fails to add authentication data to request
body: <p>I'm trying to extend functionality of <a href="https://github.com/square/SocketRocket" rel="nofollow">SocketRocket</a> library. I want to add authentication feature.</p>
  
  <p>Since this library is using <a href="https://developer.apple.com/library/prerelease/ios/documentation/CoreFoundation/Reference/CFMessageRef/index.html" rel="nofollow"><code>CFNetwork</code> <code>CFHTTPMessage*</code> API</a> for HTTP functionality (needed to start web socket connection) I'm trying to utilize this API to provide authentication.<br>
  There is perfectly matching  function for that: <code>CFHTTPMessageAddAuthentication</code>, but it doesn't work as I'm expecting (as I understand <a href="https://developer.apple.com/library/prerelease/ios/documentation/CoreFoundation/Reference/CFMessageRef/index.html#//apple_ref/c/func/CFHTTPMessageAddAuthentication" rel="nofollow">documentation</a>).</p>
  
  <p>Here is sample of code showing the problem:</p>
  
  <pre><code>- (CFHTTPMessageRef)createAuthenticationHandShakeRequest: (CFHTTPMessageRef)chalengeMessage {
      CFHTTPMessageRef request = [self createHandshakeRequest];
      BOOL result = CFHTTPMessageAddAuthentication(request,
                                                   chalengeMessage,
                                                   (__bridge CFStringRef)self.credentials.user,
                                                   (__bridge CFStringRef)self.credentials.password,
                                                   kCFHTTPAuthenticationSchemeDigest, /* I've also tried NULL for use strongest supplied authentication */
                                                   NO);
      if (!result) {
          NSString *chalengeDescription = [[NSString alloc] initWithData: CFBridgingRelease(CFHTTPMessageCopySerializedMessage(chalengeMessage))
                                                                encoding: NSUTF8StringEncoding];
          NSString  *requestDescription = [[NSString alloc] initWithData: CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request))
                                                                encoding: NSUTF8StringEncoding];
          SRFastLog(@"Failed to add authentication data `%@` to a request:\n%@After a chalenge:\n%@",
                    self.credentials, requestDescription, chalengeDescription);
      }
      return request;
  }
  </code></pre>
  
  <p><code>requestDescription</code> content is:</p>
  
  <pre><code>GET /digest-auth/auth/user/passwd HTTP/1.1
  Host: httpbin.org
  Sec-WebSocket-Version: 13
  Upgrade: websocket
  Sec-WebSocket-Key: 3P5YiQDt+g/wgxHe71Af5Q==
  Connection: Upgrade
  Origin: http://httpbin.org/
  </code></pre>
  
  <p><code>chalengeDescription</code> contains:</p>
  
  <pre><code>HTTP/1.1 401 UNAUTHORIZED
  Server: nginx
  Content-Type: text/html; charset=utf-8
  Set-Cookie: fake=fake_value
  Access-Control-Allow-Origin: http://httpbin.org/
  Access-Control-Allow-Credentials: true
  Date: Mon, 29 Jun 2015 12:21:33 GMT
  Proxy-Support: Session-Based-Authentication
  Www-Authenticate: Digest nonce="0c7479b412e665b8685bea67580cf391", opaque="4ac236a2cec0fc3b07ef4d628a4aa679", realm="me@kennethreitz.com", qop=auth
  Content-Length: 0
  Connection: keep-alive
  </code></pre>
  
  <p><code>user</code> and <code>password</code> values are valid ("user" "passwd").</p>
  
  <p>Why <code>CFHTTPMessageAddAuthentication</code> returns <code>NO</code>? There is no clue what is the problem. I've also try updated with credentials an empty request but without luck.</p>
  
  <p>I've used <code>http://httpbin.org/</code> just for testing (functionality of web socket is irrelevant at this step).</p>
  
  <p>Please not that used code doesn't use (and never will) <code>NSURLRequst</code> or <code>NSURLSession</code> or <code>NSURLConnection</code>/</p>
  
  <p><hr>
  I've tried to use different functions: <code>CFHTTPAuthenticationCreateFromResponse</code> and <code>CFHTTPMessageApplyCredentials</code> with same result. 
  At least <code>CFHTTPMessageApplyCredentials</code> returns some error information in form of <code>CFStreamError</code>. Problem is that this error information is useless: <code>error.domain = 4</code>, <code>error.error = -1000</code> where those values are not documented anywhere.<br>
  The only documented values looks like this:</p>
  
  <pre><code>typedef CF_ENUM(CFIndex, CFStreamErrorDomain) {
      kCFStreamErrorDomainCustom = -1L,      /* custom to the kind of stream in question */
      kCFStreamErrorDomainPOSIX = 1,        /* POSIX errno; interpret using &lt;sys/errno.h&gt; */
      kCFStreamErrorDomainMacOSStatus      /* OSStatus type from Carbon APIs; interpret using &lt;MacTypes.h&gt; */
  };
  </code></pre>
  
  <p><code>CFHTTPAuthenticationCreateFromResponse</code> returns invalid object, which description returns this:</p>
  
  <pre><code>&lt;CFHTTPAuthentication 0x108810450&gt;{state = Failed; scheme = &lt;undecided&gt;, forProxy = false}
  </code></pre>
  
  <p>I've found in documentation what those values means: <code>domain=kCFStreamErrorDomainHTTP</code>, <code>error=kCFStreamErrorHTTPAuthenticationTypeUnsupported</code> (thanks  @JensAlfke I've found it before your comment). Why it is unsupported? Documentation claims that digest is supported there is a constant <code>kCFHTTPAuthenticationSchemeDigest</code> which is accepted and expected by <code>CFHTTPMessageAddAuthentication</code>!</p>
  
  <p><hr>
  I've dig up <a href="http://www.opensource.apple.com/source/CFNetwork/CFNetwork-128/HTTP/CFHTTPAuthentication.c" rel="nofollow">source code of <code>CFNetwork</code> authentication</a> and trying figure out what is the problem.</p>
  
  <p>
  I have to do some mistake since this simple tast application also fails:</p>
  
  <pre><code>#import &lt;Foundation/Foundation.h&gt;
  #import &lt;CFNetwork/CFNetwork.h&gt;
  
  static NSString * const kHTTPAuthHeaderName = @"WWW-Authenticate";
  
  static NSString * const kHTTPDigestChallengeExample1 = @"Digest realm=\"testrealm@host.com\", "
      "qop=\"auth,auth-int\", "
      "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", "
      "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"";
  
  static NSString * const kHTTPDigestChallengeExample2 = @"Digest nonce=\"b6921981b6437a4f138ba7d631bcda37\", "
      "opaque=\"3de7d2bd5708ac88904acbacbbebc4a2\", "
      "realm=\"me@kennethreitz.com\", "
      "qop=auth";
  
  static NSString * const kHTTPBasicChallengeExample1 = @"Basic realm=\"Fake Realm\"";
  
  #define RETURN_STRING_IF_CONSTANT(a, x) if ((a) == (x)) return @ #x
  
  NSString *NSStringFromCFErrorDomain(CFIndex domain) {
      RETURN_STRING_IF_CONSTANT(domain, kCFStreamErrorDomainHTTP);
      RETURN_STRING_IF_CONSTANT(domain, kCFStreamErrorDomainFTP);
      RETURN_STRING_IF_CONSTANT(domain, kCFStreamErrorDomainSSL);
      RETURN_STRING_IF_CONSTANT(domain, kCFStreamErrorDomainSystemConfiguration);
      RETURN_STRING_IF_CONSTANT(domain, kCFStreamErrorDomainSOCKS);
      RETURN_STRING_IF_CONSTANT(domain, kCFStreamErrorDomainPOSIX);
      RETURN_STRING_IF_CONSTANT(domain, kCFStreamErrorDomainMacOSStatus);
  
      return [NSString stringWithFormat: @"UnknownDomain=%ld", domain];
  }
  
  NSString *NSStringFromCFErrorError(SInt32 error) {
      RETURN_STRING_IF_CONSTANT(error, kCFStreamErrorHTTPAuthenticationTypeUnsupported);
      RETURN_STRING_IF_CONSTANT(error, kCFStreamErrorHTTPAuthenticationBadUserName);
      RETURN_STRING_IF_CONSTANT(error, kCFStreamErrorHTTPAuthenticationBadPassword);
  
      return [NSString stringWithFormat: @"UnknownError=%d", (int)error];
  }
  
  NSString *NSStringFromCFHTTPMessage(CFHTTPMessageRef message) {
      return [[NSString alloc] initWithData: CFBridgingRelease(CFHTTPMessageCopySerializedMessage(message))
                                   encoding: NSUTF8StringEncoding];
  }
  
  void testAuthenticationHeader(NSString *authenticatiohHeader) {
      CFHTTPMessageRef response = CFHTTPMessageCreateResponse(kCFAllocatorDefault,
                                                              401,
                                                              NULL,
                                                              kCFHTTPVersion1_1);
      CFAutorelease(response);
  
      CFHTTPMessageSetHeaderFieldValue(response,
                                       (__bridge CFStringRef)kHTTPAuthHeaderName,
                                       (__bridge CFStringRef)authenticatiohHeader);
  
  
      CFHTTPAuthenticationRef authData = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, response);
      CFAutorelease(authData);
  
      CFStreamError error;
      BOOL validAuthData = CFHTTPAuthenticationIsValid(authData, &amp;error);
  
      NSLog(@"testing header value: %@\n%@authData are %@   error.domain=%@  error.error=%@\n\n",
            authenticatiohHeader, NSStringFromCFHTTPMessage(response),
            validAuthData?@"Valid":@"INVALID",
            NSStringFromCFErrorDomain(error.domain), NSStringFromCFErrorError(error.error));
  }
  
  int main(int argc, const char * argv[]) {
      @autoreleasepool {
          testAuthenticationHeader(kHTTPDigestChallengeExample1);
          testAuthenticationHeader(kHTTPDigestChallengeExample2);
          testAuthenticationHeader(kHTTPBasicChallengeExample1);
      }
      return 0;
  }
  </code></pre>
  
  <p>Logs show:</p>
  
  <pre><code>2015-07-01 16:33:57.659 cfauthtest[24742:600143] testing header value: Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"
  HTTP/1.1 401 Unauthorized
  Www-Authenticate: Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"
  
  authData are INVALID   error.domain=kCFStreamErrorDomainHTTP  error.error=kCFStreamErrorHTTPAuthenticationTypeUnsupported
  
  2015-07-01 16:33:57.660 cfauthtest[24742:600143] testing header value: Digest nonce="b6921981b6437a4f138ba7d631bcda37", opaque="3de7d2bd5708ac88904acbacbbebc4a2", realm="me@kennethreitz.com", qop=auth
  HTTP/1.1 401 Unauthorized
  Www-Authenticate: Digest nonce="b6921981b6437a4f138ba7d631bcda37", opaque="3de7d2bd5708ac88904acbacbbebc4a2", realm="me@kennethreitz.com", qop=auth
  
  authData are INVALID   error.domain=kCFStreamErrorDomainHTTP  error.error=kCFStreamErrorHTTPAuthenticationTypeUnsupported
  
  2015-07-01 16:33:57.660 cfauthtest[24742:600143] testing header value: Basic realm="Fake Realm"
  HTTP/1.1 401 Unauthorized
  Www-Authenticate: Basic realm="Fake Realm"
  
  authData are INVALID   error.domain=kCFStreamErrorDomainHTTP  error.error=kCFStreamErrorHTTPAuthenticationTypeUnsupported
  </code></pre>
  
  <p><hr>
  edit after my own answer:</p>
  
  <h2>Alternative solution</h2>
  
  <p>Other possible solution is to manually parse <code>WWW-Authenticate</code> response header and precess it and generate <code>Authorization</code> header for new request.</p>
  
  <p>Is there some simple library or sample code I could use in commercial application which will do this (only this)? I could do this my self but this will take a precious time. Bounty is still available :).</p>