Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduces per-user API tokens via 'apikey: true' #30

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

mattoberle
Copy link
Contributor

@mattoberle mattoberle commented Oct 6, 2020

This commit introduces a rudimentary approach to assigning API keys on
a per-user basis (rather than one global plain-text environment
variable).

Generating an API Key

When making a POST request to the /_users/ endpoint the inclusion
of the following in the request body will generate an API key for the
user:

POST /_users/

{
  "apikey": true,
  "auth": "admin",
  "provider": "google",
  "username": "[email protected]"
}

The response will look like this. Note: This is the only opportunity
to retrieve the generated key. Keys are hashed in the database and
the hashed values are excluded from the GET API.

{
  "_ref": "/_users/dGVzdEBleGFtcGxlLmNvbUBnb29nbGU=",
  "apikey": "dGVzdEBleGFtcGxlLmNvbUBnb29nbGU=:Z8EVBDN-M1T4EED-NH93XNJ-8J1374R",
  "auth": "admin",
  "provider": "google",
  "username": "[email protected]"
}

Revoking an API Key

To remove a user's key POST again with apikey: false:

POST /_users/

{
  "apikey": false,
  "auth": "admin",
  "provider": "google",
  "username": "[email protected]"
}

The response will look like this.

{
  "_ref": "/_users/dGVzdEBleGFtcGxlLmNvbUBnb29nbGU=",
  "apikey": null,
  "auth": "admin",
  "provider": "google",
  "username": "[email protected]"
}

In addition to per-user API keys, this commit introduces a new
environment variable CLAY_DISABLE_GLOBAL_ACCESS_KEY. When set to a
"true" value (t, true, 1) only user-level keys will work.

Edit: I replaced the CLAY_DISABLE_GLOBAL_ACCESS_KEY idea with a simpler method.

Set CLAY_ACCESS_KEY to a value to enable the global access key.
Unset CLAY_ACCESS_KEY to disable it.

This provides a "single-user-mode"-esq toggle for emergency recovery,
allows Clay sites to run in a secure mode, but maintains backward
compatibility for old API environment-based API keys where required or
where convenient.

This commit introduces a rudimentary approach to assigning API keys on
a per-user basis (rather than one global plain-text environment
variable).

**Generating an API Key**

When making a `POST` request to the `/_users/` endpoint the inclusion
of the following in the request body will generate an API key for the
user:

```
POST /_users/

{
  "apikey": true,
  "auth": "admin",
  "provider": "google",
  "username": "[email protected]"
}
```

The response will look like this. _Note: This is the only opportunity
to retrieve the generated key. Keys are hashed in the database._

```js
{
  "_ref": "/_users/dGVzdEBleGFtcGxlLmNvbUBnb29nbGU=",
  "apikey": "dGVzdEBleGFtcGxlLmNvbUBnb29nbGU=:Z8EVBDN-M1T4EED-NH93XNJ-8J1374R",
  "auth": "admin",
  "provider": "google",
  "username": "[email protected]"
}
```

**Revoking an API Key**

To remove a user's key `POST` again with `apikey: false`:

```
POST /_users/

{
  "apikey": false,
  "auth": "admin",
  "provider": "google",
  "username": "[email protected]"
}
```

The response will look like this.

```js
{
  "_ref": "/_users/dGVzdEBleGFtcGxlLmNvbUBnb29nbGU=",
  "apikey": null,
  "auth": "admin",
  "provider": "google",
  "username": "[email protected]"
}
```

---

In addition to per-user API keys, this commit introduces a new
environment variable `CLAY_DISABLE_GLOBAL_ACCESS_KEY`. When set to a
"true" value (`t`, `true`, `1`) _only_ user-level keys will work.

This provides a "single-user-mode"-esq toggle for emergency recovery,
allows Clay sites to run in a secure mode, but maintains backward
compatibility for old API environment-based API keys where required or
where convenient.
@mattoberle mattoberle added the enhancement New feature or request label Oct 6, 2020
Rather than having a variable to disable the global access key, this
commit disables the global API key when `CLAY_ACCESS_KEY` is set to an
empty value (or not set at all).
});

afterAll(() => {
process.env.CLAY_ACCESS_KEY = OLD_ACCESS_KEY;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure i understand what OLD_ACCESS_KEY is doing here, is it insufficient to just set process.env.CLAY_ACCESES_KEY as in L24 above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really just a pattern to preserve the original values of the environment before the suite was executed.

I think ideally we'd mock out the whole process.env for tests for reproducibility, but chose to stick close to what we had for this PR.

* @returns {boolean}
*/
function isValidAPIKey(user, apikey) {
return bcrypt.compareSync(apikey, user.apikey);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to go with bcrypt.compare() to keep from blocking other requests. I'm not sure why were doing hashSync().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good call, I'll dig into using the async methods instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants