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

add to work with async await module #7

Closed
wants to merge 7 commits into from
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
40 changes: 16 additions & 24 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
{
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"double"
],
"semi": [
"error",
"always"
]
}
}
"env": {
"es6": true,
"node": true,
"mocha": true
},
"extends": "eslint:recommended",
"rules": {
"indent": ["error", "tab"],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"]
},
"parserOptions": {
"ecmaVersion": 8
}
}
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: node_js
node_js:
- "4"
- "node"
- '10'
- 'node'
install:
- npm install
script:
Expand Down
10 changes: 2 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
var EasyNoPassword = require("./lib/token_handler");
var EasyStrategy = require("./lib/strategy");
let enp = require('./lib/handler')

function construct(secret, maxTokenAge) {
return new EasyNoPassword(secret, maxTokenAge);
}
construct.Strategy = EasyStrategy;

module.exports = construct;
module.exports = enp
58 changes: 32 additions & 26 deletions lib/base62.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,52 @@ const UINT32_CAPACITY = new UInt64(0, 1);

/**
* Encodes an 8-byte buffer as base 62.
* @param {Buffer} buffer - buffer must be length 8
*/
function encode(buf) {
if (buf.length != 8) {
throw new TypeError("Buffer must be length 8");
const encode = (buffer) => {
if (buffer.length != 8) {
throw new TypeError("buffer must be length 8");
}

var num = new UInt64(buf.readUInt32BE(4), buf.readUInt32BE(0));
var str = "";
let number = new UInt64(buffer.readUInt32BE(4), buffer.readUInt32BE(0));

let encoded = "";
do {
num.div(SIXTY_TWO);
str = CHARS[num.remainder] + str;
} while (num.greaterThan(ZERO));
number.div(SIXTY_TWO);
encoded = CHARS[number.remainder] + encoded;
} while (number.greaterThan(ZERO));

return str;
}
return encoded;
};

