diff --git a/README.md b/README.md index 2cd4ed19..4a013dab 100644 --- a/README.md +++ b/README.md @@ -526,6 +526,7 @@ callback | `[provider]` | final callback route on your server to receive the [re dynamic | `[provider]` | allow [dynamic override](#dynamic-override) of configuration overrides | `[provider]` | [static overrides](#static-overrides) for a provider response | `[provider]` | [limit](#limit-response-data) the response data +token_endpoint_auth_method | `[provider]` | Authentication method for the token endpoint from [RFC 7591](https://tools.ietf.org/html/rfc7591#section-2) name | generated | provider's [name](#grant), used to generate `redirect_uri` [provider] | generated | provider's [name](#grant) as key redirect_uri | generated | OAuth app [redirect URI](#redirect-uri), generated using `protocol`, `host`, `path` and `name` diff --git a/config/reserved.json b/config/reserved.json index 51953c0f..242aa2e9 100644 --- a/config/reserved.json +++ b/config/reserved.json @@ -5,6 +5,7 @@ "oauth", "scope_delimiter", "custom_parameters", + "token_endpoint_auth_method", "protocol", "host", diff --git a/lib/config.js b/lib/config.js index dd27ce91..4d4ac180 100644 --- a/lib/config.js +++ b/lib/config.js @@ -137,6 +137,15 @@ var format = { return Object.keys(overrides).length ? overrides : undefined }, + // https://tools.ietf.org/html/rfc7591#section-2 + token_endpoint_auth_method: ({oauth, token_endpoint_auth_method}) => { + // There is no `none` method since it's used only with public clients + var defaults = ['client_secret_post', 'client_secret_basic'] + + return oauth === 2 + ? defaults.includes(token_endpoint_auth_method) ? token_endpoint_auth_method : defaults[0] + : undefined + } } var state = (provider, key = 'state', value = provider[key]) => diff --git a/lib/flow/oauth2.js b/lib/flow/oauth2.js index db56a644..ee60da23 100644 --- a/lib/flow/oauth2.js +++ b/lib/flow/oauth2.js @@ -78,7 +78,9 @@ exports.access = (provider, authorize, session) => new Promise((resolve, reject) client_secret: provider.secret } } - if (/ebay|fitbit2|homeaway|hootsuite|reddit/.test(provider.name)) { + if (/ebay|fitbit2|homeaway|hootsuite|reddit/.test(provider.name) + || provider.token_endpoint_auth_method === 'client_secret_basic' + ) { delete options.form.client_id delete options.form.client_secret options.auth = {user: provider.key, pass: provider.secret} diff --git a/test/config.js b/test/config.js index 7a7bf15d..79c3f03e 100644 --- a/test/config.js +++ b/test/config.js @@ -82,6 +82,15 @@ describe('config', () => { t.equal(config.format.secret({oauth: 3, secret: 'secret'}), undefined) t.equal(config.format.secret({}), undefined) }) + it('token_endpoint_auth_method', () => { + t.equal(config.format.token_endpoint_auth_method({}), undefined) + t.equal(config.format.token_endpoint_auth_method({oauth: undefined}), undefined) + t.equal(config.format.token_endpoint_auth_method({oauth: 1}), undefined) + t.equal(config.format.token_endpoint_auth_method({oauth: 2}), 'client_secret_post') + t.equal(config.format.token_endpoint_auth_method({oauth: 2, token_endpoint_auth_method: 'foo'}), 'client_secret_post') + t.equal(config.format.token_endpoint_auth_method({oauth: 2, token_endpoint_auth_method: 'client_secret_basic'}), 'client_secret_basic') + t.equal(config.format.token_endpoint_auth_method({oauth: 2, token_endpoint_auth_method: 'client_secret_post'}), 'client_secret_post') + }) it('scope', () => { t.equal(config.format.scope({scope: []}), undefined) t.equal(config.format.scope({scope: ['']}), undefined) @@ -243,6 +252,7 @@ describe('config', () => { { protocol: 'http', host: 'localhost:3000', + token_endpoint_auth_method: 'client_secret_post', oauth: 2, client_id: 'key', client_secret: 'secret', @@ -255,6 +265,7 @@ describe('config', () => { sub: { protocol: 'http', host: 'localhost:3000', + token_endpoint_auth_method: 'client_secret_post', oauth: 2, client_id: 'key', client_secret: 'secret', @@ -285,6 +296,7 @@ describe('config', () => { facebook: { authorize_url: 'https://www.facebook.com/dialog/oauth', access_url: 'https://graph.facebook.com/oauth/access_token', + token_endpoint_auth_method: 'client_secret_post', oauth: 2, protocol: 'http', host: 'localhost:3000', @@ -307,6 +319,7 @@ describe('config', () => { facebook: { authorize_url: 'https://www.facebook.com/dialog/oauth', access_url: 'https://graph.facebook.com/oauth/access_token', + token_endpoint_auth_method: 'client_secret_post', oauth: 2, protocol: 'http', host: 'localhost:3000', @@ -344,6 +357,7 @@ describe('config', () => { config.provider(options, session), { authorize_url: 'https://www.facebook.com/dialog/oauth', access_url: 'https://graph.facebook.com/oauth/access_token', + token_endpoint_auth_method: 'client_secret_post', oauth: 2, dynamic: true, name: 'facebook',