Skip to content

Commit

Permalink
[module-options] Refactor module options to reuse constants from subm…
Browse files Browse the repository at this point in the history
…odules
  • Loading branch information
jonathansamines committed Jan 31, 2020
1 parent 5086d33 commit 6069abf
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 34 deletions.
64 changes: 39 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,48 @@ const AuthorizationCode = require('./lib/grants/authorization-code');
const PasswordOwner = require('./lib/grants/password-owner');
const ClientCredentials = require('./lib/grants/client-credentials');
const AccessToken = require('./lib/access-token');
const { authorizationMethodEnum, bodyFormatEnum, encodingModeEnum } = require('./lib/request-options');

// https://tools.ietf.org/html/draft-ietf-oauth-v2-31#appendix-A.1
const vsCharRegEx = /^[\x20-\x7E]*$/;

const optionsSchema = Joi
.object()
.keys({
client: Joi.object().keys({
id: Joi.string().pattern(vsCharRegEx).allow(''),
secret: Joi.string().pattern(vsCharRegEx).allow(''),
secretParamName: Joi.string().default('client_secret'),
idParamName: Joi.string().default('client_id'),
}).required(),
auth: Joi.object().keys({
tokenHost: Joi.string().required().uri({ scheme: ['http', 'https'] }),
tokenPath: Joi.string().default('/oauth/token'),
revokePath: Joi.string().default('/oauth/revoke'),
authorizeHost: Joi.string().uri({ scheme: ['http', 'https'] }).default(Joi.ref('tokenHost')),
authorizePath: Joi.string().default('/oauth/authorize'),
}).required(),
http: Joi.object().unknown(true),
options: Joi.object().keys({
scopeSeparator: Joi.string().default(' '),
credentialsEncodingMode: Joi.string().valid('strict', 'loose').default('strict'),
bodyFormat: Joi.string().valid('form', 'json').default('form'),
authorizationMethod: Joi.any().valid('header', 'body').default('header'),
}).default(),
});
const clientSchema = Joi.object().keys({
id: Joi.string().pattern(vsCharRegEx).allow(''),
secret: Joi.string().pattern(vsCharRegEx).allow(''),
secretParamName: Joi.string().default('client_secret'),
idParamName: Joi.string().default('client_id'),
}).required();

const authSchema = Joi.object().keys({
tokenHost: Joi.string().required().uri({ scheme: ['http', 'https'] }),
tokenPath: Joi.string().default('/oauth/token'),
revokePath: Joi.string().default('/oauth/revoke'),
authorizeHost: Joi.string().uri({ scheme: ['http', 'https'] }).default(Joi.ref('tokenHost')),
authorizePath: Joi.string().default('/oauth/authorize'),
}).required();

const optionsSchema = Joi.object().keys({
scopeSeparator: Joi.string().default(' '),
credentialsEncodingMode: Joi
.string()
.valid(...Object.values(encodingModeEnum))
.default(encodingModeEnum.STRICT),
bodyFormat: Joi
.string()
.valid(...Object.values(bodyFormatEnum))
.default(bodyFormatEnum.FORM),
authorizationMethod: Joi
.string()
.valid(...Object.values(authorizationMethodEnum))
.default(authorizationMethodEnum.HEADER),
}).default();

const moduleOptionsSchema = Joi.object().keys({
client: clientSchema,
auth: authSchema,
http: Joi.object().unknown(true),
options: optionsSchema,
});

