I have the following problem: I am making requests to a server, which uses HTTP basic auth and gives me
- a 401 if I do not send authentication
- a 401 if I send invalid authentication
- a 403 if I send valid authentication but the resource is forbidden for that user.
When receiving the 401s, my NSURLSession is friendly enough to call out to its delegate via
URLSession(
session: NSURLSession,
task: NSURLSessionTask, didReceiveChallenge
challenge: NSURLAuthenticationChallenge,
completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void)
where I can then modify the credentials being sent to the server.
When receiving a 403, however, I run into a problem. From there I cannot get the session to use any other credentials than what it used to get the 403. On a 403, the delegate is not asked (which is ok, it is not a 401), so how do I control which credentials are sent in the future for that protection space? Even manually emptying the shared NSURLCredentialStorage and/or setting the correct credentials there directly does not help:
let credential = NSURLCredential(user: username, password: password, persistence: .ForSession)
let protectionSpace = NSURLProtectionSpace(
host: self.host,
port: self.apiRootURL.port!.integerValue,
protocol: self.apiRootURL.scheme!,
realm: "MyRealm",
authenticationMethod: NSURLAuthenticationMethodHTTPBasic)
NSURLCredentialStorage.sharedCredentialStorage().setDefaultCredential(credential, forProtectionSpace: protectionSpace)
After this call, the correct credentials are the only ones listed in the NSURLCredentialStorage - but the next call to the server will still include the old credentials, which will inevitably lead to another 403.
Even resetting the session using its reset(completionHandler: @escaping () -> Void)
method does not help.
TLDR; how do I change the credentials that NSURLSession continues to use once it has had success with them for a given protection domain?
You catch the 403 response message, then either
I recommend the second or third approach, depending on which results in fewer requests on average. To avoid persistence, pass NSURLCredentialPersistenceNone or the Swift equivalent instead of telling it to persist for the duration of the session. To remove a credential, call
removeCredential:forProtectionSpace:options:
.There are a couple of other possibilities that might work, depending on your app design and the server design: