OAUTH2 básico - um pesadelo :-(

Olá pessoal,

Estou tentando configurar o OAUTH2 há 2 dias e estou tendo problemas.

Configurei tudo de acordo com a documentação, mas estou recebendo isso o tempo todo:

(oauth2_basic) Falha na autenticação! invalid_scope: OmniAuth::Strategies::OAuth2::CallbackError, invalid_scope | Escopo(s) inválido(s)/desconhecido(s)
Iniciado GET "/auth/failure?message=invalid_scope&origin=<censurado>%2Flatest&strategy=oauth2_basic" para 10.153.107.106 em 2024-11-27 05:30:33 +0000
Processando por Users::OmniauthCallbacksController#failure como HTML
  Parâmetros: {\"message\"=>\"invalid_scope\", \"origin\"=>\"\u003ccensored\u003e/latest\", \"strategy\"=>\"oauth2_basic\"}

Minha configuração no painel (em relação aos escopos) é a seguinte:

O IdP está configurado assim:

Alguém tem alguma dica de como depurar isso um pouco mais:

curls legais, com os quais eu poderia testar, ou uma saída de depuração melhor (já ativei a opção de depuração detalhada)?

Estou ficando louco com isso :frowning:

Obrigado e um abraço!

WS

ATUALIZAÇÃO:

Acabei de reverter as opções para apenas “scope”

mas não funcionou :frowning:

2 curtidas

percebi que nosso IdP estava com o escopo errado em sua interface de usuário…

openId está na interface de usuário
openid é o real :slight_smile:

um passo adiante, mas agora estou recebendo um timeout

image

após esta chamada


OAuth2 Debugging: request POST https://<myAuthProvider/auth/oauth2/realms/root/realms/<realm>/access_token

Headers:
--- !ruby/hash-with-ivars:Faraday::Utils::Headers
ivars:
  :@names:
    user-agent: User-Agent
    content-type: Content-Type
elements:
  User-Agent: Faraday v2.12.1
  Content-Type: application/x-www-form-urlencoded


Body:
---
client_id: <client-id>
client_secret: <client-secret>
grant_type: authorization_code
code: <code
redirect_uri: https://<myDiscourse>/auth/oauth2_basic/callback

terminando em

Mas como a chamada de autorização que obtém o código foi bem-sucedida, não pode haver nenhum problema de rede, certo?

1 curtida

Ao emitir a chamada com um curl

curl --request POST \
  --url https://<IdP address>/auth/oauth2/realms/root/realms/<realm>/access_token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data client_id=<clientId> \
  --data client_secret=<secret> \
  --data grant_type=authorization_code \
  --data code=<code > \
  --data redirect_uri=https://<discourse>/auth/oauth2_basic/callback

de dentro do contêiner docker do discourse, recebo isto

{
  "access_token": "<token>",
  "refresh_token": "<refresh_token>",
  "scope": "openid profile email",
  "id_token": "<censored>",
  "token_type": "Bearer",
  "expires_in": 7199
}

Pode ser um problema por causa disto?

image

Estes campos não estão na resposta :frowning:

2 curtidas

ninguém? :frowning:

1 curtida

Estou muito interessado no tópico porque estou pesquisando a quantidade de esforço que seria necessária para a minha primeira página de discurso para o meu bairro e, em seguida, gradualmente para as escolas dos meus filhos. Estou sobrecarregado com notificações de todas as diferentes plataformas.
Reduzir o atrito para a página é fundamental, então gostaria de oferecer alguma ajuda. Sou novato em Ruby, então só posso oferecer alguns recursos que encontrei e ideias sobre os recursos.

Você pode já ter olhado para isso, mas talvez queira verificar o repositório no GitHub para discourse-openid-connect

O repositório tem 37 contribuidores e imagino que um deles seria capaz de ajudá-lo a responder à sua pergunta.

Espero que isso ajude, pois esta é uma ótima pergunta.

1 curtida

Olá @thecatfix,

Parece promissor. Vou experimentar imediatamente e dar feedback o mais rápido possível.
Infelizmente, não tenho tempo hoje. Talvez eu consiga trabalhar nisso no fim de semana.

Muito obrigado e tenha um ótimo fim de semana!

Olá,

Não esperei e testei hoje, mas de alguma forma o pessoal do nosso departamento de EAM estragou essa descoberta. Não está funcionando e estou recebendo “invalid client - authentication failed”, apesar de ter 1000% de certeza de que as credenciais funcionam (testei com curls).
Então, estou de volta ao início.

O interessante é que estou recebendo um timeoutError o tempo todo para a requisição ao endpoint de autorização, e isso acontece bem rápido.


Faraday::TimeoutError (Net::ReadTimeout with #<TCPSocket:(closed)>)

Encontrei isso e estou curioso se algo assim também é possível no Discourse?

Eu não sou um cara do Ruby, então vou precisar de ajuda.

Quero depurar cada requisição que está sendo tratada no lado do Discourse. Isso é possível?
Também pensei em instalar um proxy local para interceptar as chamadas, mas antes de me aprofundar nisso, queria perguntar se existem métodos mais fáceis :slight_smile:

Você quer ver o tráfego HTTP bruto ou poder pular para o código?

O tráfego bruto …

requisições sendo enviadas com cabeçalhos e carga útil …

isso é possível de alguma forma?

A maneira mais fácil de fazer isso é de dentro do contêiner; você pode interceptar e imprimir requisições entre nginx e Discourse entrando no contêiner e executando:

apt-get update && apt-get -y install scapy
scapy

# no prompt do scapy, cole:
class Callback:
  def __init__(self):
    self.last = None
  def prn(self, p):
    if p != self.last: # pcaps em lo capturam duas vezes
      self.last = p
      p.hide_defaults()
      print(repr(p)) # esta linha imprime o pacote, mantenha-a ou deixe-a
      if scapy.packet.Raw in p.layers():
        try:
          print(p.load.decode())
        except:
          print(p.load)

sniff(filter="port 3000", iface="lo", prn=Callback().prn)
1 curtida

Isso é ótimo, obrigado… estava procurando exatamente por uma ferramenta assim!

Hmm… mas
parece que o discourse está recebendo o callback e então…
Pelo que entendi, ele deveria então contatar o userinfoendpoint e obter as informações do usuário com o código recuperado, mas estou recebendo um HTTP 500


GET /auth/oauth2_basic/callback?code=_B1HRB1e6kZKc8nuGLkzGC8&iss=https%3A%2F%2F%3CmyAuthDomain%3E%3A443%2Fauth%2Foauth2%2Frealms%2Froot%2Frealms%2Fintranetrealm&state=544801ae7e8262ea1667ea7531487f28e83aae232d5182b4&client_id=eadaa55a-1697-494a-8fg5-bb1137c68caa HTTP/1.0
Host: <discourseHost>
X-Request-Start: t=1732956951.485
X-Real-IP: 10.111.101.84
X-Forwarded-For: 10.131.101.84
X-Forwarded-Proto: https
Connection: close
cache-control: max-age=0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
sec-fetch-site: same-site
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
referer: https://discourseHost/
accept-encoding: gzip, deflate, br, zstd
accept-language: de-DE,de;q=0.9
priority: u=0, i
cookie: lbwdn=01; win=teUNUPSc8oibB......

<
Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |
IP  ihl=5 len=52 id=7742 flags=DF frag=0 ttl=127 proto=tcp chksum=0xdf83 src=127.0.0.1 dst=127.0.0.1 |
TCP  sport=3000 dport=33574 seq=271589520 ack=3955524768 dataofs=8 flags=A window=499 chksum=0xfe28 options=[('NOP', None), ('NOP', None), ('Timestamp', (3962821542, 3962821542))] |
<
Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |
IP  ihl=5 len=281 id=55898 flags=DF frag=0 ttl=127 proto=tcp chksum=0x2282 src=127.0.0.1 dst=127.0.0.1 |
TCP  sport=3000 dport=33530 seq=491394 ack=1852886175 dataofs=8 flags=PA window=507 chksum=0xff0d options=[('NOP', None), ('NOP', None), ('Timestamp', (3962823893, 3962816870))] |
Raw  load='HTTP/1.1 500 Internal Server Error\r\nDate: Sat, 30 Nov 2024 08:55:53 GMT\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 658\r\nX-Request-Id: 7f4bafa0-7590-4b9b-8b8d-70a4fa1ae6c5\r\nX-Runtime: 10.078459\r\n\r\n' |
HTTP/1.1 500 Internal Server Error
Date: Sat, 30 Nov 2024 08:55:53 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 658
X-Request-Id: 7f4bafa0-7590-4b9b-8b8d-70a4fa1ae6c5
X-Runtime: 10.078459

.....

<
Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |
IP  ihl=5 len=52 id=15439 flags=DF frag=0 ttl=127 proto=tcp chksum=0xc172 src=127.0.0.1 dst=127.0.0.1 |
TCP  sport=33574 dport=3000 seq=3955524768 ack=271589749 dataofs=8 flags=A window=511 chksum=0xfe28 options=[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))] |
<
Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |
IP  ihl=5 len=710 id=7744 flags=DF frag=0 ttl=127 proto=tcp chksum=0xdcef src=127.0.0.1 dst=127.0.0.1 |
TCP  sport=3000 dport=33574 seq=271589749 ack=3955524768 dataofs=8 flags=PA window=512 chksum=0xbb options=[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))] |
Raw  load='<!DOCTYPE html>\n<html>\n<head>\n  <title>Oops - Error 500</title>\n  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n</head>\n<body>\n    <h1>Oops</h1>\n    <p>The software powering this discussion forum encountered an unexpected problem. We apologize for the inconvenience.</p>\n    <p>Detailed information about the error was logged, and an automatic notification generated. We\'ll take a look at it.</p>\n    <p>No further action is necessary. However, if the error condition persists, you can provide additional detail, including steps to reproduce the error, by posting a discussion topic in the site\'s feedback category.</p>\n</body>\n</html>\n' |
<!DOCTYPE html>
<html>
<head>
  <title>Oops - Error 500</title>
