Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
internals.encode = function (value, options) {
// Encodings: 'base64json', 'base64', 'form', 'iron', 'none'
if (value === undefined ||
options.encoding === 'none') {
return value;
}
if (options.encoding === 'iron') {
return Iron.seal(value, options.password, options.iron || Iron.defaults);
}
if (options.encoding === 'base64') {
return Buffer.from(value, 'binary').toString('base64');
}
if (options.encoding === 'base64json') {
const stringified = JSON.stringify(value);
return Buffer.from(stringified, 'binary').toString('base64');
}
// encoding: 'form'
return Querystring.stringify(value);
};
internals.decode = async function (value, definition) {
if (!value &&
definition.encoding === 'form') {
return {};
}
Hoek.assert(typeof value === 'string', 'Invalid string');
// Encodings: 'base64json', 'base64', 'form', 'iron', 'none'
if (definition.encoding === 'iron') {
return await Iron.unseal(value, definition.password, definition.iron || Iron.defaults);
}
if (definition.encoding === 'base64json') {
const decoded = Buffer.from(value, 'base64').toString('binary');
try {
return Bourne.parse(decoded);
}
catch (err) {
throw Boom.badRequest('Invalid JSON payload');
}
}
if (definition.encoding === 'base64') {
return Buffer.from(value, 'base64').toString('binary');
}
it('protects an endpoint (token with iron settings)', async () => {
const server = Hapi.server();
server.auth.scheme('custom', internals.implementation);
server.auth.strategy('default', 'custom');
server.auth.default('default');
await server.register({ plugin: Nes, options: { auth: { type: 'token', password, iron: Iron.defaults } } });
server.route({
method: 'GET',
path: '/',
handler: () => 'hello'
});
await server.start();
const res = await server.inject({ url: '/nes/auth', headers: { authorization: 'Custom john' } });
expect(res.result.status).to.equal('authenticated');
expect(res.result.token).to.exist();
const client = new Nes.Client('http://localhost:' + server.info.port);
await client.connect({ auth: res.result.token });
const { payload, statusCode } = await client.request('/');
expect(payload).to.equal('hello');
it('parses cookie (signed form integrity settings)', async () => {
const definitions = new Statehood.Definitions();
definitions.add('sid', { encoding: 'form', sign: { password, integrity: Iron.defaults.integrity } });
const { states, failed } = await definitions.parse('sid=a=1&b=2&c=3%20x.2d75635d74c1a987f84f3ee7f3113b9a2ff71f89d6692b1089f19d5d11d140f8*anm-37hjjRC3eY7Mcv4gP7gXgXBKTtUz9fNFWnetEZo');
expect(failed).to.have.length(0);
expect(states.sid).to.equal({ a: '1', b: '2', c: '3 x' });
expect(states).to.equal({ sid: { a: '1', b: '2', c: '3 x' } });
});
it('parses cookie (iron settings)', async () => {
const definitions = new Statehood.Definitions();
definitions.add('key', { encoding: 'iron', password, iron: Iron.defaults });
const { states, failed } = await definitions.parse('key=Fe26.2**8ec29d2e64ab19a0429faab76c46167c933b7c2c94dac8022bb4c97de0fc359d*O2aDw2nk5Svfc4xiuatycw*DWWOPpI3-B6Bb4oOOuNxGT8v9S4jZ_hpQZaaeYREvuk**34d98c193fd2048b40655966115d75dae62aab96cd1f5b374908b86fc47a61d3*H_zsHSt6UoOj3QgBIuNMrNHAUosM6Sp51uLKak0ZUjg');
expect(failed).to.have.length(0);
expect(states).to.equal({ key: { a: 1, b: 2, c: 3 } });
});
if (!sig) {
throw Boom.badRequest('Missing signature');
}
const sigParts = sig.split('*');
if (sigParts.length !== 2) {
throw Boom.badRequest('Invalid signature format');
}
const hmacSalt = sigParts[0];
const hmac = sigParts[1];
const macOptions = Hoek.clone(definition.sign.integrity || Iron.defaults.integrity);
macOptions.salt = hmacSalt;
const mac = await Iron.hmacWithPassword(definition.sign.password, macOptions, [internals.macPrefix, name, unsigned].join('\n'));
if (!Cryptiles.fixedTimeComparison(mac.digest, hmac)) {
throw Boom.badRequest('Invalid hmac value');
}
return unsigned;
};
static async signup(name, email, remoteAuth) {
const user = await User.objects.create({
name,
email
});
if (remoteAuth) {
await Authentication.objects.create({
user,
remote_identity: remoteAuth.id,
provider: remoteAuth.provider,
access_token_enc: await iron.seal(
remoteAuth.token,
process.env.OAUTH_PASSWORD,
iron.defaults
),
metadata: {}
});
}
const host = await Host.objects.get({ id: 1 });
const namespace = await Namespace.objects.create({ name, host });
await NamespaceMember.objects.create({
accepted: true,
namespace,
user
});
return user;
}
};
request.auth.headers['x-forwarded-for'] = this.info['x-forwarded-for'];
const res = await this.server.inject({ url: route.path, method: 'auth', headers: request.auth.headers, remoteAddress: this.info.remoteAddress, allowInternals: true, validate: false });
if (res.statusCode !== 200) {
throw Boom.unauthorized(res.result.message);
}
if (!res.result.credentials) {
return;
}
this._setCredentials(res.result);
return;
}
try {
const auth = await Iron.unseal(request.auth, config.password, config.iron || Iron.defaults);
this._setCredentials(auth);
}
catch (err) {
throw Boom.unauthorized('Invalid token');
}
}
};
internals.sign = async function (name, value, options) {
if (value === undefined ||
!options) {
return value;
}
const mac = await Iron.hmacWithPassword(options.password, options.integrity || Iron.defaults.integrity, [internals.macPrefix, name, value].join('\n'));
const signed = value + '.' + mac.salt + '*' + mac.digest;
return signed;
};
credentials: request.auth.credentials,
artifacts: request.auth.artifacts,
strategy: request.auth.strategy
};
if (config.type === 'direct') {
return credentials;
}
const result = { status: 'authenticated' };
if (config.type === 'cookie') {
return h.response(result).state(config.cookie, credentials);
}
const sealed = await Iron.seal(credentials, config.password, config.iron || Iron.defaults);
result.token = sealed;
return result;
}
}