Crear un enlace de inicio de sesión de DiscourseConnect

:bookmark: Esta documentación explica cómo crear enlaces en un sitio proveedor de DiscourseConnect que inician sesión a los usuarios en Discourse y los redirigen a una URL específica de Discourse.
:person_raising_hand: Nivel de usuario requerido: Administrador

Los sitios que utilizan DiscourseConnect pueden agregar enlaces en su sitio proveedor de DiscourseConnect que iniciarán sesión a los usuarios en Discourse y los redirigirán a una URL específica de Discourse. Esto se logra creando enlaces que apuntan a la ruta /session/sso y que tienen un parámetro return_path establecido en la ruta de la página de Discourse a la que desea que los usuarios lleguen.
La propiedad href del enlace debe tener la forma que se muestra a continuación, con la ruta a la que desea que los usuarios lleguen sustituida por <ruta_relativa>:

https://foro.ejemplo.com/session/sso?return_path=<ruta_relativa>

Una etiqueta de anclaje de ejemplo que iniciará sesión a un usuario y lo redirigirá a la página de inicio de un sitio de Discourse:

<a href="https://foro.ejemplo.com/session/sso?return_path=/">Comunidad</a>

Una etiqueta de anclaje de ejemplo que iniciará sesión a un usuario y lo redirigirá a la página de Temas Principales:

<a href="https://foro.ejemplo.com/session/sso?return_path=/top">Temas Principales</a>

Cómo se almacena return_path en Discourse

Discourse almacena el valor del parámetro return_path en la sesión del servidor, indexado por el nonce de SSO que se genera cuando un usuario visita la ruta /session/sso. Al final del proceso de autenticación de DiscourseConnect, Discourse busca el return_path utilizando el nonce y redirige a los usuarios a esa ruta.

Hacer que el proceso sea fluido para usuarios autenticados

Cuando un usuario visita la ruta /session/sso de Discourse, es redirigido a la URL establecida por la configuración del sitio discourse connect url. El proveedor de DiscourseConnect manejará el proceso de autenticación de la misma manera que lo haría si el usuario hubiera hecho clic en el botón Iniciar sesión de Discourse.

Para que el proceso de autenticación sea fluido para los usuarios que ya han iniciado sesión en el sitio proveedor de autenticación, el código DiscourseConnect del proveedor de autenticación debe verificar si el usuario ha iniciado sesión o no. Si el usuario no ha iniciado sesión, se le guía a través del proceso de inicio de sesión del proveedor de autenticación. Si el usuario ya ha iniciado sesión, se omite el proceso de inicio de sesión en el sitio del proveedor de autenticación.

Aquí hay un ejemplo comentado, utilizando código del plugin WP Discourse. Demuestra cómo se puede manejar de manera diferente a los usuarios autenticados en comparación con los no autenticados:

public function sso_parse_request( $wp ) {
    // Comprobar si el inicio de sesión único (SSO) está habilitado en las opciones del plugin
    if ( empty( $this->options['enable-sso'] ) ) {
        return null;
    }

    // Manejar cualquier solicitud de cierre de sesión antes de continuar con el SSO
    $this->handle_logout_request();

    // Comprobar si los parámetros 'sso' y 'sig' existen en las variables de consulta
    if ( array_key_exists( 'sso', $wp->query_vars ) && array_key_exists( 'sig', $wp->query_vars ) ) {
        // Sanitizar la carga útil 'sso' y la firma para asegurar que son seguras para su uso
        $payload = sanitize_text_field( $wp->query_vars['sso'] );
        $sig     = sanitize_text_field( $wp->query_vars['sig'] );

        // Si el usuario no ha iniciado sesión en WordPress, redirigir a la página de inicio de sesión
        // Esto asegura que a los usuarios sin una sesión activa se les solicite iniciar sesión en WordPress primero
        if ( ! is_user_logged_in() ) {
            // Construir una URL para redirigir de vuelta después de iniciar sesión
            $redirect = add_query_arg( $payload, $sig );
            // Generar la URL de inicio de sesión de WordPress con el parámetro de redirección
            $login    = wp_login_url( esc_url_raw( $redirect ) );

            // Activar una acción antes de la redirección de inicio de sesión (opcional para registrar o acciones personalizadas)
            do_action( 'wpdc_sso_before_login_redirect', $redirect, $login );

            // Redirigir a la página de inicio de sesión de WordPress
            return $this->redirect_to( $login );
        } else {
            // Si el usuario ya está autenticado en WordPress, omitir el proceso de inicio de sesión
            // y proceder con la validación de la carga útil y la firma del SSO.
            $sso_secret = $this->options['sso-secret'];
            $sso        = new SSO( $sso_secret );
            
            // Validar la carga útil y la firma usando el secreto de SSO
            if ( ! ( $sso->validate( $payload, $sig ) ) ) {
                // Manejar solicitudes de SSO inválidas
                return $this->handle_error( 'parse_request.invalid_sso' );
            }

            // Obtener el usuario actual de WordPress con sesión iniciada
            $current_user = wp_get_current_user();
            // Preparar los parámetros de SSO utilizando los datos del usuario con sesión iniciada
            $params       = $this->get_sso_params( $current_user );

            try {
                // Generar un nonce a partir de la carga útil y construir la cadena de inicio de sesión SSO
                $params['nonce'] = $sso->get_nonce( $payload );
                $q               = $sso->build_login_string( $params );
            } catch ( \Exception $e ) {
                // Manejar excepciones si hay un problema con la generación de parámetros SSO
                return $this->handle_error( 'parse_request.invalid_sso_params', array( 'message' => esc_html( $e->getMessage() ) ) );
            }

            // Activar una acción antes de redirigir al usuario para el inicio de sesión SSO (útil para el registro)
            do_action( 'wpdc_sso_provider_before_sso_redirect', $current_user->ID, $current_user );

            // Registrar el éxito del SSO si el registro detallado está habilitado
            if ( ! empty( $this->options['verbose-sso-logs'] ) ) {
                $this->logger->info( 'parse_request.success', array( 'user_id' => $current_user->ID ) );
            }

            // Redirigir al usuario autenticado a la URL de inicio de sesión de DiscourseConnect con la cadena de inicio de sesión SSO
            return $this->redirect_to( $this->options['url'] . '/session/sso_login?' . $q );
        }
    }

    // Devolver nulo si no se encuentran parámetros SSO en la solicitud
    return null;
}

