3

I have been trying to set up a web view wrapper app that will load the content of a website (still to be launched). Currently, the website is in development mode, and the only endpoints for the website are protected behind a http authentication.

I have been looking at this solution: Swift webview xcode post data

However, I do not want to make a POST request each time, but I'd rather want to authenticate against the website once and keep the connection.

What I'm looking is for a clean and stable solution, one that would allow me to be able to have control of edge cases such as bad credentials provided.

I am not comfortable with using the NSURLConnection because that solution is deprecated in iOS9. I need a solution with NSURLSession.

Let me know if I'm missing something within the above linked solution. I am sure someone had this issue as well. Additionally, the website has SSL protection.

Kind regards

Community
  • 1
  • 1
Armin
  • 629
  • 6
  • 23
  • how does your auth work, can you supply a header in the request? is it a UI or WK web view? – Wain Jul 25 '16 at 15:08
  • I am using UIWebView. When viewing from web, the authentication is a basic HTTP authentication popup. If you try to access without the proper credentials you will get a 401 authorization response code/status. – Armin Jul 25 '16 at 18:41

3 Answers3

11

I'm not entirely sure this fulfils your demands in the best way, but if you can use a WKWebView, maybe you can simply rely on the authentication challenge delegate method? See my answer here as well, the relevant code snippet would be:

func webView(webView: WKWebView, didReceiveAuthenticationChallenge 
           challenge: NSURLAuthenticationChallenge, 
   completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {

    let creds = NSURLCredential(user:"username", password:"password", persistence: NSURLCredentialPersistence.ForSession)
    completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, creds)
}

I haven't tried it yet myself, but the documentation says the credentials should be used for the session, so additional requests resulting from links should work. Even if not, that just results for the method to be called again and you can provide the credentials once more.

This is just a rump, you'd have to get name and password from an alert or the like (also you can store the credentials more elegantly to make subsequent calls to the delegate method more elegant).

You also wrote you're using SSL, so I take it you're familiar with the App Transport Security flags (since the question title just has "HTTP" in it and not "HTTPS", which you probably want for it to work smoothly, otherwise see the question I linked).

Community
  • 1
  • 1
Gero
  • 4,394
  • 20
  • 36
  • Yes, using WKWebView simplifies it. At the moment of asking this question I was not sure that WKWebView is an option for me, but it does resolve the issue. This probably is the cleanest solution right now. – Armin Jul 27 '16 at 14:32
  • Glad I could help. Also note my "elegant" writing style: "Store it more *elegantly* ... to be more *elegant*" ... Good thing you didn't ask for advice in English, I'm obviously a derp... :) – Gero Jul 27 '16 at 14:34
  • Note: this needs quite a bunch of updates for Swift 3 (and probably 4), and to catch this callback the `.navigationDelegate` needs to be set and `WKNavigationDelegate` needs implementing (the above code). – Jonny Oct 18 '17 at 09:22
2

Okay here is the Swift 3 code.

extension MyController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        guard let u = self.webuser, let p = self.webp else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }

        let creds = URLCredential.init(user: u, password: p, persistence: .forSession)
        completionHandler(.useCredential, creds)
    }
}
Jonny
  • 15,955
  • 18
  • 111
  • 232
0

You shouldn't need to use POST or a connection or session. You should just create a mutable URL request and set an auth header. Then ask the web view to load the request.

Auth header details from Wikipedia:

  1. The username and password are combined with a single colon.
  2. The resulting string is encoded using the RFC2045-MIME variant of Base64, except not limited to 76 char/line.
  3. The authorization method and a space i.e. "Basic " is then put before the encoded string.

For example, if the user agent uses Aladdin as the username and OpenSesame as the password then the field is formed as follows:

Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
Wain
  • 118,658
  • 15
  • 128
  • 151
  • Sadly, this seems to work only on the first request. Every new request within the web view (menu links etc.) will fail because of missing authorisation. Are you sure that a session is not required? – Armin Jul 26 '16 at 09:13
  • session does not equate to a web session. the server should be returning a cookie with some auth token in it and using that. that would be a web session (not a URL session, which is very different) – Wain Jul 26 '16 at 09:46