OAuth2 Básico do Discourse

:discourse2: Summary Discourse OAuth2 Basic supports basic OAuth2 providers, assuming they have a JSON API endpoint where user details can be retrieved by token.
:open_book: Install Guide This plugin is bundled with Discourse core. There is no need to install the plugin separately.

Features

This plugin allows you to use a basic OAuth2 provider as authentication for Discourse. It should work with many providers, with the caveat that they must provide a JSON endpoint for retrieving information about the user you are logging in.

This is mainly useful for people who are using login providers that aren’t very popular. If you want to use Google, Facebook or Twitter, those are included out of the box and you don’t need this plugin. You can also look for other login providers in our Github Repo.

Configuration

Basic Configuration

  1. First, register your Discourse application with your OAuth2 provider. It will require a Redirect URI which will be:

    http://DISCOURSE_HOST/auth/oauth2_basic/callback

:information_source: Replace DISCOURSE_HOST with the appropriate value, and make sure you are using https if enabled. The OAuth2 provider should supply you with a client ID and secret, as well as a couple of URLs.

  1. Visit your AdminSettingsOAuth2 Login and fill in the basic configuration for the OAuth2 provider:
  • oauth2_enabled - check this off to enable the feature
  • oauth2_client_id - the client ID from your provider
  • oauth2_client_secret - the client secret from your provider
  • oauth2_authorize_url - your provider’s authorization URL
  • oauth2_token_url - your provider’s token URL.

:information_source: If you can’t figure out the values for the above settings, check the developer documentation from your provider or contact their customer support.

Configuring the JSON User Endpoint

Discourse is now capable of receiving an authorization token from your OAuth2 provider. Unfortunately, Discourse requires more information to be able to complete the authentication.

We require an API endpoint that can be contacted to retrieve information about the user based on the token.

For example, the OAuth2 provider SoundCloud provides such a URL. If you have an OAuth2 token for SoundCloud, you can make a GET request to https://api.soundcloud.com/me?oauth_token=A_VALID_TOKEN and will get back a JSON object containing information on the user.

To configure this on Discourse, we need to set the value of the oauth2_user_json_url setting. In this case, we’ll input the value of:

https://api.soundcloud.com/me?oauth_token=:token

The part with :token tells Discourse that it needs to replace that value with the authorization token it received when the authentication completed.

There is one last step to complete. We need to tell Discourse what attributes are available in the JSON it received. Here’s a sample response from SoundCloud:

{
  "id": 3207,
  "permalink": "jwagener",
  "username": "Johannes Wagener",
  "uri": "https://api.soundcloud.com/users/3207",
  "permalink_url": "http://soundcloud.com/jwagener",
  "avatar_url": "http://i1.sndcdn.com/avatars-000001552142-pbw8yd-large.jpg?142a848",
  "country": "Germany",
  "full_name": "Johannes Wagener",
  "city": "Berlin"
}

The oauth2_json_user_id_path, oauth2_json_username_path, oauth2_json_name_path and oauth2_json_email_path variables should be set to point to the appropriate attributes in the JSON.

The only mandatory attribute is id - we need that so when the user logs on in the future that we can pull up the correct account. The others are great if available – they will make the signup process faster for the user as they will be pre-populated in the form.

Here’s how I configured the JSON path settings:

  oauth2_json_user_id_path: 'id'
  oauth2_json_username_path: 'permalink'
  oauth2_json_name_path: 'full_name'

I used permalink because it seems more similar to what Discourse expects for a username than the username in their JSON. Notice I omitted the email path: SoundCloud do not provide an email so the user will have to provide and verify this when they sign up the first time on Discourse.

If the properties you want from your JSON object are nested, you can use periods. So for example if the API returned a different structure like this:

{
  "user": {
    "id": 1234,
    "email": {
      "address": "test@example.com"
    }
  }
}

You could use user.id for the oauth2_json_user_id_path and user.email.address for oauth2_json_email_path.

If the key itself includes periods, you will need to put double quotes around it, or escape the periods with a backslash. For example, given this JSON:

{
  "example.com/uid": "myuid"
}

You would specify the path as example\.com/uid or "example.com/uid"

:warning: If you set oauth2_json_email_path, the OAuth2 provider must confirm the user owns that email address. Failure to do this can result in account takeover in Discourse!

:discourse2: Hosted by us? This plugin is available on our Business and Enterprise plans. OAuth 2.0 & OpenID Connect Support | Discourse - Civilized Discussion

Last edited by @tobiaseigen 2025-07-16T21:39:12Z

