Refatoração do Plugin OpenID Connect

Olá a todos,

Estou buscando melhorar nossa postura de segurança e, parte disso, significa que precisamos evitar o uso de credenciais secretas sempre que possível.
Infelizmente, o plugin OIDC exigia uma credencial de segredo do cliente para acessar o endpoint /userinfo, e eu não conseguia configurar meu fórum com ele habilitado.

Felizmente, a especificação OIDC é definida de uma forma que não requer o uso de tokens secretos.
Se seguirmos o fluxo id_token, o IdP nos enviará todas as informações necessárias para autenticar um usuário sem ter que contatar o IdP novamente.

Isso é seguro porque o redirecionamento é configurado no IdP e não precisamos nos preocupar com o token do portador sendo encaminhado para o destino errado.

Eu fui em frente e criei um patch para o plugin OIDC para suportar o fluxo id_token e submeti um pull request aqui:

O PR não está totalmente completo, pois ainda precisa de testes unitários, mas está praticamente pronto e confirmei que isso funciona corretamente com o Azure AD (Entra ID).

Para referência, aqui está a documentação para o plugin OIDC:

1 curtida

Eu acho que o que você está descrevendo aqui é o "Fluxo Implícito" do OpenID Connect, que funciona sem comunicação de servidor para servidor, e, portanto, não precisa de um segredo compartilhado. Em vez disso, todas as informações são transmitidas através dos redirecionamentos HTTP, e o ID token é criptograficamente verificado usando as chaves públicas do Documento de Descoberta.

Tudo bem, e acho que é uma solicitação de recurso válida. Mas é um sistema muito diferente do nosso plugin atual, que usa o fluxo de código de autorização. Este fluxo usa comunicação de servidor para servidor com um segredo compartilhado, e, portanto, nenhuma verificação criptográfica do id_token é necessária. Isso é mais simples em alguns aspectos, mas mais complexo em outros.

Importante: não podemos simplesmente alterar a implementação padrão no plugin. Sites que usam o fluxo de código de autorização precisam continuar usando-o. Pelo que me lembro, muitos provedores de identidade nem sequer suportam o Fluxo Implícito.

Então, acho que há dois caminhos a seguir aqui:

  1. Adicionamos suporte para o "fluxo implícito" no plugin OIDC, mas o tornamos algo opcional. Acho improvável que aceitemos um PR que introduza a gema openid-connect como dependência do Discourse core, portanto, ele precisaria ser implementado dentro de nossa estratégia atual.

    O plugin discourse-lti (integração de ferramentas de aprendizado) pode ser uma fonte útil de inspiração, porque o protocolo LTI é baseado no fluxo implícito OIDC. A lógica de decodificação do id_token está aqui

    ou alternativamente

  2. Você constrói o fluxo implícito como um plugin totalmente novo. Neste caso, você teria a liberdade de fazer o que quisesse em termos de implementação (mas também teria que mantê-lo sozinho)

Vale notar também que o “Fluxo Implícito” do OIDC aumenta a área de superfície para ataques CSRF e exposição de tokens:

Acho que ainda pode fazer sentido para algumas situações. Mas parece que o fluxo de Código de Autorização (padrão do Discourse) com PKCE (opcional no Discourse) é a maneira mais recomendada de usar OIDC.

Obrigado pela resposta detalhada!

Acho que ainda pode fazer sentido em algumas situações. Mas parece que o fluxo de Código de Autorização (o padrão do Discourse) com PKCE (opcional no Discourse) é a maneira mais recomendada de usar OIDC.

Isso é muito interessante - eu não tinha compreendido totalmente o PKCE até agora. Com base na minha leitura da documentação do Auth0, parece que (em certas configurações) este fluxo tem as vantagens do fluxo implícito (sem segredo do cliente) sem nenhuma das desvantagens.

Será que isso é algo que pode ser feito em vez disso? Parece que é tão simples quanto remover o parâmetro client_secret do endpoint /token.

E, finalmente, minha principal preocupação é apenas eliminar a necessidade de um segredo do cliente :slight_smile:

Ok - isso funciona com a seguinte configuração:

"DISCOURSE_OPENID_CONNECT_ENABLED": "true",
"DISCOURSE_OPENID_CONNECT_DISCOVERY_DOCUMENT": "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration",
"DISCOURSE_OPENID_CONNECT_CLIENT_ID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"DISCOURSE_OPENID_CONNECT_CLIENT_SECRET": "",
"DISCOURSE_OPENID_CONNECT_VERBOSE_LOGGING": "true",
"DISCOURSE_OPENID_CONNECT_USE_PKCE": "true"

E a seguinte alteração no código:

diff --git a/plugins/discourse-openid-connect/lib/omniauth_open_id_connect.rb b/plugins/discourse-openid-connect/lib/omniauth_open_id_connect.rb
index 410a88f46dc..e74ee360aae 100644
--- a/plugins/discourse-openid-connect/lib/omniauth_open_id_connect.rb
+++ b/plugins/discourse-openid-connect/lib/omniauth_open_id_connect.rb
@@ -73,6 +73,11 @@ module OmniAuth
              )
           options[:client_options][:auth_scheme] = :request_body
         end
+
+        # Se estivermos usando PKCE _e_ não houver client_secret, use o esquema de autenticação request_body.
+        if options[:pkce] && options[:client_secret].empty?
+          options[:client_options][:auth_scheme] = :request_body
+        end
       end
 
       def request_phase

O URI de redirecionamento está configurado no painel de controle do Azure da seguinte forma: