Skip to content

Commit

Permalink
Add UUID, Add JWT for authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
apavamontri committed May 21, 2017
1 parent 4d60736 commit 16c58ef
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 9 deletions.
12 changes: 10 additions & 2 deletions domains/configuration-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@ class ConfigurationData {
this.nodeEnv = environment;
}

get SecretKey() {
return this.secretKey;
}
set SecretKey(key) {
this.secretKey = key;
}

// toString() print out all the configuration data in easy to read format
toString() {
const output = {
MongoDBUrl: this.MongoDBUrl,
NodeEnv: this.NodeEnv,
MongoDBUrl: this.mongoDBUrl,
NodeEnv: this.nodeEnv,
SecretKey: this.secretKey,
};

return JSON.stringify(output, null, 2);
Expand Down
36 changes: 35 additions & 1 deletion domains/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,44 @@
* User domain contains information about user
*/
class User {
get Id() {
return this.id;
}
set Id(id) {
this.id = id;
}

get Email() {
return this.email;
}
set Email(email) {
this.email = email;
}

get Password() {
return this.password;
}
set Password(password) {
this.password = password;
}

get WebToken() {
return this.webToken;
}
set WebToken(webToken) {
this.webToken = webToken;
}

// toString() print out user information in easy to read format
toString() {
return 'User domain';
const output = {
Id: this.id,
Email: this.email,
Password: this.password,
WebToken: this.webToken,
};

return JSON.stringify(output, null, 2);
}
}

Expand Down
5 changes: 3 additions & 2 deletions infrastructures/environment-variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ class EnvironmentVariable {
const configurationData = new ConfigurationData();

// We are loding the configuration from the environment variable
configurationData.MongoDBUrl = process.env.MONGODB_URL;
configurationData.NodeEnv = process.env.NODE_ENV;
configurationData.MongoDBUrl = process.env.MONGODB_URL || 'localhost';
configurationData.NodeEnv = process.env.NODE_ENV || 'development';
configurationData.SecretKey = process.env.SECRET_KEY || 'secret';

return configurationData;
}
Expand Down
17 changes: 17 additions & 0 deletions infrastructures/json-web-token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const jwt = require('jsonwebtoken');

class JsonWebToken {
static getNewJsonWebToken(options) {
const tokenOptionalInfo = {
algorithm: 'HS256',
expiresIn: options.expiration,
};

return jwt.sign(
options.tokenPayload,
options.secret,
tokenOptionalInfo);
}
}

module.exports = JsonWebToken;
12 changes: 12 additions & 0 deletions infrastructures/mongodb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class MongoDb {
static saveEmailUser(user) {
// Fake saving by just return the user object
return user;
}

static isUserExists(email) {
return false;
}
}

module.exports = MongoDb;
19 changes: 19 additions & 0 deletions infrastructures/unique-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Unique Id class is responsible for generating a Universally unique identifier
*/
const uuidV4 = require('uuid/v4');

class UniqueId {
/**
* getNewUuid() Generate new Universally unique identifier
*/
static getNewUuid() {
/**
* Generate a v4 UUID (random)
* https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29
*/
return uuidV4();
}
}

module.exports = UniqueId;
16 changes: 16 additions & 0 deletions interfaces/authentication.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Authentication {
constructor(options) {
this.uniqueIdAdapter = options.UniqueIdAdapter;
this.webTokenAdapter = options.WebTokenAdapter;
}

getNewUserId() {
return this.uniqueIdAdapter.getNewUuid();
}

getNewWebToken(options) {
return this.webTokenAdapter.getNewJsonWebToken(options);
}
}

module.exports = Authentication;
15 changes: 15 additions & 0 deletions interfaces/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class Database {
constructor(options) {
this.databaseAdapter = options.DatabaseAdapter;
}

saveEmailUser(user) {
return this.databaseAdapter.saveEmailUser(user);
}

isUserExists(email) {
return this.databaseAdapter.isUserExists(email);
}
}

module.exports = Database;
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
"nodemon": "^1.11.0"
},
"dependencies": {
"express": "^4.15.2"
"express": "^4.15.2",
"jsonwebtoken": "^7.4.1",
"lodash": "^4.17.4",
"moment": "^2.18.1"
}
}
34 changes: 31 additions & 3 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,55 @@
*/
const EnvironmentVariables = require('./infrastructures/environment-variables');
const ExpressWebServer = require('./infrastructures/express-server');
const JsonWebToken = require('./infrastructures/json-web-token');
const MongoDb = require('./infrastructures/mongodb');
const UniqueId = require('./infrastructures/unique-id');