module.exports = {

Expand All @@ -43,7 +57,7 @@ module.exports = {
* @returns {Object} The simple-oauth2 client
*/
create(opts = {}) {
const options = Joi.attempt(opts, optionsSchema, 'Invalid options provided to simple-oauth2');
const options = Joi.attempt(opts, moduleOptionsSchema, 'Invalid options provided to simple-oauth2');
const client = new Client(options);

return {
Expand Down
2 changes: 1 addition & 1 deletion lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const Hoek = require('@hapi/hoek');
const Wreck = require('@hapi/wreck');
const debug = require('debug')('simple-oauth2:client');
const RequestOptions = require('./request-options');
const { RequestOptions } = require('./request-options');

const defaultHttpHeaders = {
Accept: 'application/json',
Expand Down
15 changes: 13 additions & 2 deletions lib/encoding.js → lib/request-options/encoding.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

const HEADER_ENCODING_FORMAT = 'base64';

const encodingModeEnum = {
STRICT: 'strict',
LOOSE: 'loose',
};


/**
* Encode a single {value} using the application/x-www-form-urlencoded media type
* while also applying some additional rules specified by the spec
Expand All @@ -26,7 +32,7 @@ function getCredentialsString(clientID, clientSecret) {
return `${clientID}:${clientSecret}`;
}

module.exports = class Encoding {
class Encoding {
constructor(encodingMode) {
this.encodingMode = encodingMode;
}
Expand All @@ -40,12 +46,17 @@ module.exports = class Encoding {
getAuthorizationHeaderToken(clientID, clientSecret) {
let encodedCredentials;

if (this.encodingMode === 'strict') {
if (this.encodingMode === encodingModeEnum.STRICT) {
encodedCredentials = getCredentialsString(useFormURLEncode(clientID), useFormURLEncode(clientSecret));
} else {
encodedCredentials = getCredentialsString(clientID, clientSecret);
}

return Buffer.from(encodedCredentials).toString(HEADER_ENCODING_FORMAT);
}
}

module.exports = {
Encoding,
encodingModeEnum,
};
27 changes: 21 additions & 6 deletions lib/request-options.js → lib/request-options/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,28 @@
const Hoek = require('@hapi/hoek');
const querystring = require('querystring');
const debug = require('debug')('simple-oauth2:request-options');
const Encoding = require('./encoding');
const { Encoding, encodingModeEnum } = require('./encoding');

const JSON_CONTENT_TYPE = 'application/json';
const FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded';

const authorizationMethodEnum = {
HEADER: 'header',
BODY: 'body',
};

const bodyFormatEnum = {
FORM: 'form',
JSON: 'json',
};

function getDefaultRequestOptions() {
return {
headers: {},
};
}

module.exports = class RequestOptions {
class RequestOptions {
constructor(config, params) {
this.config = config;
this.requestOptions = this.createOptions(params);
Expand All @@ -24,7 +34,7 @@ module.exports = class RequestOptions {
const parameters = Object.assign({}, params);
const requestOptions = getDefaultRequestOptions();

if (this.config.options.authorizationMethod === 'header') {
if (this.config.options.authorizationMethod === authorizationMethodEnum.HEADER) {
const encoding = new Encoding(this.config.options.credentialsEncodingMode);
const credentials = encoding.getAuthorizationHeaderToken(this.config.client.id, this.config.client.secret);

Expand All @@ -38,16 +48,14 @@ module.exports = class RequestOptions {
parameters[this.config.client.secretParamName] = this.config.client.secret;
}

if (this.config.options.bodyFormat === 'form') {
if (this.config.options.bodyFormat === bodyFormatEnum.FORM) {
debug('Using form request format');

// An example using `form` authorization params in the body is the GitHub API
requestOptions.payload = querystring.stringify(parameters);
requestOptions.headers['Content-Type'] = FORM_CONTENT_TYPE;
} else {
debug('Using json request format');

// An example using `json` authorization params in the body is the Amazon Developer Publishing API
requestOptions.payload = parameters;
requestOptions.headers['Content-Type'] = JSON_CONTENT_TYPE;
}
Expand All @@ -58,4 +66,11 @@ module.exports = class RequestOptions {
toObject(requestOptions = {}) {
return Hoek.applyToDefaults(requestOptions, this.requestOptions);
}
}

module.exports = {
RequestOptions,
authorizationMethodEnum,
bodyFormatEnum,
encodingModeEnum,
};

0 comments on commit 6069abf

Please sign in to comment.