Criar um link de login para DiscourseConnect

:bookmark: Esta documentação explica como criar links em um site provedor DiscourseConnect que efetuam login de usuários no Discourse e os redirecionam para uma URL específica do Discourse.

:person_raising_hand: Nível de usuário exigido: Administrador

Sites que usam DiscourseConnect podem adicionar links em seu site provedor DiscourseConnect que farão login de usuários no Discourse e os redirecionarão para uma URL específica do Discourse. Isso é feito criando links que apontam para a rota /session/sso e que têm um parâmetro return_path definido para o caminho da página do Discourse onde você deseja que os usuários cheguem.

A propriedade href do link deve estar no formato abaixo, com o caminho para onde você deseja que os usuários cheguem substituído por \u003crelative_path\u003e:

https://forum.exemplo.com/session/sso?return_path=\u003crelative_path\u003e

Um exemplo de tag âncora que fará login de um usuário e o redirecionará para a página inicial de um site Discourse:

\u003ca href=\"https://forum.exemplo.com/session/sso?return_path=/\"\u003eComunidade\u003c/a\u003e

Um exemplo de tag âncora que fará login de um usuário e o redirecionará para a página de Tópicos Principais:

\u003ca href=\"https://forum.exemplo.com/session/sso?return_path=/top\"\u003eTópicos Principais\u003c/a\u003e

Como o return_path é armazenado no Discourse

O Discourse armazena o valor do parâmetro return_path na sessão do servidor, indexado pelo nonce do SSO que é gerado quando um usuário visita a rota /session/sso. No final do processo de autenticação do DiscourseConnect, o Discourse procura o return_path usando o nonce e redireciona os usuários para esse caminho.

Tornando o processo contínuo para usuários autenticados

Quando um usuário visita a rota /session/sso do Discourse, ele é redirecionado para a URL definida pela configuração do site discourse connect url. O provedor DiscourseConnect lidará então com o processo de autenticação da mesma forma que faria se o usuário tivesse clicado no botão \u003cbutton\u003eLogin\u003c/button\u003e do Discourse.

Para que o processo de autenticação seja contínuo para usuários que já estão logados no site provedor de autenticação, o código DiscourseConnect do provedor de autenticação precisa verificar se o usuário está logado ou não. Se o usuário não estiver logado, leve-o através do processo de login do provedor de autenticação. Se o usuário já estiver logado, pule o processo de login no site provedor de autenticação.

Aqui está um exemplo comentado, usando código do plugin WP Discourse. Ele demonstra como usuários autenticados podem ser tratados de forma diferente de usuários não autenticados:

public function sso_parse_request( $wp ) {
    // Verifica se o Single Sign-On (SSO) está ativado nas opções do plugin
    if ( empty( $this-\u003eoptions['enable-sso'] ) ) {
        return null;
    }

    // Lida com quaisquer solicitações de logout antes de prosseguir com o SSO
    $this-\u003ehandle_logout_request();

    // Verifica se os parâmetros 'sso' e 'sig' existem nas variáveis de consulta
    if ( array_key_exists( 'sso', $wp-\u003equery_vars ) \u0026\u0026 array_key_exists( 'sig', $wp-\u003equery_vars ) ) {
        // Limpa o payload 'sso' e a assinatura para garantir que sejam seguros para uso
        $payload = sanitize_text_field( $wp-\u003equery_vars['sso'] );
        $sig     = sanitize_text_field( $wp-\u003equery_vars['sig'] );

        // Se o usuário não estiver logado no WordPress, redireciona para a página de login
        // Isso garante que os usuários sem uma sessão ativa sejam solicitados a fazer login no WordPress primeiro
        if ( ! is_user_logged_in() ) {
            // Constrói uma URL para redirecionar de volta após o login
            $redirect = add_query_arg( $payload, $sig );
            // Gera a URL de login do WordPress com o parâmetro de redirecionamento
            $login    = wp_login_url( esc_url_raw( $redirect ) );

            // Aciona uma ação antes do redirecionamento de login (opcional para registro ou ações personalizadas)
            do_action( 'wpdc_sso_before_login_redirect', $redirect, $login );

            // Redireciona para a página de login do WordPress
            return $this-\u003eredirect_to( $login );
        } else {
            // Se o usuário já estiver autenticado no WordPress, pula o processo de login
            // e prossegue com a validação do payload e assinatura do SSO.
            $sso_secret = $this-\u003eoptions['sso-secret'];
            $sso        = new SSO( $sso_secret );
            
            // Valida o payload e a assinatura usando o segredo do SSO
            if ( ! ( $sso-\u003evalidate( $payload, $sig ) ) ) {
                // Lida com solicitações SSO inválidas
                return $this-\u003ehandle_error( 'parse_request.invalid_sso' );
            }

            // Obtém o usuário do WordPress atualmente logado
            $current_user = wp_get_current_user();
            // Prepara os parâmetros SSO usando os dados do usuário logado
            $params       = $this-\u003eget_sso_params( $current_user );

            try {
                // Gera um nonce a partir do payload e constrói a string de login do SSO
                $params['nonce'] = $sso-\u003eget_nonce( $payload );
                $q               = $sso-\u003ebuild_login_string( $params );
            } catch ( \\Exception $e ) {
                // Lida com exceções se houver um problema na geração dos parâmetros do SSO
                return $this-\u003ehandle_error( 'parse_request.invalid_sso_params', array( 'message' =\u003e esc_html( $e-\u003egetMessage() ) ) );
            }

            // Aciona uma ação antes de redirecionar o usuário para o login do SSO (útil para logs)
            do_action( 'wpdc_sso_provider_before_sso_redirect', $current_user-\u003eID, $current_user );

            // Registra o sucesso do SSO se o registro detalhado estiver ativado
            if ( ! empty( $this-\u003eoptions['verbose-sso-logs'] ) ) {
                $this-\u003elogger-\u003einfo( 'parse_request.success', array( 'user_id' =\u003e $current_user-\u003eID ) );
            }

            // Redireciona o usuário autenticado para a URL de login do DiscourseConnect com a string de login do SSO
            return $this-\u003eredirect_to( $this-\u003eoptions['url'] . '/session/sso_login?' . $q );
        }
    }

    // Retorna nulo se nenhum parâmetro de SSO for encontrado na solicitação
    return null;
}

