إنشاء رابط تسجيل دخول DiscourseConnect

:bookmark: This documentation explains how to create links on a DiscourseConnect provider site that log users into Discourse and redirect them to a specific Discourse URL.

:person_raising_hand: Required user level: Administrator

Sites using DiscourseConnect can add links on their DiscourseConnect provider site that will log users into Discourse and redirect them to a specific Discourse URL. This is done by creating links that point to the /session/sso route that have a return_path parameter set to the path of the Discourse page you want users to end up on.

The link’s href property should be in the form below, with the path you want users to end up on substituted for <relative_path>:

https://forum.example.com/session/sso?return_path=<relative_path>

An example anchor tag that will log in a user and redirect them to a Discourse site’s homepage:

<a href="https://forum.example.com/session/sso?return_path=/">Community</a>

An example anchor tag that will log in a user and redirect them to the Top Topics page:

<a href="https://forum.example.com/session/sso?return_path=/top">Top Topics</a>

How the return_path is stored on Discourse

Discourse stores the value of the return_path parameter on the session object that is created when a user visits the /session/sso route. At the end of the DiscourseConnect authentication process, Discourse redirects users to the return_path.

Making the process seamless for authenticated users

When a user visits the Discourse /session/sso route, they are redirected to the URL set by the discourse connect url site setting. The DiscourseConnect provider will then handle the authentication process in the same way as it would if the user had clicked the Discourse Login button.

For the authentication process to be seamless for users who are already logged in on the authentication provider site, the authentication provider’s DiscourseConnect code needs to check to see if the user is logged in or not. If the user isn’t logged in, take them through the auth providers login process. If the user is already logged in, skip the login process on the auth provider site.

Here’s a commented example, using code from the WP Discourse plugin. It demonstrates how authenticated users can be handled differently than unauthenticated users:

public function sso_parse_request( $wp ) {
    // Check if Single Sign-On (SSO) is enabled in the plugin options
    if ( empty( $this->options['enable-sso'] ) ) {
        return null;
    }

    // Handle any logout requests before proceeding with SSO
    $this->handle_logout_request();

    // Check if the 'sso' and 'sig' parameters exist in the query variables
    if ( array_key_exists( 'sso', $wp->query_vars ) && array_key_exists( 'sig', $wp->query_vars ) ) {
        // Sanitize the 'sso' payload and signature to ensure they are safe for use
        $payload = sanitize_text_field( $wp->query_vars['sso'] );
        $sig     = sanitize_text_field( $wp->query_vars['sig'] );

        // If the user is not logged in to WordPress, redirect to the login page
        // This ensures that users without an active session are prompted to log in to WordPress first
        if ( ! is_user_logged_in() ) {
            // Construct a URL to redirect back to after logging in
            $redirect = add_query_arg( $payload, $sig );
            // Generate the WordPress login URL with the redirect parameter
            $login    = wp_login_url( esc_url_raw( $redirect ) );

            // Trigger an action before the login redirection (optional for logging or custom actions)
            do_action( 'wpdc_sso_before_login_redirect', $redirect, $login );

            // Redirect to the WordPress login page
            return $this->redirect_to( $login );
        } else {
            // If the user is already authenticated in WordPress, bypass the login process
            // and proceed with validating the SSO payload and signature.
            $sso_secret = $this->options['sso-secret'];
            $sso        = new SSO( $sso_secret );
            
            // Validate the payload and signature using the SSO secret
            if ( ! ( $sso->validate( $payload, $sig ) ) ) {
                // Handle invalid SSO requests
                return $this->handle_error( 'parse_request.invalid_sso' );
            }

            // Get the current logged-in WordPress user
            $current_user = wp_get_current_user();
            // Prepare SSO parameters using the logged-in user's data
            $params       = $this->get_sso_params( $current_user );

            try {
                // Generate a nonce from the payload and build the SSO login string
                $params['nonce'] = $sso->get_nonce( $payload );
                $q               = $sso->build_login_string( $params );
            } catch ( \Exception $e ) {
                // Handle exceptions if there is an issue with SSO parameter generation
                return $this->handle_error( 'parse_request.invalid_sso_params', array( 'message' => esc_html( $e->getMessage() ) ) );
            }

            // Trigger an action before redirecting the user for SSO login (useful for logging)
            do_action( 'wpdc_sso_provider_before_sso_redirect', $current_user->ID, $current_user );

            // Log the SSO success if verbose logging is enabled
            if ( ! empty( $this->options['verbose-sso-logs'] ) ) {
                $this->logger->info( 'parse_request.success', array( 'user_id' => $current_user->ID ) );
            }

            // Redirect the authenticated user to the DiscourseConnect login URL with the SSO login string
            return $this->redirect_to( $this->options['url'] . '/session/sso_login?' . $q );
        }
    }

    // Return null if no SSO parameters are found in the request
    return null;
}

