# User API keys specification

**URL:** https://meta.discourse.org/t/user-api-keys-specification/48536
**Category:** Integrations
**Tags:** rest-api, reference
**Created:** [August 12, 2016, 2:11am UTC](https://meta.discourse.org/t/user-api-keys-specification/48536 "2016-08-12T02:11:53Z")
**Posts on this page:** 20
**Page:** 2

<div class="post-metadata">

### Author: ![dickmao](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dickmao/32/167485_2.png) [@dickmao](https://meta.discourse.org/u/dickmao)
#### Post date: [August 31, 2019, 1:02pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/26 "2019-08-31T13:02:43Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![tdekoekkoek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/tdekoekkoek/32/132035_2.png) [@tdekoekkoek](https://meta.discourse.org/u/tdekoekkoek)
#### Post date: [September 16, 2019, 6:15pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/27 "2019-09-16T18:15:39Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![pmusaraj](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pmusaraj/32/119489_2.png) [@pmusaraj](https://meta.discourse.org/u/pmusaraj)
#### Post date: [September 17, 2019, 5:52pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/28 "2019-09-17T17:52:23Z")

</div>

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

---

<div class="post-metadata">

### Author: ![heluecht](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/heluecht/32/161680_2.png) [@heluecht](https://meta.discourse.org/u/heluecht)
#### Post date: [November 20, 2019, 12:43pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/29 "2019-11-20T12:43:13Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [November 20, 2019, 8:09pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/30 "2019-11-20T20:09:16Z")

</div>

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

---

<div class="post-metadata">

### Author: ![heluecht](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/heluecht/32/161680_2.png) [@heluecht](https://meta.discourse.org/u/heluecht)
#### Post date: [November 20, 2019, 10:48pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/31 "2019-11-20T22:48:26Z")

</div>

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?

---

<div class="post-metadata">

### Author: ![zeroarst](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/zeroarst/32/264224_2.png) [@zeroarst](https://meta.discourse.org/u/zeroarst)
#### Post date: [January 23, 2020, 9:06am UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/32 "2020-01-23T09:06:51Z")

</div>

> [@sam](#):
>
> **push\_url** : url to push notifications to (required and valid only if `push` or `notifications` are included in the scopes)

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.

---

<div class="post-metadata">

### Author: ![dickmao](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/dickmao/32/167485_2.png) [@dickmao](https://meta.discourse.org/u/dickmao)
#### Post date: [January 25, 2020, 3:17am UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/33 "2020-01-25T03:17:54Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [March 19, 2020, 9:05pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/34 "2020-03-19T21:05:52Z")

</div>

@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](https://meta.discourse.org/t/improved-bookmarks-with-reminders/144542) in Google Calendar using User API keys.

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [March 19, 2020, 10:03pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/35 "2020-03-19T22:03:23Z")

</div>

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](https://github.com/discourse/discourse/blob/067696df8f95e81b818cfcb4bcfe90c09ba68b45/app/models/user_api_key.rb#L5-L19))

```diff
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…)

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [March 20, 2020, 1:47am UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/36 "2020-03-20T01:47:25Z")

</div>

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.

> [@david](#):
>
> (Aside: why are we using nested arrays here…)

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

---

<div class="post-metadata">

### Author: ![fzngagan](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/fzngagan/32/259349_2.png) [@fzngagan](https://meta.discourse.org/u/fzngagan)
#### Post date: [April 24, 2020, 11:19am UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/37 "2020-04-24T11:19:06Z")

</div>

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?

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [April 24, 2020, 10:46pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/38 "2020-04-24T22:46:38Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![Vandana\_Gangisetty](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/vandana_gangisetty/32/181583_2.png) [@Vandana\_Gangisetty](https://meta.discourse.org/u/Vandana_Gangisetty)
#### Post date: [May 24, 2020, 2:17pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/39 "2020-05-24T14:17:04Z")

</div>

Hi..Are you able to resolve this issue? I have the same issue and not able to fix it. I tried passing different type of keys and nothing worked. Any help would be greatly appreciated.

---

<div class="post-metadata">

### Author: ![cookieman768](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/cookieman768/32/118511_2.png) [@cookieman768](https://meta.discourse.org/u/cookieman768)
#### Post date: [September 22, 2020, 2:25pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/40 "2020-09-22T14:25:53Z")

</div>

Are there any libraries for this? If not, an example implementation? I’m trying to use PHP to identify a user’s Discourse account on a separate portion of the website. This seems like a modified OAuth flow but I’m a bit confused on how to implement this.

Specifically, I’m not sure how to do the whole public/private key generation.

Is there a way to just do OAuth 2 with Discourse as an OAuth provider?

---

<div class="post-metadata">

### Author: ![Morten\_Bendtsen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/morten_bendtsen/32/199774_2.png) [@Morten\_Bendtsen](https://meta.discourse.org/u/Morten_Bendtsen)
#### Post date: [November 25, 2020, 2:14pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/41 "2020-11-25T14:14:39Z")

</div>

Did you succeed in this using the User-Api-Key? I’m also getting a `You are not permitted to view the requested resource`

---

<div class="post-metadata">

### Author: ![Morten\_Bendtsen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/morten_bendtsen/32/199774_2.png) [@Morten\_Bendtsen](https://meta.discourse.org/u/Morten_Bendtsen)
#### Post date: [January 6, 2021, 8:31am UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/42 "2021-01-06T08:31:43Z")

</div>

I figured out what I did wrong: The returned payload is not the UserAPI key itself, but an encrypted JSON string, that would need to be decrypted with the private key of the private/public keypair.

---

<div class="post-metadata">

### Author: ![JQ331](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@JQ331](https://meta.discourse.org/u/JQ331)
#### Post date: [July 28, 2021, 2:41pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/43 "2021-07-28T14:41:39Z")

</div>

EDIT: I’ve been able to get most of it to work, and will provide a description once I’ve got it fully working.

* * *

How does the client get the private/public key pair and id?

> [@sam](#):
>
> Client (desktop app, browser plugin, mobile app) generates a private/public key pair and return url

> [@Alan\_Murphy](#):
>
> I basically require a description of paramaters PUBLIC\_KEY,NONCE,CLIENTID used in the sample request below and where I can get them

> [@Morten\_Bendtsen](#):
>
> I figured out what I did wrong: The returned payload is not the UserAPI key itself, but an encrypted JSON string, that would need to be decrypted with the private key of the private/public keypair.

Are you able to provide code for getting the user api key with a javascript app? (A javascript app trying to allow a user to make API calls to a discourse forum).

I am getting 403 errors. Or, an error saying: `Sorry, we are unable to issue user API keys, this feature may be disabled by the site admin` (even though my site has checked: `Allow generation of user API keys`).

I think the issue could be how to generate the private/public key pair (how is that done?), and then handling the redirect.

Any code is appreciated.

---

<div class="post-metadata">

### Author: ![JQ331](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@JQ331](https://meta.discourse.org/u/JQ331)
#### Post date: [September 2, 2021, 4:19pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/45 "2021-09-02T16:19:19Z")

</div>

I have gotten this to work, after some trial and error.

Here are the basic steps that I follow when: I have a separate app that I have coded, and I want users to be able to use that app to make API calls to a discourse site.

To do that, I need to generate a per-user API token to make calls on behalf of each specific user (at least in a nodejs/javascript environment).

* * *

Note that for the javascript side of things, I found the code that @KengoTODA provided here: [discourse-api-key-generator/src/index.ts at main · KengoTODA/discourse-api-key-generator · GitHub](https://github.com/KengoTODA/discourse-api-key-generator/blob/main/src/index.ts) to be very helpful.

* * *

Here are the steps I’ve followed:

**First: Generate a public and private key pair.**

This is something your app needs to generate–a public key and a private key. The github gist provides one method to do this.

**Second: Have a redirect URL.**

This is the URL discourse will redirect to, providing the final API token in the payload. If you have a desktop app (ie, doesn’t have a browser URL), the redirect URL will be based on a custom protocol you set up that opens the app when the redirect URL is entered in the browser.

Note that the redirect url needs to be whitelisted on the site settings of the target discourse site.

The discourse site also likely needs to have the site setting checked for “Allow user api keys”. See the original post on this topic for “Site Settings”.

**Third: Send the API request call to the discourse request url.**

So your app will send a call to a url that follows this form:

https://[your target discourse site .com]/user-api-key-new"

and adding as parameters:

- your app name
- you “client\_id” (I was able to use hostname(), from `const {hostname} = require('os')` for a desktop app, just like in the github gist referenced above)
- scopes (this is the scopes that you want the user to be able to do through the api, like “write”, “read”, etc)
- your public key (from step 1 above)
- your redirect url (from step 2 above)
- nonce (this is a value you can choose–like just using ‘1’ seems to work)

**Fourth: User authorizes your app on the discourse site page that is opened by the request url**

When you send the request url successfully, it opens a page on the discourse site telling the user your app wants to access the site.

On that page, there is a button for the user to allow this. When the user clicks this button, the discourse site redirects to the redirect url you provided, and attaches as a param a `?payload=[the API KEY]`. The API KEY here is the key you need to decode in your app.

**Fifth: Your app picks up the redirect url value (with payload value), and you decode the API KEY**

You’re almost there. Your app needs to parse the redirect url that discourse went to, and get the API Key contained in the payload.

Once you have that API Key, you need to do two things:

1. Get the actual key, not the URL encoded version: if you are getting a param from a url, it is often url encoded (adding % here and there, etc.). You need to clean it up. In javascript, I have found `decodeURIComponent` to work for this.
2. Once you have the cleaned up API KEY returned from discourse, you need to decode it. To do this, you can use javascript decoding with private keys. Basically, you use your private key (generated in the first step above), and to decode the cleaned up API KEY. There is some example javascript in the github gist I referenced above: [discourse-api-key-generator/src/index.ts at main · KengoTODA/discourse-api-key-generator · GitHub](https://github.com/KengoTODA/discourse-api-key-generator/blob/main/src/index.ts)

After you run your decoding code, you have the token itself, which you can now use to make authenticated API calls on behalf of the user.

**Sixth: Use the token (ie, the final, cleaned up, decoded API Key) to make api calls on behalf of the user**

With that token, it appears you don’t need to enter the user name in the api call. I find the following header to be sufficient when you include it in your GET, POST, PUT, etc call:

```plaintext
headers: {
"User-Api-Key": [the token]
}

```

And with that, you hopefully have a working per-user authentication method to interact with discourse.

---

<div class="post-metadata">

### Author: ![mattdm](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/mattdm/32/216484_2.png) [@mattdm](https://meta.discourse.org/u/mattdm)
#### Post date: [June 13, 2022, 6:57pm UTC](https://meta.discourse.org/t/user-api-keys-specification/48536/46 "2022-06-13T18:57:15Z")

</div>

What are the security implications of adding things to `allowed_user_api_auth_redirects`? I have someone asking to add a string in order to support NextCloud integration.

[Previous page](https://meta.discourse.org/t/user-api-keys-specification/48536.md?page=1)

[Next page](https://meta.discourse.org/t/user-api-keys-specification/48536.md?page=3)
