記事

WebブラウザAppでのシングルサインオンのサポート

WebブラウザAppを拡張し、他のAppからのWeb認証リクエストを処理できるようにします。

概要

Appは、ASWebAuthenticationSession(英語)のインスタンスを使用して、Webサービスを通してユーザー認証を行うことができます。Appがこの認証セッションのstart()(英語)メソッドを呼び出したら、システムはユーザーのデフォルトのWebブラウザに、所定のURLで認証処理を開始するよう要求します。ユーザーのデフォルトのブラウザが認証リクエストに対応していない場合は、Safariが利用されます。いずれの場合も、指定されたブラウザはURLをロードし、ユーザーが認証を行うのを待ち、認証の結果を示すコールバックURLを返します。

macOS用のWebブラウザAppは、この処理に対応できるように作成することができます。ブラウザをセッションハンドラとして登録し、認証リクエストを待ち受け、処理します。

セッションハンドリング機能の宣言

Xcodeのプロパティリストエディタで、ASWebAuthenticationSessionWebBrowserSupportCapabilities(英語)キーをWebブラウザAppのInformation Property List(英語)に追加します。キーの値については、IsSupported(英語)キーとそれに対応する値YESを含む辞書を作成します。

Webブラウザサポート機能のプロパティリストのキーを表示するXcodeのスクリーンショット。

この機能を宣言することで、システムに対し、このWebブラウザAppがシングルサインオンのリクエストを処理できることを知らせます。このブラウザがデフォルトのブラウザに設定されていれば、システムから認証リクエストが転送されます。

オプションとして、EphemeralBrowserSessionIsSupported(英語)キーと、ブラウザが一時的なブラウジングをサポートするかどうかを示す値を追加することもできます。

一時的なブラウザセッションサポートのキーを表示するXcodeのスクリーンショット。

このキーが追加されていない場合や、値がNOに設定されている場合に、Appが一時的な認証セッションを行おうとすると、警告メッセージが表示されます。値をYESに設定し、この機能のサポートを宣言している場合、認証リクエストを受け取った際には必ずshouldUseEphemeralSession(英語)プロパティに従うようにしてください。詳しくは後述の認証の実行を参照してください。

認証リクエストの待ち受け

認証リクエストの待ち受けのため、WebブラウザAppでASWebAuthenticationSessionWebBrowserSessionHandling(英語)プロトコルに準拠します。ハンドラとして機能するクラスを選択し、このプロトコルへの準拠を宣言します。

class MyAuthenticationHandler: ASWebAuthenticationSessionWebBrowserSessionHandling {
    var request: ASWebAuthenticationSessionRequest?
}

この準拠クラスで、プロトコルのbegin(_:)(英語)メソッドを実装し、新しいASWebAuthenticationSessionRequest(英語)インスタンスを受け取れるようにします。その中にカプセル化されているデータを使用して、リクエストを処理します。詳しくは後述の認証の実行を参照してください。

func begin(_ request: ASWebAuthenticationSessionRequest!) {
    self.request = request   // Store for later.

    if request.shouldUseEphemeralSession == true {
        // Load request.url in an isolated session and wait for the callback.
    } else {
        // Load request.url and wait for the callback.
    }
}

呼び出したAppが終了した場合や操作を取り消した場合、キャンセルが発生する可能性があります。これを待ち受けるため、cancel(_:)(英語)メソッドを実装します。複数のリクエストを処理している場合は、キャンセル要求のuuid(英語)プロパティを使用すれば、処理中のリクエストのうちのどれをキャンセルするかを特定できます。

func cancel(_ request: ASWebAuthenticationSessionRequest!) {
    // Abandon the request and clean up.
    self.request = nil
}

インターフェイスを実装したら、共有セッションマネージャのsessionHandler(英語)プロパティを設定し、システムがこのWebブラウザAppのセッションハンドラを見つけられるようにします。これは通常、起動時に1回だけ行います。たとえば、AppデリゲートのapplicationDidFinishLaunching(_:)(英語)メソッドで設定してもよいでしょう。

func applicationDidFinishLaunching(_ aNotification: Notification) {
    let manager = ASWebAuthenticationSessionWebBrowserSessionManager.shared
    manager.sessionHandler = MyAuthenticationHandler()
    // The manager keeps a strong reference to your handler.
}

認証の実行

ハンドラが新しい認証リクエストを受け取ったら、そのリクエストのurl(英語)プロパティ中に含まれるURLをロードし、ユーザーにその内容を表示します。ユーザーはその内容に対応した操作を行います。たとえば、認証情報を入力してボタンをクリックするなどです。認証を実行するサービスは、既知のコールバックスキームを使用するURLにブラウザをリダイレクトすることによって、認証の結果を示します。

ブラウザは、リクエストのcallbackURLScheme(英語)プロパティによって、サービスがどのようなコールバックスキームを使用しているかがわかります。ブラウザは、このスキームに関わるリダイレクトを検出すると、リクエストのcomplete(withCallbackURL:)(英語)メソッドを呼び出し、URL全体をセッションマネージャに返します。たとえば、WebKit APIを使用している場合、ナビゲーションデリゲートのwebView(_:decidePolicyFor:decisionHandler:)(英語)メソッドからこれを実行できます。

func webView(_ webView: WKWebView,
             decidePolicyFor navigationAction: WKNavigationAction,
             decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

    guard let url = navigationAction.request.url else { return }

    if url.scheme == request?.callbackURLScheme {
        request?.complete(withCallbackURL: url)
    } else {
        // Handle normally.
    }
}

また、何らかの理由で(たとえばユーザーが認証のウインドウを閉じてしまったなど)、ブラウザが操作を最後まで実行できなかった場合には、リクエストのcancelWithError(_:)(英語)メソッドを代わりに呼び出します。

一時的なブラウジングのサポートを宣言している場合は、上述のセッションハンドリング機能の宣言で説明したように、リクエストのshouldUseEphemeralSession(英語)プロパティに必ず従わなければなりません。この値がtrueのときは、認証プロセス中にcookieなどの既存のブラウジングデータを使用しないようにします。また、認証プロセス中に収集したデータをプロセス終了後も保持したり、他のセッションと共有したりすることも、避けなければなりません。

認証のための起動の処理

ブラウザが認証処理をサポートする場合、システムによって、認証のために起動される場合があるかもしれません。この状況は、共有セッションマネージャのwasLaunchedByAuthenticationServices(英語)プロパティをチェックすることによって、検出することができます。

func applicationDidFinishLaunching(_ aNotification: Notification) {
    let manager = ASWebAuthenticationSessionWebBrowserSessionManager.shared
    manager.sessionHandler = MyAuthenticationHandler()

    if manager.wasLaunchedByAuthenticationServices == true {
        // Adjust startup behavior accordingly.
    }
}

このインジケータを利用して、起動時のブラウザの動作を調整してください。たとえば、前回開いたウインドウやタブを復元しないようにするなどです。

関連項目

Webブラウザ認証セッションのサポート

class ASWebAuthenticationSessionWebBrowserSessionManager(英語)

AppとWebブラウザの間のデータの共有を仲介するセッションマネージャ。

property list key ASWebAuthenticationSessionWebBrowserSupportCapabilities(英語)

ブラウザAppが他のAppからの認証リクエストを処理できることを宣言するために使用するキーのリスト。