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

Fix scope encoding in token requests #187

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@ res.redirect(authorizationUri);
// Get the access token object (the authorization code is given from the previous step).
const tokenConfig = {
code: '<code>',
redirect_uri: 'http://localhost:3000/callback',
scope: '<scope>', // also can be an array of multiple scopes, ex. ['<scope1>, '<scope2>', '...']
redirect_uri: 'http://localhost:3000/callback'
};

// Save the access token
Expand Down
4 changes: 4 additions & 0 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ module.exports = (config) => {
headers: {},
};

if (Array.isArray(params.scope)) {
params.scope = params.scope.join(' ');
}

if (config.options.authorizationMethod === 'header') {
const basicHeader = encoding.getAuthorizationHeaderToken(
config.client.id,
Expand Down
3 changes: 2 additions & 1 deletion test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"rules": {
"max-len": "off",
"object-curly-newline": "off"
"object-curly-newline": "off",
"func-names": "off"
Copy link
Author

@cjlarose cjlarose Apr 22, 2018

Choose a reason for hiding this comment

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

I modified a few tests to write shared values to this and removed arrow function literals to do this.

The use of arrow function literals is explicitly discouraged by mocha, but I'm happy to switch the tests back to using block-scoped variables and arrow literals if preferred.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hi @cjlarose Sorry for the delay, please revert the mocha test changes related to the arrow functions usage. I am aware of what mocha suggests and open to changes, however in order to make your change easier to merge right now, I need you to restore those changes.

Copy link
Author

Choose a reason for hiding this comment

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

Will do! 👍

},
"env": {
"mocha": true
Expand Down
207 changes: 89 additions & 118 deletions test/auth-code.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
'use strict';

const nock = require('nock');
const qs = require('querystring');
const { expect } = require('chai');
const oauth2Module = require('./../index');
const baseConfig = require('./fixtures/module-config');
const expectedAccessToken = require('./fixtures/access_token');
const { stubTokenRequest } = require('./util');

describe('authorization code grant type', () => {
let scope;
let result;

describe('when computing an authorization url', () => {
const authorizeConfig = {
redirect_uri: 'http://localhost:3000/callback',
Expand Down Expand Up @@ -114,170 +111,128 @@ describe('authorization code grant type', () => {
describe('when requesting an access token', () => {
describe('with body credentials', () => {
describe('with format json', () => {
before(() => {
const scopeOptions = {
reqheaders: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
};

const expectedRequestParams = {
code: 'code',
redirect_uri: 'http://callback.com',
grant_type: 'authorization_code',
client_id: 'the client id',
client_secret: 'the client secret',
};

scope = nock('https://authorization-server.org', scopeOptions)
.post('/oauth/token', expectedRequestParams)
.reply(200, expectedAccessToken);
});

before(async () => {
before(function () {
const config = Object.assign({}, baseConfig, {
options: {
bodyFormat: 'json',
authorizationMethod: 'body',
},
});

const tokenParams = {
this.oauth2 = oauth2Module.create(config);
this.headers = {
Accept: 'application/json',
'Content-Type': 'application/json',
};
this.expectedRequestParams = {
code: 'code',
redirect_uri: 'http://callback.com',
grant_type: 'authorization_code',
client_id: 'the client id',
client_secret: 'the client secret',
};

const oauth2 = oauth2Module.create(config);
result = await oauth2.authorizationCode.getToken(tokenParams);
});

it('performs the http request', () => {
it('performs the http request', async function () {
const scope = stubTokenRequest({ headers: this.headers, requestBody: this.expectedRequestParams });
const tokenParams = { code: 'code', redirect_uri: 'http://callback.com' };
await this.oauth2.authorizationCode.getToken(tokenParams);
scope.done();
});

it('returns an access token as result of the token request', () => {
it('returns an access token as result of the token request', async function () {
stubTokenRequest({ headers: this.headers, requestBody: this.expectedRequestParams });
const tokenParams = { code: 'code', redirect_uri: 'http://callback.com' };
const result = await this.oauth2.authorizationCode.getToken(tokenParams);
expect(result).to.be.deep.equal(expectedAccessToken);
});
});

describe('with format form', () => {
before(() => {
const scopeOptions = {
reqheaders: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
};

const expectedRequestParams = {
code: 'code',
redirect_uri: 'http://callback.com',
grant_type: 'authorization_code',
client_id: 'the client id',
client_secret: 'the client secret',
};

scope = nock('https://authorization-server.org', scopeOptions)
.post('/oauth/token', qs.stringify(expectedRequestParams))
.reply(200, expectedAccessToken);
});

before(async () => {
before(function () {
const config = Object.assign({}, baseConfig, {
options: {
bodyFormat: 'form',
authorizationMethod: 'body',
},
});

const tokenParams = {
this.oauth2 = oauth2Module.create(config);
this.headers = {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
};
this.expectedRequestParams = {
code: 'code',
redirect_uri: 'http://callback.com',
grant_type: 'authorization_code',
client_id: 'the client id',
client_secret: 'the client secret',
};

const oauth2 = oauth2Module.create(config);
result = await oauth2.authorizationCode.getToken(tokenParams);
});

it('performs the http request', () => {
it('performs the http request', async function () {
const scope = stubTokenRequest({
headers: this.headers,
requestBody: qs.stringify(this.expectedRequestParams),
});
const tokenParams = { code: 'code', redirect_uri: 'http://callback.com' };
await this.oauth2.authorizationCode.getToken(tokenParams);
scope.done();
});

it('returns an access token as result of the token request', () => {
it('returns an access token as result of the token request', async function () {
stubTokenRequest({
headers: this.headers,
requestBody: qs.stringify(this.expectedRequestParams),
});
const tokenParams = { code: 'code', redirect_uri: 'http://callback.com' };
const result = await this.oauth2.authorizationCode.getToken(tokenParams);
expect(result).to.be.deep.equal(expectedAccessToken);
});
});
});

describe('with header credentials', () => {
before(() => {
const scopeOptions = {
reqheaders: {
Accept: 'application/json',
Authorization: 'Basic dGhlK2NsaWVudCtpZDp0aGUrY2xpZW50K3NlY3JldA==',
},
};

const expectedRequestParams = {
code: 'code',
redirect_uri: 'http://callback.com',
grant_type: 'authorization_code',
};

scope = nock('https://authorization-server.org', scopeOptions)
.post('/oauth/token', expectedRequestParams)
.reply(200, expectedAccessToken);
});

before(async () => {
before(function () {
const config = Object.assign({}, baseConfig, {
options: {
authorizationMethod: 'header',
},
});

const tokenParams = {
this.oauth2 = oauth2Module.create(config);
this.headers = {
Accept: 'application/json',
Authorization: 'Basic dGhlK2NsaWVudCtpZDp0aGUrY2xpZW50K3NlY3JldA==',
};
this.expectedRequestParams = {
code: 'code',
redirect_uri: 'http://callback.com',
grant_type: 'authorization_code',
};

const oauth2 = oauth2Module.create(config);
result = await oauth2.authorizationCode.getToken(tokenParams);
});

it('performs the http request', () => {
it('performs the http request', async function () {
const scope = stubTokenRequest({
headers: this.headers,
requestBody: this.expectedRequestParams,
});
const tokenParams = { code: 'code', redirect_uri: 'http://callback.com' };
await this.oauth2.authorizationCode.getToken(tokenParams);
scope.done();
});

it('resolves the access token', () => {
it('resolves the access token', async function () {
stubTokenRequest({
headers: this.headers,
requestBody: this.expectedRequestParams,
});
const tokenParams = { code: 'code', redirect_uri: 'http://callback.com' };
const result = await this.oauth2.authorizationCode.getToken(tokenParams);
expect(result).to.be.deep.equal(expectedAccessToken);
});
});

describe('with additional http configuration', () => {
before(() => {
const scopeOptions = {
reqheaders: {
Accept: 'application/json',
Authorization: 'Basic dGhlK2NsaWVudCtpZDp0aGUrY2xpZW50K3NlY3JldA==',
'X-MYTHICAL-HEADER': 'mythical value',
'USER-AGENT': 'hello agent',
},
};

const expectedRequestParams = {
code: 'code',
redirect_uri: 'http://callback.com',
grant_type: 'authorization_code',
};

scope = nock('https://authorization-server.org', scopeOptions)
.post('/oauth/token', expectedRequestParams)
.reply(200, expectedAccessToken);
});

before(async () => {
before(function () {
const config = Object.assign({}, baseConfig, {
http: {
headers: {
Expand All @@ -286,21 +241,37 @@ describe('authorization code grant type', () => {
},
},
});

const tokenParams = {
this.oauth2 = oauth2Module.create(config);
this.headers = {
Accept: 'application/json',
Authorization: 'Basic dGhlK2NsaWVudCtpZDp0aGUrY2xpZW50K3NlY3JldA==',
'X-MYTHICAL-HEADER': 'mythical value',
'USER-AGENT': 'hello agent',
};
this.expectedRequestParams = {
code: 'code',
redirect_uri: 'http://callback.com',
grant_type: 'authorization_code',
};

const oauth2 = oauth2Module.create(config);
result = await oauth2.authorizationCode.getToken(tokenParams);
});

it('performs the http request with custom headers', () => {
it('performs the http request with custom headers', async function () {
const scope = stubTokenRequest({
headers: this.headers,
requestBody: this.expectedRequestParams,
});
const tokenParams = { code: 'code', redirect_uri: 'http://callback.com' };
await this.oauth2.authorizationCode.getToken(tokenParams);
scope.done();
});

it('resolves the access token', () => {
it('resolves the access token', async function () {
stubTokenRequest({
headers: this.headers,
requestBody: this.expectedRequestParams,
});
const tokenParams = { code: 'code', redirect_uri: 'http://callback.com' };
const result = await this.oauth2.authorizationCode.getToken(tokenParams);
expect(result).to.be.deep.equal(expectedAccessToken);
});
});
Expand Down
Loading