DiscourseConnect ログインリンクを作成する

:bookmark: このドキュメントでは、DiscourseConnect プロバイダーサイトで、ユーザーを Discourse にログインさせ、特定の Discourse URL にリダイレクトするリンクを作成する方法を説明します。

:person_raising_hand: 必要なユーザーレベル: 管理者

DiscourseConnect を使用しているサイトでは、DiscourseConnect プロバイダーサイトに、ユーザーを Discourse にログインさせ、特定の Discourse URL にリダイレクトするリンクを追加できます。これは、return_path パラメータがユーザーに最終的に表示させたい Discourse ページのパスに設定された /session/sso ルートを指すリンクを作成することで実現されます。

リンクの href プロパティは以下の形式である必要があり、\<相対パス\> の部分をユーザーに最終的に表示させたいパスに置き換えます。

https://forum.example.com/session/sso?return_path=く相対パス>

ユーザーをログインさせ、Discourse サイトのホームページにリダイレクトするアンカータグの例:

<a href="https://forum.example.com/session/sso?return_path=/">コミュニティ</a>

ユーザーをログインさせ、「トップトピック」ページにリダイレクトするアンカータグの例:

<a href="https://forum.example.com/session/sso?return_path=/top">トップトピック</a>

Discourse での return_path の保存方法

Discourse は、return_path パラメータの値を、ユーザーが /session/sso ルートにアクセスしたときに生成される SSO ノンス (nonce) をキーとしてサーバーセッションに保存します。DiscourseConnect 認証プロセスの最後に、Discourse はノンスを使用して return_path を検索し、ユーザーをそのパスにリダイレクトします。

認証済みユーザーにとってプロセスをシームレスにする方法

ユーザーが Discourse の /session/sso ルートにアクセスすると、discourse connect url サイト設定で設定された URL にリダイレクトされます。その後、DiscourseConnect プロバイダーは、ユーザーが Discourse の ログイン ボタンをクリックした場合とまったく同じ方法で認証プロセスを処理します。

認証プロバイダーサイトで既にログインしているユーザーに対して認証プロセスをシームレスにするには、認証プロバイダーの DiscourseConnect コードで、ユーザーがログインしているかどうかを確認する必要があります。ユーザーがログインしていない場合は、認証プロバイダーサイトでのログインプロセスに進めます。ユーザーが既にログインしている場合は、認証プロバイダーサイトでのログインプロセスをスキップします。

ここでは、WP Discourse プラグイン のコードを使用したコメント付きの例を示します。これは、認証済みユーザーと未認証ユーザーをどのように異なる方法で処理できるかを示しています。

public function sso_parse_request( $wp ) {
    // プラグインオプションでシングルサインオン (SSO) が有効になっているかを確認
    if ( empty( $this->options['enable-sso'] ) ) {
        return null;
    }

    // 続行する前にログアウト要求を処理
    $this->handle_logout_request();

    // クエリ変数に 'sso' および 'sig' パラメータが存在するかどうかを確認
    if ( array_key_exists( 'sso', $wp->query_vars ) && array_key_exists( 'sig', $wp->query_vars ) ) {
        // 'sso' ペイロードと署名をサニタイズして、安全に使用できるようにする
        $payload = sanitize_text_field( $wp->query_vars['sso'] );
        $sig     = sanitize_text_field( $wp->query_vars['sig'] );

        // WordPress にログインしていない場合、ログインページにリダイレクトする
        // これにより、アクティブなセッションがないユーザーは最初に WordPress にログインするように求められる
        if ( ! is_user_logged_in() ) {
            // ログイン後にリダイレクトするための URL を構築
            $redirect = add_query_arg( $payload, $sig );
            // リダイレクトパラメータを使用して WordPress のログイン URL を生成
            $login    = wp_login_url( esc_url_raw( $redirect ) );

            // ログインリダイレクトの前にアクションをトリガーする(ロギングやカスタムアクションにオプション)
            do_action( 'wpdc_sso_before_login_redirect', $redirect, $login );

            // WordPress のログインページにリダイレクト
            return $this->redirect_to( $login );
        } else {
            // ユーザーが既に WordPress で認証されている場合、ログインプロセスをバイパスし、
            // SSO ペイロードと署名の検証に進む。
            $sso_secret = $this->options['sso-secret'];
            $sso        = new SSO( $sso_secret );
            
            // SSO シークレットを使用してペイロードと署名を検証
            if ( ! ( $sso->validate( $payload, $sig ) ) ) {
                // 無効な SSO リクエストを処理
                return $this->handle_error( 'parse_request.invalid_sso' );
            }

            // 現在ログインしている WordPress ユーザーを取得
            $current_user = wp_get_current_user();
            // ログインしているユーザーデータを使用して SSO パラメータを準備
            $params       = $this->get_sso_params( $current_user );

            try {
                // ペイロードからノンスを生成し、SSO ログイン文字列を構築
                $params['nonce'] = $sso->get_nonce( $payload );
                $q               = $sso->build_login_string( $params );
            } catch ( \Exception $e ) {
                // SSO パラメータ生成に問題がある場合の例外を処理
                return $this->handle_error( 'parse_request.invalid_sso_params', array( 'message' => esc_html( $e->getMessage() ) ) );
            }

            // SSO ログインのためにユーザーをリダイレクトする前にアクションをトリガー(ロギングに役立つ)
            do_action( 'wpdc_sso_provider_before_sso_redirect', $current_user->ID, $current_user );

            // 詳細な SSO ログが有効な場合に SSO 成功を記録
            if ( ! empty( $this->options['verbose-sso-logs'] ) ) {
                $this->logger->info( 'parse_request.success', array( 'user_id' => $current_user->ID ) );
            }

            // 認証済みユーザーを、SSO ログイン文字列付きの DiscourseConnect ログイン URL にリダイレクト
            return $this->redirect_to( $this->options['url'] . '/session/sso_login?' . $q );
        }
    }

    // リクエストに SSO パラメータが見つからない場合は null を返す
    return null;
}

