WKWebViewでBasic認証を行う

WKWebViewで認証されたページにアクセスしたいときは、確認証方法に合わせた処理が必要です。この記事ではHTTPのBasic認証に対応する方法を解説します。

スポンサーリンク

全体的な流れ

WKWebViewでBasic認証に対応するには、次のような流れを実装します。

  1. 認証エラーになったら、認証方式を判定する。
  2. アカウント情報を持っていたら、アカウント情報をヘッダに追加してアクセスする。
  3. アカウント情報を持っていなかったら、認証画面を表示する。
  4. アカウント情報が入力されたら、アカウント情報をヘッダに追加してアクセスする。

テスト用にBasic認証を行うページを用意する方法については、以下の記事をご覧ください。

関連記事

アプリの中でHTTPのBasic認証を行う仕様があり、その機能を実装しようと思ったときに、Basic認証のコードをテストできるテスト環境が欲しくなりました。 そこで、Dockerでコンテナ化しているWebサーバーでBasi[…]

上記の記事ではDockerで開発マシンの中にBasic認証を行うページを構築する方法を解説しています。開発時のテストに使うのに手軽な方法だと思います。

認証が必要なページにアクセスするとどうなる?

認証に対応するために必要な処理を何も実装せずに、認証が必要なページにアクセスすると、エラーページが表示されます。SafariやChromeのような認証ダイアログは自動的には表示されません。

これらの処理はアプリ側で実装する必要があります。

まずは、次の2つを実装します。

  1. 認証エラーになったことを判定する。
  2. 認証方式がBasic認証かどうかを判定する。

認証エラーになったことの判定

認証エラーになり、認証が必要になると、WKNavigationDelegateの次のメソッドが呼ばれます。

func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge,
                 completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)

WKNavigationDelegateプロトコルに適合したクラスで実装し、WKWebViewクラスのnavigationDelegateプロトコルにセットします。

必要な処理を実行して、最後にcompletionHandlerを実行します。

認証方式の判定

認証方式を判定するには、URLAuthenticationChallengeprotectionSpaceプロパティを取得して、URLProtectionSpaceauthenticationMethodプロパティをチェックします。

次のコードは、Basic認証かどうかを判定しています。

func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge,
			 completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
	var isBasicAuthentication = false
	
	let method = challenge.protectionSpace.authenticationMethod
	if method.compare(NSURLAuthenticationMethodHTTPBasic) == .orderedSame {
		isBasicAuthentication = true
	}
	
	completionHandler(.performDefaultHandling, nil)
}

Basic認証の場合は、authenticationMethodの値はNSURLAuthenticationMethodHTTPBasicになっています。

認証画面の表示

Basic認証だったら、認証画面を表示し、それ以外だったらデフォルトの処理を行うように実装します。

認証画面でログイン、または、キャンセルボタンが押されたら、webView(_:didReceive:completionHandler:)で取得したcompletionHandlerを実行します。

次のコードは、UIAlertControllerでログイン画面を作っているサンプルコードです。とりあえず、表示するというコードなので、ボタンの処理は特に行っていません。

func showBasicAuthAlert(completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
	let alert = UIAlertController(title: "Authentication",
								  message: "Enter your account",
								  preferredStyle: .alert)
	
	// ユーザー名のテキストフィールドを作成する
	alert.addTextField { (textField) in
		textField.placeholder = "User Name"
		textField.keyboardType = .asciiCapable
	}
	
	// パスワードのテキストフィールドを作成する
	alert.addTextField { (textField) in
		textField.placeholder = "Password"
		textField.keyboardType = .asciiCapable
		textField.isSecureTextEntry = true
	}
	
	// ログインボタンを作成する
	alert.addAction(UIAlertAction(title: "Login", style: .default, handler: { (action) in
		completionHandler(.performDefaultHandling, nil)
	}))
	
	// キャンセルボタンを作成する
	alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) in
		completionHandler(.performDefaultHandling, nil)
	}))
	
	// アラートを表示する
	self.present(alert, animated: true, completion: nil)
}

キャンセルボタンの処理

キャンセルボタンが押されたときの処理を実装します。とはいっても、既に実装済みのコードです。

completionHandlerの引数に.performDefaultHandlingを指定して実行します。cancelAuthenticationChallengeも定義されていて、そちらを使った方が良いと最初は思ったのですが、試してみると、cancelAuthenticationChallenageは処理の中断でした。

SafariやChromeの動作を見てみると、Basic認証でエラーになったときは、エラーページを表示するようになっています。しかし、cancelAuthenticationChallengeを指定してしまうと、エラーページの表示処理も行わずに中止してしまいました。

.performDefaultHandlingを指定すると、エラーページの表示に遷移するので、WKWebViewと組み合わせるときは、.performDefaultHandlingの方が良いと思います。

但し、エラーになったら、WKWebView自体を非表示にして、別の画面に遷移するような仕様のときは、cancelAuthenticationChallengeの方が良いでしょう。

ログイン処理

ログイン処理は、次の処理を実装します。

  1. 入力されたユーザー名とパスワードを取得する
  2. 取得したユーザー名とパスワードから認証情報を作成する
  3. 作成した認証情報を使って認証を行う

コードにすると次のようになります。

// ログインボタンを作成する
alert.addAction(UIAlertAction(title: "Login", style: .default, handler: { (action) in
	var userName = ""
	var password = ""
	
	// 入力された文字列を取得する
	if let textFields = alert.textFields {
		if let str = textFields[0].text {
			userName = str
		}
		if let str = textFields[1].text {
			password = str
		}
	}
	
	// このセッション専用の認証情報を作成する
	let credential = URLCredential(user: userName, password: password, persistence: .forSession)
	
	// 作成した認証情報で認証を行う
	completionHandler(.useCredential, credential)
}))

認証情報は、URLCredentialのインスタンスです。イニシャライザでユーザー名とパスワードを指定できます。最後のpersistenceは認証情報をどのように保持するかを指定します。

次の値が定義されています。

  • none : 保持しない
  • forSession : セッション中だけ保持する
  • permanent : キーチェインに保存する
  • synchronizable : キーチェインに保存し、Apple IDに紐付けて同期可能にする(iCloud経由で同期可能という意味だと思います)

認証情報を作成したら、useCredentialを指定して completionHandlerを実行します。

認証に成功すると、ページにアクセスできます。

スポンサーリンク
最新情報をチェックしよう!
>現役のプログラマーが書くプログラミング情報

現役のプログラマーが書くプログラミング情報

日々の開発の中での学びや分かったこと、調べたことなどを書いていくブログです。

CTR IMG