Establecer la ruta de retorno a URL que no son de Discourse

Discourse permite iniciar sesión a un usuario y redirigirlo a una URL que no es de Discourse. Tenga en cuenta que para que esto funcione, debe agregar el dominio a la configuración del sitio discourse connect allowed redirect domains. Por defecto, esta configuración está en blanco, lo que impide redirecciones a URL que no son de Discourse. También puede usar * como comodín para permitir todos los dominios. Si lo habilita, asegúrese de usar la URL absoluta en el parámetro return_path para cualquier URL que no sea de Discourse a la que desee dirigir a los usuarios.

21 Me gusta

Estoy usando este método pero cada vez necesito iniciar sesión a través de SSO.

¿Cómo podemos hacerlo sin interrupciones, es decir, si ya he iniciado sesión en Discourse, no habría necesidad de ir a la página de SSO para iniciar sesión nuevamente?

1 me gusta

A menos que algo haya cambiado, así es como se espera que funcione el enlace. Por ejemplo, si has iniciado sesión en Discourse y haces clic en un enlace del sitio proveedor de SSO que apunta a https://forum.example.com/session/sso?return_path=/t/some-slug/23, deberías ser redirigido sin problemas a /t/some-slug/23 sin tener que visitar la página de inicio de sesión primero.

1 me gusta

Ya estoy en la última actualización de Discourse y así es como funciona el SSO para mí:

Como puedes ver, ya he iniciado sesión, pero cuando introduzco una URL como https://forum.example.com/session/sso?return_path=/t/some-slug/23, se me redirige de nuevo a la página de inicio de sesión del SSO.

Creo que lo que está sucediendo es que cuando visitas una ruta como https://forum.example.com/session/sso?return_path=/t/some-slug/23, Discourse te redirige a la discourse connect url, independientemente de si has iniciado sesión en Discourse o no. Eso sucede aquí:

Se espera que el sitio proveedor de SSO maneje el caso de los usuarios que ya han iniciado sesión en el sitio. Así es como lo maneja el plugin WP Discourse:

Ese código (lo que sigue a la declaración else) maneja el caso de los usuarios que ya han iniciado sesión en WordPress. Se les redirige de nuevo a la URL que se proporciona a través del parámetro de consulta return_path. Por lo tanto, desde el punto de vista del usuario, se le lleva directamente a la URL de la ruta de retorno, pero lo que realmente sucede es que se le redirige al sitio proveedor de SSO y luego de vuelta a Discourse.

Creo que el problema en tu sitio es que tu código SSO no está manejando el caso de los usuarios que ya han iniciado sesión en el sitio.

No tengo las cosas configuradas para probar esto en este momento. Es posible que esté leyendo el código incorrectamente. Antes de mirar el código, pensé que se realizaba una verificación en el lado de Discourse para ver si el usuario ya había iniciado sesión en Discourse, pero no parece ser así como funciona.

3 Me gusta

Muchas gracias por tu explicación.

Sí, eso es algo que vamos a arreglar en nuestro SSO.

Sin embargo

Creo que tener esto verificado en el lado de Discourse habría creado una mejor experiencia de usuario.

1 me gusta

Edité el OP para agregar detalles sobre cómo funciona el proceso y cómo manejar el caso de usuarios que ya están autenticados en el sitio del proveedor SSO. Es una pregunta que ha surgido varias veces.

Posiblemente, una versión menos detallada de este tema podría integrarse en Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso) - #482. El parámetro de consulta return_path no se menciona en ese tema.