return_path を非 Discourse URL に設定する

Discourse では、ユーザーをログインさせて非 Discourse URL にリダイレクトさせることが可能です。これを機能させるには、discourse connect allowed redirect domains サイト設定にドメインを追加する必要があることに注意してください。デフォルトでは、この設定は空白であり、非 Discourse URL へのリダイレクトを防いでいます。ワイルドカードとして * を使用してすべてのドメインを許可することもできます。これを有効にする場合は、ユーザーをリダイレクトさせたい非 Discourse URL の場合は、return_path パラメータに絶対 URL を使用するようにしてください。

「いいね!」 21

SSO経由で毎回ログインする必要があるのですが、この方法を使用しています。

シームレスにするにはどうすればよいでしょうか。つまり、Discourseにすでにログインしている場合、再度ログインするためにSSOページに移動する必要がなくなりますか?

「いいね!」 1

何かが変更されていない限り、リンクはそのように機能することが期待されています。たとえば、Discourseにログインしていて、SSOプロバイダーサイトのリンクをクリックして https://forum.example.com/session/sso?return_path=/t/some-slug/23 を指している場合、最初にログインページにアクセスしなくても、/t/some-slug/23 にシームレスにリダイレクトされるはずです。

「いいね!」 1

Discourse の最新アップデートを適用済みですが、SSO は次のように機能します。

ご覧のとおり、すでにログインしていますが、https://forum.example.com/session/sso?return_path=/t/some-slug/23 のような URL を入力すると、再度 SSO ログインページにリダイレクトされます。

https://forum.example.com/session/sso?return_path=/t/some-slug/23 のようなルートにアクセスすると、Discourse にログインしているかどうかにかかわらず、Discourse は discourse connect url にリダイレクトすると考えられます。これは次の場所で発生します。

その後、SSO プロバイダーサイトは、サイトに既にログインしているユーザーのケースを処理することが期待されます。WP Discourse プラグインは次のように処理します。

そのコード(else ステートメントの後に続く部分)は、既に WordPress にログインしているユーザーのケースを処理します。それらは return_path クエリパラメーターによって提供される URL にリダイレクトされます。したがって、ユーザーの観点からは、直接リターンパス URL に移動しますが、実際には SSO プロバイダーサイトにリダイレクトされ、その後 Discourse に戻ります。

あなたのサイトでの問題は、SSO コードが既にサイトにログインしているユーザーのケースを処理していないことだと考えられます。

現在、これをテストする設定がありません。コードの読み方を間違っている可能性があります。コードを確認する前は、Discourse 側でユーザーが既に Discourse にログインしているかどうかを確認するチェックが実行されると考えていましたが、どうやらそうではないようです。

「いいね!」 3

ご説明いただきありがとうございます。

はい、それは私たちがSSOで修正する予定のことです。

しかし

Discourse側でこれをチェックすることで、より良いユーザーエクスペリエンスが実現できたと思います。

「いいね!」 1

OPを編集して、プロセスがどのように機能するか、またSSOプロバイダーサイトで既に認証されているユーザーのケースをどのように処理するかについての詳細を追加しました。これは何度か提起された質問です。

おそらく、このトピックのより詳細でないバージョンを Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso) - #482 に統合できるかもしれません。return_path クエリパラメータは、そのトピックでは言及されていません。