Setting the return path to non-Discourse URLs

Discourse allows you to login a user and redirect them to a non-Discourse URL. Note that for this to work you need to add the URL to the discourse connect allowed redirect domains site setting. By default this setting is blank - preventing redirects to non-Discourse URLs. If you enable it, be sure to use the absolute URL in the return_path parameter for any non-Discourse URLs that you want to direct users to.

Last edited by @simon 2024-09-25T07:22:09Z

Check documentPerform check on document:
20 إعجابًا

أنا أستخدم هذه الطريقة ولكن في كل مرة أحتاج إلى تسجيل الدخول عبر 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) بالنسبة لي:

كما ترى، لقد تم تسجيل دخولي بالفعل، ولكن عندما أدخل عنوان URL مثل https://forum.example.com/session/sso?return_path=/t/some-slug/23، سأتم إعادة توجيهي إلى صفحة تسجيل الدخول الأحادي (SSO) مرة أخرى.

أعتقد أن ما يحدث هو أنه عند زيارة مسار مثل https://forum.example.com/session/sso?return_path=/t/some-slug/23، يقوم Discourse بإعادة توجيهك إلى discourse connect url، بغض النظر عما إذا كنت مسجلاً الدخول إلى Discourse أم لا. يحدث هذا هنا:

من المتوقع بعد ذلك أن يتعامل موقع موفر SSO مع حالة المستخدمين الذين قاموا بتسجيل الدخول بالفعل إلى الموقع. إليك كيف يتعامل المكون الإضافي WP Discourse مع الأمر:

هذا الرمز (ما يلي عبارة else) يتعامل مع حالة المستخدمين الذين قاموا بتسجيل الدخول بالفعل إلى WordPress. يتم إعادة توجيههم مرة أخرى إلى عنوان URL الذي يتم توفيره بواسطة معلمة الاستعلام return_path. لذلك من وجهة نظر المستخدم، يتم نقلهم مباشرة إلى عنوان URL للمسار المرتجع، ولكن ما يحدث بالفعل هو أنهم يعاد توجيههم إلى موقع موفر SSO، ثم مرة أخرى إلى Discourse.

أعتقد أن المشكلة في موقعك هي أن رمز SSO الخاص بك لا يتعامل مع حالة المستخدمين الذين قاموا بتسجيل الدخول بالفعل إلى الموقع.

ليس لدي الإعدادات اللازمة لاختبار هذا الآن. من الممكن أن أكون أقرأ الرمز بشكل غير صحيح. قبل النظر إلى الرمز، اعتقدت أنه تم إجراء فحص على جانب Discourse لمعرفة ما إذا كان المستخدم قد قام بتسجيل الدخول بالفعل إلى Discourse، ولكن يبدو أن هذه ليست الطريقة التي يعمل بها الأمر.

3 إعجابات

شكرا جزيلا على شرحك.

نعم، هذا شيء سنقوم بإصلاحه في الدخول الأحادي الخاص بنا.

ولكن

أعتقد أن وجود هذا الفحص من جانب Discourse كان سيخلق تجربة مستخدم أفضل.

إعجاب واحد (1)

لقد قمت بتحرير المنشور الأصلي لإضافة تفاصيل حول كيفية عمل العملية، وكيفية التعامل مع حالة المستخدمين الذين تم تسجيل دخولهم بالفعل في موقع موفر تسجيل الدخول الموحد. إنه سؤال تم طرحه عدة مرات.

ربما يمكن دمج نسخة أقل تفصيلاً من هذا الموضوع في Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso) - #482. لم يتم ذكر المعلمة return_path في هذا الموضوع.