.....

Nas logs, ele imprime estas linhas

Uma captura de tela mostrando um erro de rede com um tempo limite, incluindo cabeçalhos HTTP e detalhes do erro. (Legenda por IA)

Parece-me que o discourse tem problemas em lidar com o callback, e agora preciso de uma visão mais profunda do que acontece nos bastidores do discourse.

Não recebo nenhuma pista nos logs, apesar de ter ativado todos os modos de depuração que conheço.

Hmmm, estou realmente preso com isso e é frustrante :frowning:

Olá pessoal,

Estou muito mais avançado agora :slight_smile:

Tinha a ver com a nossa infraestrutura EAM…

Agora tenho o próximo problema, embora :smiley:

Meu userinfotoken se parece com isso:


{
  "company-i": "A1",
  "accounting-code": "5806",
  "given_name": "Mister",
  "family_name": "Bean",
  "name": "Mister Bean",
  "departmentnumber": "Covert Operations",
  "salutation": "Dude",
  "description": "Some description",
  "preferredlanguage": "DE",
  "inumber": "723jfio-7zwe8489",
  "employeenumber": "36484332",
  "employeetype": "BigCompany",
  "uid": "f57383",
  "adupn": "f57383@europe.bigcom.corp",
  "uniqueuid": "f57383",
  "uniqueuidq": "f57383",
  "loginname": "f57383",
  "email": "Mister.Bean@bigcom.corp",
  "sub": "f57383",
  "subname": "f57383"
}