const ConfigurationAdapter = require('./interfaces/configuration');
const AuthenticationInterface = require('./interfaces/authentication');
const ConfigurationInterface = require('./interfaces/configuration');
const DatabaseInterface = require('./interfaces/database');
const WebServerInterface = require('./interfaces/webserver');

const ConfigurationInteractor = require('./usecases/configuration');
const VersionInteractor = require('./usecases/version');
const UserInteractor = require('./usecases/user');

const environmentVariable = new EnvironmentVariables();
const configurationAdapter = new ConfigurationAdapter({
const configurationInterface = new ConfigurationInterface({
ConfigurationAdapter: environmentVariable,
});

const configurationInteractor = new ConfigurationInteractor({
ConfigurationInterface: configurationAdapter,
ConfigurationInterface: configurationInterface,
});

const configuraionData = configurationInteractor.load();

// Print out the current configuration data for testing purpose
console.log(configuraionData.toString());

const authenticationInterface = new AuthenticationInterface({
UniqueIdAdapter: UniqueId,
WebTokenAdapter: JsonWebToken,
});

const databaseInterface = new DatabaseInterface({
DatabaseAdapter: MongoDb,
});

const userInteractor = new UserInteractor({
ConfigurationData: configuraionData,
AuthenticationInterface: authenticationInterface,
DatabaseInterface: databaseInterface,
});

const output = userInteractor.createNewEmailUser({
Email: '[email protected]',
Password: 'secret',
});

console.log(JSON.stringify(output, null, 2));

const versionInteractor = new VersionInteractor();
const webserverInterface = new WebServerInterface({
VersionInteractor: versionInteractor,
Expand Down
50 changes: 50 additions & 0 deletions usecases/user.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,57 @@
/**
* User usecases is a business logic of user
*/
const UserDomain = require('../domains/user');

class User {
constructor(options) {
this.configurationData = options.ConfigurationData;
this.authenticationInterface = options.AuthenticationInterface;
this.databaseInterface = options.DatabaseInterface;
}

createNewEmailUser(options) {
if (!options && typeof options === 'undefined') {
throw new Error('Missing (Email) user information');
}

if (!options.Email
|| !options.Password) {
throw new Error('Missing Email and/or password information');
}

if (this.databaseInterface.isUserExists(options.Email)) {
throw new Error(`User [${options.Email} already exists.`);
}

const newUser = new UserDomain();

newUser.Id = this.authenticationInterface.getNewUserId();
newUser.Email = options.Email;
newUser.Password = options.Password;

newUser.WebToken = this.authenticationInterface.getNewWebToken({
tokenPayload: {
Id: newUser.Id,
},
secret: this.configurationData.SecretKey,
expiration: this.expiresOneYearFromNow(),
});

return this.databaseInterface.saveEmailUser(newUser);
}

// Get number of seconds until token is expired
expiresOneYearFromNow() {
const now = new Date();
const nowInMilliseconds = now.getTime();

const nextYearInMilliseconds = new Date(
new Date().setFullYear(now.getFullYear() + 1)).getTime();

return (nextYearInMilliseconds - nowInMilliseconds) / 1000;
}

static toString() {
return 'User interactor';
}
Expand Down

0 comments on commit 16c58ef

Please sign in to comment.