Check documentPerform check on document:
28 curtidas

Olá,
Estamos tentando integrar o Discourse com nosso aplicativo usando OAuth2 Basic, mas estamos recebendo o seguinte erro nos logs:
Nota: Estamos usando NGROK para depurar a conexão.

OAuth2 Debugging: request POST https://formshare.ngrok.io/oauth2/token

Headers: {"User-Agent"=>"Faraday v1.9.3", "Content-Type"=>"application/x-www-form-urlencoded", "Authorization"=>"Basic S2k2SFZtTVpuSTFHUExiRXVlWVJDN4CbOkNvb1k0anlQemt3dWNRV21Sa2FWOVNnbHZLbjJFT3cxc3BIMmtMck9yY21vNDM4Tg=="}

Body: {"client_id"=>"Ki6HVmMZnI1GPLbEueYRC4Cb", "client_secret"=>"...some_secret_...", "grant_type"=>"authorization_code", "code"=>"5pPCrsp0pZ84373MNaHh2cuskfc8AlbfmdwMBFIVW4n4z9aX", :redirect_uri=>"https://community.formshare.org/auth/oauth2_basic/callback"}

------------------

OAuth2 Debugging: response status 200

From POST https://formshare.ngrok.io/oauth2/token

Headers: {"content-length"=>"108", "content-type"=>"text/html; charset=UTF-8", "date"=>"Thu, 01 Sep 2022 21:42:08 GMT", "ngrok-trace-id"=>"79cdc3f1c3eae5e37a30796aebbf9bd6", "server"=>"gunicorn"}

Body: {"token_type": "Bearer", "access_token": "p0FVuwjSXL1ZINEklMAVqUlpZxSll1SgnbpE8YWP4C", "expires_in": 864000}

-----------------------------------

(oauth2_basic) Authentication failure! invalid_credentials: OAuth2::Error, {"token_type": "Bearer", "access_token": "p0FVuwjSXL1ZINEklMAVqUlpZxSll1SgnbpE8YWP4C", "expires_in": 864000}

Deixamos os parâmetros “oauth2 callback user id path” e “oauth2 callback user info paths” vazios.

Qualquer ideia é bem-vinda.

Posso usar isto para autenticar com o serviço XBL da Microsoft?

Presumo que a lógica seria semelhante a esta?

Olá a todos. Estou tentando configurar este plugin com nosso servidor Oauth2 interno com fluxo de código de autorização.

Quando um usuário clica em “Conectar com Oauth”, o endpoint /authorize funciona e um código é retornado para o callback. Mas então o Discourse mostra um erro genérico 500 “Oops. O software que alimenta este fórum de discussão encontrou um problema inesperado” e o endpoint /token não é acessado.

O log de erros diz o seguinte:
OAuth2::ConnectionError (FinalDestination: todos os IPs resolvidos foram desautorizados) lib/final_destination/ssrf_detector.rb:74:in lookup_and_filter_ips' lib/final_destination/http.rb:13:in connect’ lib/midd

hostname discourse-app
process_id 653
application_version 702f27e6ee10ac257f5fee3f331d05f5fa5d7a45
HTTP_HOST *****
REQUEST_METHOD GET
HTTP_USER_AGENT Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
HTTP_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.9
HTTP_REFERER *****
HTTP_X_FORWARDED_FOR *****
HTTP_X_REAL_IP *****
time 10:25 pm
params
code def50200babf84f7376f99fefa34369d876566b6bc0a341d8fba431999a72549ac06f6aad01df6fa43061707c525ba5d725ad
state 20139e0a134a5972566d4ddb9e5216973a

Pelo que entendi, há um problema com algum endereço IP? Atualmente, o servidor Oauth2 está hospedado em meu ambiente de desenvolvimento (localhost) e os endpoints de autorização e token estão configurados de acordo. Isso é um problema?

Encontrei o problema:

  1. Por algum motivo, o endpoint /token nunca foi chamado. Após preencher o máximo de opções nos parâmetros de administrador relacionados ao OAuth, o endpoint foi chamado sem resposta.
  2. Esqueci que era o servidor Discourse que chamaria o endpoint /token e não o webclient. Portanto, o servidor não conseguia alcançar meu servidor OAuth2 localhost. Colocar nosso servidor OAuth2 atrás de um domínio resolveu o problema.

Agora, consigo conectar usuários existentes, mas não entendo como registrar novos usuários através deste plugin.
Se o usuário se registra com OAuth, ele recebe um erro dizendo que não possui uma conta ativa no servidor Discourse. O que é normal, pois é um novo usuário.

