NSURL, basic authentication and URL encoding

I need to send the URL of a ICS file to the Calendar app so the user can subscribe to the internet calendar and I'm using UIApplication openURL.

I'm building the URL using NSURLComponents and setting username and password.


Username and password are URL encoded so if the user has special characters in the password they will be encoded (e.g. the @ sign will be %40).


The Calendar application then tries to get the file. It doesn't send the credentials during the first GET, then it receives a 401 and then it does another request, time time sending the credential as HTTP Basic Authentication.


Everything works fine if the user doesn't have special characters but fails otherwise because the server doesn't decode the password.


Is there a way to avoid sending username and password without URL encoding?

I'd like to avoid adding special logic on the server based on user agent or something like that.


I can build the URL using NSURL URLWithString but this is not 100% reliable because the Calendar app might try to contact the wrong host in some cases.


I also tried to add the encoding param during the challenge response as reported here https://tools.ietf.org/id/draft-reschke-basicauth-enc-00.html but it doesn't seem to affect the behavior.


Thank you for you attention


Vale

If you're talking to an HTTP based calendar server that's using HTTP Basic Authentication, then the process that you're seeing where the client makes the request without credentials, receives the 401 response, and then resends the request with credentials is the expected sequence for HTTP Basic Authentication. So your question "Is there a way to avoid sending username and password without URL encoding?" both how and why you're doing what you're doing.


Note: The enconding used for HTTP Basic Authentication isn't the same as URL encoding. The username:password credential is encoded in Base64, not URL escapes. So if you encode the @ to %40 you're doing it wrong.

Everything works fine if the user doesn't have special characters but fails otherwise because the server doesn't decode the password.

I think you have to work this backwards from here. What exactly is the server expecting here? Can you post an example of an successful Basic authorisation with a non-ASCII password?

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Here's the request/response when I build the request manually to validate username and password before sending the the URL to the calendar app.

Note that the string bWVyZWRpdGg6VGVzdDEyMzRA is the base64 encoded of meredith:Test1234@


HEAD /my-calendar-subscription-server/1632a50c-7b0c-4fb9-b46a-45c1de0ac291/calendar.ics HTTP/1.1

Host: my-host.com

Accept: */

Content-Length: 0

Connection: keep-alive

Proxy-Connection: keep-alive

User-Agent: my-app-ios/14 CFNetwork/758.1.6 Darwin/15.0.0

Accept-Language: en-us

Authorization: Basic bWVyZWRpdGg6VGVzdDEyMzRA

Accept-Encoding: gzip, deflate


HTTP/1.1 200 OK

Cache-Control: no-cache, no-store, max-age=0, must-revalidate

Content-Length: 189

Content-Type: text/calendar;charset=UTF-8

Date: Fri, 20 Nov 2015 10:44:08 GMT

Expires: 0

Pragma: no-cache

Server: Apache-Coyote/1.1

X-Application-Context: application:default:4150

X-Content-Type-Options: nosniff

X-Frame-Options: DENY

X-XSS-Protection: 1; mode=block

Connection: keep-alive


Then I build the URL to pass it to the calendar app.

I use NSURLComponent and this is the URL I pass to NSApplication openURL


http://meredith:Test1234%40@my-host.com/my-calendar-subscription-server/1632a50c-7b0c-4fb9-b46a-45c1de0ac291/calendar.ics


I set the password as it is using NSURLComponent setPassword, without any encoding, and of course they are URL encoded in the final URL.


These are the requests sent by the Calendar app


First request


GET /my-calendar-subscription-server/1632a50c-7b0c-4fb9-b46a-45c1de0ac291/calendar.ics HTTP/1.1

Host: my-host.com

Proxy-Connection: keep-alive

Accept: text/calendar

User-Agent: iOS/9.1 (13B143) dataaccessd/1.0

Accept-Language: en-us

Accept-Encoding: gzip, deflate

Connection: keep-alive


HTTP/1.1 401 Unauthorized

Cache-Control: no-cache, no-store, max-age=0, must-revalidate

Content-Language: en

Content-Type: text/html;charset=utf-8

Date: Fri, 20 Nov 2015 10:44:08 GMT

Expires: 0

Pragma: no-cache

Server: Apache-Coyote/1.1

WWW-Authenticate: Basic realm="MyRealm"

X-Content-Type-Options: nosniff

X-Frame-Options: DENY

X-XSS-Protection: 1; mode=block

Content-Length: 1104

Connection: keep-alive


Second request


GET /my-calendar-subscription-server/1632a50c-7b0c-4fb9-b46a-45c1de0ac291/calendar.ics HTTP/1.1

Host: my-host.com

Connection: keep-alive

Proxy-Connection: keep-alive

Accept: text/calendar

User-Agent: iOS/9.1 (13B143) dataaccessd/1.0

Accept-Language: en-us

Authorization: Basic bWVyZWRpdGg6VGVzdDEyMzQlNDA=

Accept-Encoding: gzip, deflate


HTTP/1.1 401 Unauthorized

Cache-Control: no-cache, no-store, max-age=0, must-revalidate

Content-Language: en

Content-Type: text/html;charset=utf-8

Date: Fri, 20 Nov 2015 10:44:08 GMT

Expires: 0

Pragma: no-cache

Server: Apache-Coyote/1.1

WWW-Authenticate: Basic realm="MyRealm"

X-Content-Type-Options: nosniff

X-Frame-Options: DENY

X-XSS-Protection: 1; mode=block

Content-Length: 1040

Connection: keep-alive


Note that the string bWVyZWRpdGg6VGVzdDEyMzQlNDA= is the base64 encoded of meredith:Test1234%40 so the server fails to validate username/password.


What I'm asking is: it there a way to tell the Calendar app not to use the URL encoded version of username and password when it builds the base64 auth string?


At the moment I'm solving this issue on the server side with a filter based on the user agent (if iOS then -> base64 decode -> URL decode -> base64 encode)


Thank you for your attention


Vale

NSURL, basic authentication and URL encoding
 
 
Q