cocoa - NSURLRequest with UTF8 password -
here method i've written connect server , user auth token:
+ (void)getauthtokenforusername:(nsstring *)username password:(nsstring *)password completionhandler:(void (^)(nsstring *, nserror *))completionhandler { username = [username urlencodedstring]; password = [password urlencodedstring]; nsstring *format = @"https://%@:%@@api.example.com/v1/user/api_token?format=json"; nsstring *string = [nsstring stringwithformat:format, username, password]; nsurl *url = [nsurl urlwithstring:string]; [nsurlconnection sendasynchronousrequest:[nsurlrequest requestwithurl:url] queue:[nsoperationqueue mainqueue] completionhandler:^(nsurlresponse *urlresponse, nsdata *data, nserror *error) { nsstring *token; if (data) { nsdictionary *dictionary = [nsjsonserialization jsonobjectwithdata:data options:0 error:&error]; token = [nsstring stringwithformat:@"%@:%@", username, dictionary[@"result"]]; } completionhandler(token, error); }]; }
a url looks this: https://username:hello%c2%b0@api.example.com/v1/user/api_token?format\=json
, password hello°
. urlencodedstring
method encodes in example, request never works. problem not escaping or server, because can curl
same url , nice json , authentication works, though there non-ascii character in password. works other programming languages ruby
or python
. same url never works nsurlconnection
, doesn't work in safari, of course uses nsurlconnection
. 'the operation not completed' '401 forbidden' every time.
(my code works fine when password contains ascii characters. tried using nsurlcredential
methods, same problem.)
what need nsurlconnection
work such url https://username:hello%c2%b0@api.example.com/v1/user/api_token?format\=json
password contains non-ascii characters?
i have performed several tests against mockup server , think have solution you.
first of all, when add username & password url, not send server part of url. sent part of authorization
header (see basic access authentication).
the fastest workaround do
nsurlrequest* request = [nsurlrequest requestwithurl:url]; nsstring* usernamepassword = [[nsstring stringwithformat:@"%@:%@", username, password] base64encode]; [request setvalue:[nsstring stringwithformat:@"basic %@", usernamepassword] forhttpheaderfield:@"authorization"]
to understand problem, let's go bit deeper. let's forget nsurlconnection sendasynchronousrequest:
, let create old-fashioned connection nsurlconnectiondelegate
. in delegate, let's define 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 don't create these methods, username & password url won't ever added http header. if add them, you'll see proposed password hello%c2%b0
. obviously, that's wrong.
you can see problem directly from
nslog(@"password: %@", [[nsurl urlwithstring:@"http://username:hello%c2%b0@www.google.com"] password]);
which prints
hello%c2%b0
i believe bug in framework. nsurl
returns password
encoded , nsurlcredential
doesn't decode left invalid password.
Comments
Post a Comment