/*
/**
* Decodes a base 62 string into an 8-byte buffer.
* @param {String} string - Number encoded must be length 12 digits al least
*/
function decode(str) {
const decode = (string) => {
// FIXME: Check for multiplication overflow
if (str.length > 12) {
if (string.length > 12) {
throw new TypeError("Number encoded is too large");
}

var num = new UInt64();
for (let i=0; i<str.length; i++) {
let n = CHARS.indexOf(str[i]);
let numbers = new UInt64();
for (let i = 0; i < string.length; i++) {
let n = CHARS.indexOf(string[i]);
if (n === -1) {
throw new TypeError("Unknown character in string");
}
num.multiply(SIXTY_TWO);
num.add(new UInt64(n));
numbers.multiply(SIXTY_TWO);
numbers.add(new UInt64(n));
}

var buf = new Buffer(8);
num.div(UINT32_CAPACITY);
buf.writeUInt32BE(parseInt(num.toString()), 0);
buf.writeUInt32BE(num.remainder, 4);
return buf;
}

module.exports = { encode, decode };
let buffer = new Buffer.alloc(8);
numbers.div(UINT32_CAPACITY);
buffer.writeUInt32BE(parseInt(numbers.toString()), 0);
buffer.writeUInt32BE(numbers.remainder, 4);
return buffer;
};

module.exports = {
encode,
decode,
};
145 changes: 145 additions & 0 deletions lib/handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use strict";

const assert = require("assert");
const base62 = require("./base62");
const crypto = require("crypto");

const UINT32_CAPACITY = 0xffffffff + 1;

/**
* Create a no-password auth token. Can be emailed or texted to the user.
*
* @param {string} username An identifier for this user, which could be their username, user ID, email address, and so forth. This is required to ensure that tokens are unique to a particular user.
* @param {string} secret A secret string to encrypt then decrypt no-password auth token
*/
const create = async (username, secret) => {
if (!username) throw new Error("An identifier for the user is required.");
if (typeof username !== "string")
throw new Error("The first argument must be a username string.");

let encrypted = await encrypt(username, secret);
return encrypted;
};

/**
* Parse a no-password auth token and check if is expiry
*
* @param {string} token A token created by {@link #create}.
* @param {string} username The identifier used when creating the token
* @param {string} secret A secret string to encrypt then decrypt no-password auth token
* @return `boolean` which is true if the token is valid, or false if the token is invalid.
*/
const validate = async (token, username, secret, expiry = 900000) => {
if (!username) throw new Error("username is required.");
if (typeof username !== "string")
throw new Error("username must be a string.");
if (!token) throw new Error("token required.");
if (typeof token !== "string") throw new Error("token must be a string.");

let timestamp = await decrypt(token, username, secret);

let currentTimestamp = new Date().getTime();

if (currentTimestamp - timestamp < expiry && currentTimestamp - timestamp > 0)
return true;
else return false;
};

/**
* Create Key to use with cipher and decipher
* @param {string} username An identifier for this user, which could be their username, user ID, email address, and so forth. This is required to ensure that tokens are unique to a particular user.
* @param {string} secret A secret string to encrypt then decrypt no-password auth token
* @param {number} iterations
*/
const createKey = (username, secret, iterations = 1000) => {
let usernameBuffer = new Buffer.from(username, "utf-8");
let key = crypto.pbkdf2Sync(usernameBuffer, secret, iterations, 16, "sha512");
return key;
};

/**
* Encrypt username with secret
*
* @param {string} username An identifier for this user, which could be their username, user ID, email address, and so forth. This is required to ensure that tokens are unique to a particular user.
* @param {secret} secret A secret string to encrypt then decrypt no-password auth token
* @returns {string} alphanumeric no-password auth token
*/
const encrypt = async (username, secret) => {
let key = createKey(username, secret);

// timestamp -> buf
let timestamp = new Date().getTime();
let buffer = new Buffer.alloc(8);
buffer.writeUInt32BE(timestamp / UINT32_CAPACITY, 0, true);
buffer.writeUInt32BE(timestamp % UINT32_CAPACITY, 4, true);

let cipher = createCipher(key);
let encrypted = cipher.update(buffer);
assert.strictEqual(0, cipher.final().length);

// encrypted -> token
let token = base62.encode(encrypted);
return token;
};

/**
* Decrypt username with secret
*
* @param {string} token The no-password auth token to decrypt
* @param {string} username An identifier for this user, which could be their username, user ID, email address, and so forth. This is required to ensure that tokens are unique to a particular user.
* @param {secret} secret A secret used to encrypt the no-password auth token
* @returns {string} alphanumeric no-password auth token
*/
const decrypt = (token, username, secret) => {
let key = createKey(username, secret);
// token -> encrypted
// This can fail if an invalid token is passed. Fail silently and return timestamp zero.
let encrypted;
try {
encrypted = base62.decode(token);
} catch (error) {
throw error;
}

let decipher = createDecipher(key);
let buffer = decipher.update(encrypted);
assert.strictEqual(0, decipher.final().length);

// buf -> timestamp
let timestamp = 0;
timestamp += buffer.readUInt32BE(0) * UINT32_CAPACITY; // note: timestamps are small enough that there is no floating-point overflow happening here
timestamp += buffer.readUInt32BE(4);

return timestamp;
};

/**
*
* Cipher and Decipher
*
*/

const createCipher = (key) => {
// Use a zero vector as the default IV. It isn't necessary to enforce distinct ciphertexts.
let iv = new Buffer.alloc(8);
iv.fill(0);
// buf -> encrypted
let cipher = crypto.createCipheriv("bf", key, iv);
cipher.setAutoPadding(false);
return cipher;
};

const createDecipher = (key) => {
// Use a zero vector as the default IV. It isn't necessary to enforce distinct ciphertexts.
let iv = new Buffer.alloc(8);
iv.fill(0);
// encrypted -> buf
let decipher = crypto.createDecipheriv("bf", key, iv);
decipher.setAutoPadding(false);
return decipher;
};

module.exports = {
create,
validate,
};
66 changes: 0 additions & 66 deletions lib/strategy.js

This file was deleted.

Loading