Especificação das chaves de API de usuário

I find allowed_user_api_auth_redirects default of “discourse://auth_redirect” rather restrictive, especially because “discourse” does not appear to be a valid URI scheme.

Please explain the thinking behind this default. Thank you.

I am having this issue as well. If I initiate the API from a JS application, then automatically the allowed headers are: User-Api-Key, User-Api-Client-Id even though I do not need user API keys. All I want is a simple API key but I cannot get anything to work. If I try to pass Api-Key in the headers I get a CORS error since it expects User-Api-Key. But when I try to use User-Api-Key, I get 403 errors. I am stuck. I would think this is the base usage for using the APIs. I am not trying to do anything out of the ordinary. I am simply trying to create a new topic post.

2 curtidas

That is the custom URI scheme used by the DiscourseHub app for iOS and Android.

6 curtidas

I’ve got a question concerning the “read tokens” and “write tokens”. This comment here is from 2016, so this possibly had already been changed? Or are the defaults still only “read tokens”?

Background: I’m one of the coders behind a distributed social media system. We already do have connectors to non-federating systems. The idea is to write an addon for discourse as well. But when most likely most system won’t allow users to generate tokens that allow posting, we will try another way. We already do have a mail connector. Then we will simply use the mailing list function of Discourse and we will try to enhance the returned content and will post via SMTP.

You can do write tokens if you ask for the scope upfront

3 curtidas

Of course this is always possible. But I have the feeling that this is a support nightmare. Our software has got some hundred installations with (in total) more than 10k users. When they see that there is an addon that is connection to Discourse, many will surely like to use it. And since it most likely won’t work out of the box, this will generate questions and support work from our side. Additionally it will generate work for the admins of the several Discourse installations. And very likely not all will allow it - which will cause frustration.

So possibly at first I will focus on integrating the mailing list mode mails. Or is it possible to combine these two? Means: Reading of the posts via the API, but posting via SMTP?

Hi…I don’t know to generate public_key… should I use RSA generator to get public/private key?
If so I have done with some online RSA generators. but I am getting this error:

