Skip to content

Commit

Permalink
feat(auth): implement api tokens
Browse files Browse the repository at this point in the history
Implement API authentication using Cloudflare's API Tokens, which use
RFC6750 bearer authentication instead of custom headers.

Signed-off-by: Terin Stock <terin@cloudflare.com>
  • Loading branch information
luisfavila authored and terinjokes committed Jul 23, 2019
1 parent 754e29e commit d28eeb4
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 9 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ Node.js v4 and greater are supported.

## Configuration

### API Keys
Set your account email address and API key. The API key can be found on
the [My Account][my-account] page in the Cloudflare dashboard.
the [My Profile -> API Tokens][api-tokens] page in the Cloudflare dashboard.

[my-account]: https://www.cloudflare.com/a/account
[api-tokens]: https://dash.cloudflare.com/profile/api-tokens

```javascript
var cf = require('cloudflare')({
Expand All @@ -45,6 +46,17 @@ var cf = require('cloudflare')({
});
```

### API Tokens (BETA)
Create your token on the [My Profile -> API Tokens][api-tokens] page in the Cloudflare dashboard.

[api-tokens]: https://dash.cloudflare.com/profile/api-tokens

```javascript
var cf = require('cloudflare')({
token: 'your Cloudflare API token'
});
```

## API Overview

Every resource is accessed via your `cf` instance:
Expand Down
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ const withEnvProxy = function withEnvProxy(opts) {
* @class Cloudflare
* @param {Object} auth - The API authentication for an account
* @param {string} auth.email - The account email address
* @param {string} auth.key - The account API token key
* @param {string} auth.key - The account API key
* @param {string} auth.token - The account API token
*
* @property {DNSRecords} dnsRecords - DNS Records instance
* @property {IPs} ips - IPs instance
Expand All @@ -75,6 +76,7 @@ const Cloudflare = auto(
const opts = {
email: auth && auth.email,
key: auth && auth.key,
token: auth && auth.token,
};

withEnvProxy(opts);
Expand Down
16 changes: 11 additions & 5 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@ module.exports = prototypal({
constructor: function constructor(options) {
this.email = options.email;
this.key = options.key;
this.token = options.token;
this.getter = new Getter(options);
},
request(requestMethod, requestPath, data, opts) {
const uri = `https://api.cloudflare.com/client/v4/${requestPath}`;
const key = opts.auth.key || this.key;
const token = opts.auth.token || this.token;
const email = opts.auth.email || this.email;

const options = {
json: opts.json !== false,
Expand All @@ -56,11 +60,13 @@ module.exports = prototypal({
},
};

if (isUserServiceKey(opts.auth.key || this.key)) {
options.headers['X-Auth-User-Service-Key'] = opts.auth.key || this.key;
} else {
options.headers['X-Auth-Key'] = opts.auth.key || this.key;
options.headers['X-Auth-Email'] = opts.auth.email || this.email;
if (isUserServiceKey(key)) {
options.headers['X-Auth-User-Service-Key'] = key;
} else if (key) {
options.headers['X-Auth-Key'] = key;
options.headers['X-Auth-Email'] = email;
} else if (token) {
options.headers.Authorization = `Bearer ${token}`;
}

if (requestMethod === 'GET') {
Expand Down
2 changes: 1 addition & 1 deletion lib/method.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

const URLPattern = require('url-pattern');

const options = ['key', 'email'];
const options = ['key', 'email', 'token'];

const isPlainObject = function isPlainObject(x) {
const prototype = Object.getPrototypeOf(x);
Expand Down
49 changes: 49 additions & 0 deletions test/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,55 @@ describe('HTTP Client', () => {
return res.then(resp => assert.deepEqual(resp, body));
});

it('should support API Tokens', () => {
const getter = new FakeGetter();
const token = 'vr29SNZEpswxdp';
const body = {
hello: 'world',
};

const options = {
json: true,
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
Accept: 'application/json',
},
query: {
name: 'world',
},
};

td.when(getter.got(), {ignoreExtraArgs: true}).thenReject();
td
.when(
getter.got(
'https://api.cloudflare.com/client/v4/example/42',
td.matchers.contains(options)
)
)
.thenResolve({body});

const subject = new Client({
token,
});

const res = subject.request(
'GET',
'example/42',
{
name: 'world',
},
{
auth: {},
headers: {},
}
);

return res.then(resp => assert.deepEqual(resp, body));
});

it('should override authentication', () => {
const getter = new FakeGetter();
const body = {
Expand Down

0 comments on commit d28eeb4

Please sign in to comment.