Existe um callback dedicado para registrar o usuário em vez de fazer login? Ou um parâmetro específico para definir para permitir a criação de conta?

Meu servidor OAuth da empresa estava gerando uma resposta JSON /profile com um pequeno erro de digitação em um campo. Tudo ficou bem depois de corrigir o erro de digitação.
Mas tenho que dizer que os logs do Discourse podem ser muito enganosos! Não havia nada de errado com o callback.

Olá equipe,

Estou tendo um problema para extrair o ID que preciso para minha solicitação JSON de usuário da resposta de autorização. Ao ler a documentação, parece que o ID da conta é enviado em um array aninhado:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "token_type":"Bearer",
   "expires_in":1800,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
   "permissions":[
      {
        "accountId":123,
        "availableScopes":["contacts_view", "contacts_me",
"contacts_edit", "finances_view", "events_view"]
      }
   ]
}

Tentei definir o caminho do ID do usuário de retorno de chamada oauth2 como permissions[0].accountId, mas o valor do meu uid está sempre em branco. Infelizmente, as chamadas para extrair o JSON do usuário exigem esse accountId na URL JSON.

Consegui fazer isso funcionar passando permissions.first.accountId. Descobri que, ao passar permissions para uma propriedade de teste, a matriz já era analisada como uma matriz Ruby. Infelizmente, os campos parecem rejeitar a sintaxe Ruby para chamar elementos de matriz e qualquer tentativa de usar a sintaxe JavaScript resultaria em um TypeError String to Integer. Felizmente, Ruby tinha a sintaxe acima, este é o método pretendido?

Consegui fazer isso funcionar com o Authentik OAuth2, no entanto, houve alguns soluços com a configuração oauth2 user json url. Usei o endpoint user_info do Authentik para isso (/application/o/userinfo/), mas não sabia como mapear os campos. Para quem estiver procurando como configurar o Discourse com o OAuth2 do Authentik, aqui está o resumo:

  • Caminho do ID do usuário: preferred_username
  • Caminho do nome de usuário: preferred_username
  • Caminho do nome: name
  • Caminho do e-mail: email
  • Caminho do e-mail verificado: email_verified
  • Avatar: vazio.

Tive os seguintes problemas:

  1. No início, esqueci a barra final na URL JSON https://DOMAIN/application/o/userinfo/. Isso levou a solicitação de informações do usuário (link permanente para a origem) a retornar um código HTTP 301, o que causou falha no login. Não sei se a barra final deveria estar lá por especificação, mas talvez fosse bom lidar corretamente com o 301.
  2. Depurar isso acabou sendo complicado. As configurações de oauth2 debug auth foram uma salvação, mas… O Logster trunca o log de depuração antes de realmente despejar os dados de resposta significativos. Tive que modificar manualmente no contêiner a linha de log para
    log("user_json_response: #{user_json_response.status} #{user_json_response.headers} #{user_json_response.body}")
    
    Talvez essa linha de log pudesse ser atualizada? Acho que poderia ajudar outras pessoas a descobrir o caminho dos atributos JSON.
4 curtidas

Acabei de configurar o Auth0 com o plugin e descobri que os avatares não estão sendo capturados.

Estas são as configurações relevantes:

  DISCOURSE_OAUTH2_ENABLED: true
  DISCOURSE_OAUTH2_CLIENT_ID: '${DISCOURSE_OAUTH2_CLIENT_ID}'
  DISCOURSE_OAUTH2_CLIENT_SECRET: '${DISCOURSE_OAUTH2_CLIENT_SECRET}'
  DISCOURSE_OAUTH2_AUTHORIZE_URL: '${DISCOURSE_OAUTH2_ISSUER}/authorize?connection=xxx&login_options=yyy'
  DISCOURSE_OAUTH2_TOKEN_URL: '${DISCOURSE_OAUTH2_ISSUER}/oauth/token'
  DISCOURSE_OAUTH2_USER_JSON_URL: '${DISCOURSE_OAUTH2_ISSUER}/userinfo'
  DISCOURSE_OAUTH2_SCOPE: 'email openid profile'
  DISCOURSE_OAUTH2_JSON_USER_ID_PATH: 'sub'
  DISCOURSE_OAUTH2_JSON_USERNAME_PATH: 'nickname'
  DISCOURSE_OAUTH2_JSON_NAME_PATH: 'name'
  DISCOURSE_OAUTH2_JSON_EMAIL_PATH: 'email'
  DISCOURSE_OAUTH2_JSON_EMAIL_VERIFIED_PATH: 'email_verified'
  DISCOURSE_OAUTH2_JSON_AVATAR_PATH: 'picture'
  DISCOURSE_OAUTH2_EMAIL_VERIFIED: true
  DISCOURSE_OAUTH2_OVERRIDES_EMAIL: true
  DISCOURSE_OAUTH2_ALLOW_ASSOCIATION_CHANGE: false