Definindo o caminho de retorno para URLs que não são do Discourse

O Discourse permite que você faça login de um usuário e o redirecione para uma URL que não seja do Discourse. Observe que, para que isso funcione, você precisa adicionar o domínio à configuração do site discourse connect allowed redirect domains. Por padrão, essa configuração está em branco - impedindo redirecionamentos para URLs que não são do Discourse. Você também pode usar * como um curinga para permitir todos os domínios. Se você ativá-la, certifique-se de usar a URL absoluta no parâmetro return_path para quaisquer URLs que não sejam do Discourse para as quais você deseja direcionar os usuários.

21 curtidas

Estou usando este método, mas toda vez preciso fazer login via sso.

Como podemos torná-lo contínuo, ou seja, se eu já fiz login no Discourse, não haverá necessidade de ir para a página sso para fazer login novamente?

1 curtida

A menos que algo tenha mudado, é assim que o link deve funcionar. Por exemplo, se você estiver logado no Discourse e clicar em um link no site do provedor sso que aponta para https://forum.example.com/session/sso?return_path=/t/some-slug/23, você deverá ser redirecionado continuamente para /t/some-slug/23 sem ter que visitar a página de login primeiro.

1 curtida

Já estou na versão mais recente do Discourse e é assim que o SSO funciona para mim:

Como você pode ver, já estou logado, mas quando insiro um URL como https://forum.example.com/session/sso?return_path=/t/some-slug/23, sou redirecionado para a página de login do SSO novamente.

Eu acho que o que está acontecendo é que, ao visitar uma rota como https://forum.example.com/session/sso?return_path=/t/some-slug/23, o Discourse redireciona você para a discourse connect url, independentemente de você estar logado no Discourse ou não. Isso acontece aqui:

O site do provedor de SSO deve então lidar com o caso de usuários que já estão logados no site. Veja como o plugin WP Discourse lida com isso:

Esse código (o que segue a instrução else) lida com o caso de usuários que já estão logados no WordPress. Eles são redirecionados de volta para a URL fornecida pelo parâmetro de consulta return_path. Do ponto de vista do usuário, eles são levados diretamente para a URL do caminho de retorno, mas o que realmente acontece é que eles são redirecionados para o site do provedor de SSO e, em seguida, de volta para o Discourse.

Eu acho que o problema no seu site é que seu código de SSO não está lidando com o caso de usuários que já estão logados no site.

Eu não tenho as coisas configuradas para testar isso agora. É possível que eu esteja lendo o código incorretamente. Antes de olhar o código, eu pensei que uma verificação era executada no lado do Discourse para ver se o usuário já estava logado no Discourse, mas não parece ser assim que funciona.

3 curtidas

Muito obrigado pela sua explicação.

Sim, isso é algo que vamos corrigir em nosso sso.

No entanto

Eu acho que ter isso verificado no lado do Discourse teria criado uma melhor experiência do usuário.

1 curtida

Editei o OP para adicionar detalhes sobre como o processo funciona e como lidar com o caso de usuários que já estão autenticados no site do provedor SSO. É uma questão que surgiu algumas vezes.

Possivelmente uma versão menos detalhada deste tópico poderia ser integrada em Setup DiscourseConnect - Official Single-Sign-On for Discourse (sso) - #482. O parâmetro de consulta return_path não é mencionado nesse tópico.