Search code examples
iosdjangohttp-authentication

handling non-ascii logins when accessing RESTful django from iOS with http auth


I have the following architecture: iOS client uses NSURLConnection with willSendRequestForAuthenticationChallenge to auth against Django-rest-framework based REST service. It works fine when login is ascii only, but when I use non-ascii letters, it even does not send HTTP_AUTHORIZATION !

That is probably because RFC does not cover non-ascii chars in HTTP basic auth.

What is the best approach to solve it? Will digest auth help?

I can't encode login to url, since I do not want it to be displayed in server logs. And I also want to stay with willSendRequestForAuthenticationChallenge.

PS: I can use form-based auth or json-based on Django side (by implementing BaseAuthentication) but on iOS I will need to create NSURLAuthenticationChallenge which seems to be a big deal


Solution

  • Generally you would be out of luck with both HTTP Basic & Digest authentication, it's only ASCII characters and there's simply no way to get around that.

    However, because you're controlling the backend then you can use this trick: encode both the username and password in base64 on the iOS side like this:

    NSData *usernameData = [username dataUsingEncoding:NSUTF8StringEncoding];
    NSString *usernameBase64 = [usernameData base64EncodedStringWithOptions:0];
    

    Then create your NSURLCredential with the encoded strings. Since HTTP Basic Authentication already encodes the credentials in base64, by doing this you will be encoding the credentials twice before sending them to the server.

    Now on the DRF side, create a subclass of BasicAuthentication and call it Base64BasicAuthentication for instance. Override authenticate_credentials like this:

    def authenticate_credentials(self, userid, password):
        userid = base64.b64decode(userid)
        password = base64.b64decode(password)
        return super(Base64BasicAuthentication, self).authenticate_credentials(userid, password)
    

    And then just use the Base64BasicAuthentication as your default auth class in Django REST Framework. It's a bit of a hack, but it should do the job.