No log de depuração, posso ver que o elemento picture está definido na resposta JSON, mas o avatar do usuário não muda, nem para usuários novos nem para os existentes.

O que eu perdi?

Qual é a melhor maneira de substituir o ícone no botão de login por outro ícone ou imagem?

.btn-social.oauth2_basic:before {
    content: url('https://www.contoso.com/path/to/image');
}

.btn-social.oauth2_basic > svg {
    display: none;
}

parece suficiente, mas um pouco improvisado.

2 curtidas

Parece que o plugin só atualiza o avatar/nome de usuário na criação inicial do usuário, não toda vez que ele faz login.

Existe alguma maneira de corrigir isso e fazer com que o plugin também atualize o avatar no login/reconexão?

Você pode usar as configurações auth overrides email, auth overrides username e auth overrides name para fazer com que essas coisas se apliquem em logins futuros. Receio que atualmente não tenhamos uma configuração semelhante para avatares, mas seria pr-welcome

2 curtidas

Obrigado! Na verdade, encontrei estes mais tarde. Eu fiz um fork do repositório e adicionei minhas próprias versões para fazê-lo funcionar com o Roblox, o que incluiu a substituição para avatares. Que eu acredito que apenas usa a substituição de avatar do DiscourseConnect para que as pessoas não possam alterá-la.

Uma coisa que eu gostaria, no entanto, é que o Roblox não forneça um e-mail no OAuth, então, infelizmente, preciso que eles se inscrevam com um e-mail. Mas isso não é um problema para vocês, haha.

Uma postagem foi dividida em um novo tópico: Login do Twitter não funciona no meta

Alguém sabe se isso ainda funciona?

Sim. Tenho certeza de que este plugin funciona. :+1:

1 curtida

Olá, consegui integrar este plugin no meu discourse discuss.frontendlead.com, estou usando teachable oauth https://docs.teachable.com/docs/oauth-quickstart-guide

No entanto, eu só quero permitir que as pessoas se registrem com sucesso se e somente se elas tiverem uma conta paga atual no teachable. Eu imaginaria que preciso adicionar funcionalidade personalizada no plugin para lidar com isso? Estou me perguntando, vocês ou mesmo eu podemos introduzir outro campo nas configurações chamado código personalizado após o oauth, que permite aos desenvolvedores realizar ações específicas após o cadastro? Ou se houver melhores sugestões, por favor, me avisem.

Editar: Eu fiz um fork do repositório e consegui fazer funcionar aqui:

Se alguém mais usando teachable estiver tentando fazer o mesmo, meu repositório funcionará imediatamente, a única coisa é que se você não comprou um curso, ele dirá que você precisa ir ao meu domínio para comprá-lo. você pode querer atualizar isso para o seu próprio caso de uso.

2 curtidas

Isso é ótimo!

Há uma situação semelhante com o registro OAuth2 no plugin Discourse Patreon. Quando o “Login com Patreon” está habilitado, ele permite que qualquer pessoa com uma conta Patreon se registre no site Discourse. O que os proprietários de sites geralmente querem é permitir que apenas seus apoiadores possam registrar contas no Discourse. Eu me pergunto se os detalhes são retornados do Patreon que permitiriam adicionar uma lógica semelhante à autenticação do Patreon?

1 curtida

Tenho exatamente o mesmo erro que @qlands acima.

Meu plano inicial era enviar as informações do perfil no token. Vendo que não funcionou, reduzi a indentação para tentar a abordagem JSON. Mas ele nem chega ao ponto de chamar o arquivo JSON.

A mensagem de erro é:

(oauth2_basic) Falha na autenticação! credenciais inválidas: OAuth2::Error, {
  "access_token":"fa79b6fe0763862f5a8fd8",
  "token_type":"Bearer",
  "expires_in":3600,
  "scope":"profile"
}

Você vê algo de errado com a resposta acima?
Por que o plugin geraria um erro de credenciais inválidas enquanto o servidor OAuth2 respondeu com um 200 com um token?