E estou recebendo este erro nos logs:


ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR:  null value in column "provider_uid" of relation "user_associated_accounts" violates not-null constraint
DETAIL:  Failing row contains (6, oauth2_basic, null, null, 2024-12-03 13:06:49.831182, {\"name\": \"Mister Bean\", \"email\": \"Mister.Bean@bigcom.corp\", \"user..., {\"token\": \"dszghsdhsdfoph\", \"expires\": true, \"expir..., {}, 2024-12-03 13:06:49.831362, 2024-12-03 13:06:49.831362).

O Discourse não parece ser capaz de mapear a resposta do userinfoendpoint para seus nomes internos.

Eu tentei configurar isso pela interface do usuário:

mas provavelmente de forma errada.

Alguém poderia me dar uma dica de como configurar isso?

Obrigado e um abraço,

WS

oauth2 json user id path está preenchido?

Certifique-se de que esses valores estejam definidos: Configure sign up and log in with Auth0 using the OAuth2 Basic Plugin.

Olá,\n\nobrigado por responder.\n\nParece que meu provedor não envia os campos provider_name e provider_uid, e, portanto, o Discourse tenta criar a entrada sem eles e obtém uma violação de NotNull.\nMas, não é responsabilidade do plugin (oauth2_basic) preencher esses valores (se nenhum vier do IdP remoto)?\nEu esperaria algo como\n\nprovider_name: "oauth2_basic"\nprovider_uid: "1234"\n\nUsado automaticamente com cada login de usuário… :frowning:

O provider_name é fixado em oauth2_basic e o provider_uid seria o que você preencher em oauth2 json user id path.

Você só precisa definir essa configuração do site para o caminho do ID com base no que seu provedor retorna. O plugin usa esse caminho JSON para preencher o provider_uid, então se ele estiver atualmente vazio ou for um caminho que não existe, o valor será nulo.

Do seu JSON acima, parece que o valor deve ser sub.

2 curtidas

Mas eu fiz isso…

Você está certo, o caminho deveria ser “sub”, mas eu configurei isso aqui

Espere um pouco… Você estava certo, e eu sou estúpido :frowning:

Configuração errada…

Funciona agora!

2 curtidas

Existe uma maneira de salvar essas configurações em algum lugar :smiley:
fazer um backup apenas das configurações de configuração?

Elas estão no banco de dados?

Isso é ótimo.

Todas as configurações do site são salvas no banco de dados. Assumindo que você tenha acesso ao console do Rails, o seguinte deve mostrar uma lista das configurações.

SiteSetting.where("name LIKE 'oauth2_%'").pluck(:name, :value)

Você também pode simplesmente criar um backup.