OpenSSL::PKey::RSAError (Neither PUB key nor PRIV key: nested asn1 error) /var/www/discourse/app/controllers/user_api_keys_controller.rb:189:in `initialize'

Also, I want to ask you guys if this suits my user case:
I have an app, and I want to basically authenticate user and get the username, is generating api key flow the simplest flow for me to validate user’s login in my app? If possible, I want avoid SSO because it seems more complicate.

Same boat here, although I am only trying to use User-Api-Key (not Api-Key) to create a topic post and am getting CSRF denial from the actionpack library.

Unless the discourse server has turned off CSRF checking, posting from a third-party desktop app seems hard. I’m not about to emulate a browser.

@sam What is your take on allowing User API Keys that only have the read scope attached, to be passed via URL params on GET requests?

Use case is allowing integrations like subscribing your Improved Bookmarks with Reminders in Google Calendar using User API keys.

5 curtidas

How about creating a specific new scope, with a third parameter to indicate “get param allowed”. That way, people can’t misuse it for other things (e.g. bypassing CORS and requesting the discourse API from another site).

(from here)

SCOPES = {
    read: [:get],
    write: [:get, :post, :patch, :put, :delete],
    message_bus: [[:post, 'message_bus']],
    push: nil,
    one_time_password: nil,
    notifications: [[:post, 'message_bus'], [:get, 'notifications#index'], [:put, 'notifications#mark_read']],
    session_info: [
      [:get, 'session#current'],
      [:get, 'users#topic_tracking_state'],
      [:get, 'list#unread'],
      [:get, 'list#new'],
      [:get, 'list#latest']
    ],
+   calendar: [ [:get, 'users#bookmarks_cal', true ] ],
  }

(Aside: why are we using nested arrays here…)

10 curtidas

I like that the API key would be flagged explicitly as “allowed in GET” at the user level.

As a whole the option could be open for any GETs. The rule I like is, when operating in this mode:

  1. User API key is 100% restricted to a single specific GET controller action
  2. User API key is flagged as allowed in GET query params.

This limits the impact of any leak here via a proxy cause the key will never be reused.

I guess {get: 'list#new'} , {get: 'list#latest'} would work as well.

7 curtidas

I’m super interested in get param only type user api keys. My question is, are you guys planning to allow people to generate these keys via UI?

Probably, maybe behind a site setting or with a plugin. We do plan to normalise the feature set a bit so admin api keys also support scopes.

4 curtidas

Olá… Você consegue resolver esse problema? Estou com o mesmo problema e não consigo corrigi-lo. Tentei passar diferentes tipos de chaves, mas nada funcionou. Qualquer ajuda seria muito apreciada.

Existem bibliotecas para isso? Caso contrário, há um exemplo de implementação? Estou tentando usar PHP para identificar a conta do usuário do Discourse em uma parte separada do site. Isso parece um fluxo OAuth modificado, mas estou um pouco confuso sobre como implementá-lo.

Especificamente, não tenho certeza de como fazer toda a geração de chaves pública/privada.

Existe uma maneira de usar apenas OAuth 2 com o Discourse como provedor OAuth?

2 curtidas

Você conseguiu fazer isso usando a User-Api-Key? Eu também estou recebendo Você não tem permissão para visualizar o recurso solicitado.

1 curtida

Descobri o que fiz de errado: o payload retornado não é a própria chave da UserAPI, mas uma string JSON criptografada que precisaria ser descriptografada com a chave privada do par de chaves pública/privada.

2 curtidas

EDIT: Consegui fazer a maior parte funcionar e fornecerei uma descrição assim que estiver totalmente funcional.


Como o cliente obtém o par de chaves privada/pública e o ID?

Você consegue fornecer código para obter a chave da API do usuário com um aplicativo JavaScript? (Um aplicativo JavaScript tentando permitir que um usuário faça chamadas de API para um fórum Discourse).

Estou recebendo erros 403. Ou um erro dizendo: Desculpe, não é possível emitir chaves de API de usuário; este recurso pode estar desativado pelo administrador do site (embora meu site tenha marcado: Permitir geração de chaves de API de usuário).

Acho que o problema pode ser como gerar o par de chaves privada/pública (como isso é feito?) e, em seguida, lidar com o redirecionamento.

Qualquer código será muito apreciado.

Consegui fazer isso funcionar, após alguns testes e ajustes.

Aqui estão as etapas básicas que sigo quando: tenho um aplicativo separado que desenvolvi e quero que os usuários possam usá-lo para fazer chamadas de API a um site do Discourse.

Para isso, preciso gerar um token de API por usuário para fazer chamadas em nome de cada usuário específico (pelo menos em um ambiente Node.js/JavaScript).


Observe que, para a parte de JavaScript, encontrei o código fornecido por @KengoTODA aqui: discourse-api-key-generator/src/index.ts at main · KengoTODA/discourse-api-key-generator · GitHub, que foi muito útil.


Aqui estão as etapas que segui:

Primeiro: Gere um par de chaves pública e privada.

Isso é algo que seu aplicativo precisa gerar: uma chave pública e uma chave privada. O gist do GitHub fornece um método para fazer isso.

Segundo: Tenha uma URL de redirecionamento.

Esta é a URL para a qual o Discourse irá redirecionar, fornecendo o token de API final no payload. Se você tiver um aplicativo de desktop (ou seja, sem uma URL de navegador), a URL de redirecionamento será baseada em um protocolo personalizado que você configurou, que abre o aplicativo quando a URL de redirecionamento é inserida no navegador.

Observe que a URL de redirecionamento precisa estar na lista de permissões nas configurações do site do Discourse alvo.

O site do Discourse também provavelmente precisa ter a configuração do site marcada para “Permitir chaves de API do usuário”. Consulte a postagem original sobre este tópico para “Configurações do Site”.

Terceiro: Envie a chamada de solicitação de API para a URL de solicitação do Discourse.

Então, seu aplicativo enviará uma chamada para uma URL que segue este formato:

https://[seu site alvo do Discourse .com]/user-api-key-new"

e adicionando como parâmetros:

  • o nome do seu aplicativo
  • seu “client_id” (consegui usar hostname(), de const {hostname} = require('os') para um aplicativo de desktop, assim como no gist do GitHub mencionado acima)
  • escopos (estes são os escopos que você deseja que o usuário possa realizar por meio da API, como “write”, “read”, etc.)
  • sua chave pública (da etapa 1 acima)
  • sua URL de redirecionamento (da etapa 2 acima)
  • nonce (este é um valor que você pode escolher—usar apenas ‘1’ parece funcionar)

Quarto: O usuário autoriza seu aplicativo na página do site do Discourse aberta pela URL de solicitação.

Quando você envia a URL de solicitação com sucesso, ela abre uma página no site do Discourse informando ao usuário que seu aplicativo deseja acessar o site.

Nessa página, há um botão para o usuário permitir isso. Quando o usuário clica nesse botão, o site do Discourse redireciona para a URL de redirecionamento que você forneceu e anexa como parâmetro um ?payload=[A CHAVE DE API]. A CHAVE DE API aqui é a chave que você precisa decodificar em seu aplicativo.

Quinto: Seu aplicativo captura o valor da URL de redirecionamento (com o valor do payload) e você decodifica a CHAVE DE API.

Você está quase lá. Seu aplicativo precisa analisar a URL de redirecionamento para a qual o Discourse foi e obter a Chave de API contida no payload.

Depois de obter essa Chave de API, você precisa fazer duas coisas:

  1. Obter a chave real, não a versão codificada em URL: se você estiver obtendo um parâmetro de uma URL, ela geralmente está codificada em URL (adicionando % aqui e ali, etc.). Você precisa limpá-la. Em JavaScript, encontrei que decodeURIComponent funciona para isso.
  2. Depois de obter a CHAVE DE API limpa retornada pelo Discourse, você precisa decodificá-la. Para isso, você pode usar a decodificação em JavaScript com chaves privadas. Basicamente, você usa sua chave privada (gerada na primeira etapa acima) para decodificar a CHAVE DE API limpa. Há algum exemplo de JavaScript no gist do GitHub que referenciei acima: discourse-api-key-generator/src/index.ts at main · KengoTODA/discourse-api-key-generator · GitHub

Depois de executar seu código de decodificação, você tem o próprio token, que agora pode usar para fazer chamadas de API autenticadas em nome do usuário.

Sexto: Use o token (ou seja, a Chave de API final, limpa e decodificada) para fazer chamadas de API em nome do usuário.

Com esse token, parece que você não precisa inserir o nome de usuário na chamada de API. Acredito que o seguinte cabeçalho seja suficiente quando incluído em suas chamadas GET, POST, PUT, etc.:

headers: {
"User-Api-Key": [o token]
}

E com isso, você espera ter um método de autenticação por usuário funcionando para interagir com o Discourse.

7 curtidas

Quais são as implicações de segurança de adicionar itens a allowed_user_api_auth_redirects? Tenho alguém pedindo para adicionar uma string para dar suporte à integração com o NextCloud.

1 curtida