I have a cocoa class set up that I want to use to connect to a RESTful web service I\'m building. I have decided to use HTTP Basic Authentication on my PHP backend like so…<
In a situation where a 401 or other authentication challenge is unacceptable/impossible, I sometimes use a dummy CFHTTPMessage to generate the authetication line, then copy that back into the NSURLRequest:
// assume NSString *username and *password exist and NSURLRequest *urlRequest
// exists and is fully configured except for HTTP Basic Authentication..
CFHTTPMessageRef dummyRequest =
CFHTTPMessageCreateRequest(
kCFAllocatorDefault,
CFSTR("GET"),
(CFURLRef)[urlRequest URL],
kCFHTTPVersion1_1);
CFHTTPMessageAddAuthentication(
dummyRequest,
nil,
(CFStringRef)username,
(CFStringRef)password,
kCFHTTPAuthenticationSchemeBasic,
FALSE);
authorizationString =
(NSString *)CFHTTPMessageCopyHeaderFieldValue(
dummyRequest,
CFSTR("Authorization"));
CFRelease(dummyRequest);
[urlRequest setValue:authorizationString forHTTPHeaderField:@"Authorization"];
This may seem completely a bizarre way to do it but it is tolerant of situations where the username/password aren't URL clean and where NSURLRequest refuses to consult the NSURLCredentialStorage because the server isn't actually sending a HTTP 401 (for example it sends a regular page instead).
A synchronous NSURLConnection
will absolutely work with NSURLCredentialStorage
. Here's how things usually work:
NSURLConnection
requests the page from the serverNSURLConnection
looks to see what credentials it can glean from the URLNSURLConnection
will also consult NSURLCredentialStorage
to fill in the gapsNSURLConnection
will send the -connection:didReceiveAuthenticationChallenge:
delegate method asking for credentialsNSURLConnection
now finally has full credentials, it retries the original request including authorization data.By using the synchronous connection method, you only lose out on step 5, the ability to provide custom authentication. So, you can either pre-provide authentication credentials in the URL, or place them in NSURLCredentialStorage
before sending the request. e.g.
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://user:pass@example.com"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
or:
NSURLCredential *credential = [NSURLCredential credentialWithUser:@"user"
password:@"pass"
persistence:NSURLCredentialPersistenceForSession];
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:@"example.com"
port:0
protocol:@"http"
realm:nil
authenticationMethod:nil];
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential
forProtectionSpace:protectionSpace];
[protectionSpace release];
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
Set your credential as the default credential for the protectionspace:
// Permananent, session, whatever.
NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence: NSURLCredentialPersistencePermanent];
// Make sure that if the server you are accessing presents a realm, you set it here.
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"blah.com" port:0 protocol:@"http" realm:nil authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
// Store it
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];
At this point, any subsequent NSURLConnection that is challenged using a protection space that matches what you set will use this credential
I would note mikeabdullahuk's answer is good but also if you use NSURLCredentialPersistencePermanent instead of per session it will store the credentials in the users keychain so next time you can check NSURLCredentialStorage for a non nil value for the default credentials for a protection space and if you get a non nil value you can just pass the credentials in. I am using this method right now for a delicious.com client I am writing and it works very well in my tests.