I have just performed several tests against my mockup server and I think I have a solution for you.
First of all, when you add username & password to an URL, they are not actually send to the server as part of the URL. They are sent as part of the Authorization
header (see Basic access authentication).
The fastest workaround for you is to do
NSURLRequest* request = [NSURLRequest requestWithURL:URL];
NSString* usernamePassword = [[NSString stringWithFormat:@"%@:%@", username, password] base64Encode];
[request setValue:[NSString stringWithFormat:@"Basic %@", usernamePassword] forHTTPHeaderField:@"Authorization"]
To understand the problem, let's go a bit deeper. Let's forget NSURLConnection sendAsynchronousRequest:
and let us create an old-fashioned connection with a NSURLConnectionDelegate
. Then in the delegate, let's define the following methods:
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(@"Proposal: %@ - %@", challenge.proposedCredential.user, challenge.proposedCredential.password);
NSURLCredential* credential = [NSURLCredential credentialWithUser:@"username"
password:@"hello°"
persistence:NSURLCredentialPersistenceNone];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
If you don't create these methods, the username & password from your URL won't ever be added to the HTTP header.
If you add them, you'll see that the proposed password is hello%C2%B0
. Obviously, that's wrong.
You can see the problem directly from
NSLog(@"Password: %@", [[NSURL URLWithString:@"http://username:hello%C2%B0@www.google.com"] password]);
which prints
hello%C2%B0
I believe this is a bug in the framework. NSURL
returns password
encoded and NSURLCredential
doesn't decode it so we are left with an invalid password.