Skip to content
This repository has been archived by the owner on Aug 25, 2021. It is now read-only.

Commit

Permalink
Merge branch 'auth-core'
Browse files Browse the repository at this point in the history
  • Loading branch information
yashdiniz committed Mar 5, 2021
2 parents b9ff18b + 9482f60 commit 18113e5
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 37 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ db/
*.toc

# ignoring the DS store
.DS_Store
.DS_Store

# ignoring the certificates!!
certs/
Binary file added report/images/stacked-auto-encoder.svgz
Binary file not shown.
29 changes: 25 additions & 4 deletions server/config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const dotenv = require('dotenv');
const path = require('path');
const fs = require('fs');

const projectRoot = '/home/yash/Desktop/focusa-new/server';
dotenv.config({ path: projectRoot + '/.env' });
dotenv.config({ path: path.join(projectRoot, '.env') });

const production = false;
const port = 1896; // using FOCUSA legacy port for testing.
Expand All @@ -23,8 +25,26 @@ const pbkdfIters = 1<<14,
currentPasswordScheme = 'pbkdf2',
minPasswordLength = 8,
UUIDSize = 24;

const JWTsignOptions = {
algorithm: 'RS256',
expiresIn: 300, // 5 minutes
notBefore: 0, // available from current timestamp
audience: 'react-native-app',
issuer: 'FOCUSA',
subject: 'graphql',
},
JWTverifyOptions = {
algorithms: ['RS256'],
audience: 'react-native-app',
issuer: 'FOCUSA',
subject: 'graphql',
},
JWTsecret = {
public: fs.readFileSync(path.join(projectRoot, 'certs/certificate.pem')),
private: fs.readFileSync(path.join(projectRoot, 'certs/key.pem')),
};
const usernamePattern = /\w+/; // almost alphanumeric pattern(URL safe)
const sessionMaxAge = 5 * 60 * 1000;

// course-related
const maxModRolesforCourse = 2;
Expand All @@ -33,8 +53,9 @@ const maxModRolesforCourse = 2;
const defaultProfilePic = 'dp.jpeg';

module.exports = {
port, authPort, sessionMaxAge,
projectRoot, graphiql, secret, realm, remote,
port, authPort,
projectRoot, graphiql, secret, realm, remote,
JWTsignOptions, JWTverifyOptions, JWTsecret,
pbkdfIters, pbkdfDigest, pbkdfLen, UUIDSize, currentPasswordScheme,
minPasswordLength, usernamePattern, maxModRolesforCourse,
defaultProfilePic
Expand Down
7 changes: 7 additions & 0 deletions server/generateCerts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
openssl x509 -text -noout -in certificate.pem
openssl pkcs12 -inkey key.pem -in certificate.pem -export -out certificate.p12 # gyroscope: subject to change
openssl pkcs12 -in certificate.p12 -noout -info

16 changes: 5 additions & 11 deletions server/services/auth/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,15 @@ const { pbkdfDigest, pbkdfIters, pbkdfLen, usernamePattern, currentPasswordSchem
const crypto = require('crypto');

// Reference: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
/**
* The hashing technique used for passwords.
* @param {string} word The word to hash.
* @param {string} salt A random salt to use for hash obfuscation.
*/
const pbkdf = (word, salt) => new Promise((resolve, reject) => // will return a Promise!
crypto.pbkdf2(word, salt, pbkdfIters, pbkdfLen, pbkdfDigest,
(err, key) => err !== null ? reject(err) : resolve(key.toString('base64'))));

// logs.get('_local/firstTime') // check the presence of the flag
// .catch(e=>{ // if it does not exist
// console.log("Welcome to FOCUSA! Initialising database.");
// return security.putRole('_admin', "The administrator role.") // create an admin role
// .then(o=> security.putRole('_super', "The superuser role."))
// .then(o=> security.createUser('admin','admin',['_admin'])) // then create a user admin
// .then(o=> logs.put({ // then create the flag
// _id:'_local/firstTime', value: true,
// }));
// }).catch(e=> console.error("Database initialization failed.", e));

const userExistsError = new Error('Username already exists. Please try another username.'),
loginError = new Error('Login failed. Incorrect username or password.'),
userNonExistant = new Error('User does not exist.');
Expand Down
8 changes: 3 additions & 5 deletions server/services/auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const express = require('express');
const express_session = require('express-session');
const app = express();

const { authPort, secret, sessionMaxAge } = require('../../config');
const { authPort, secret } = require('../../config');
const { localStrategy } = require('./strategy.js');
const { ensureAuthenticated } = require('./ensureAuthenticated');

Expand Down Expand Up @@ -37,20 +37,18 @@ app.get('/login',
failureRedirect: '/error',
}),
(req, res) => {
// set the refresh cookie maximum age as needed.
req.session.cookie.maxAge = sessionMaxAge;
// TODO: add in the JWT transfer too.
res.json({
name: req.user.name,
time: req.user.time,
token: req.user.token,
login: true });
}
);

app.get('/check', ensureAuthenticated, (req, res) => {
res.json({
name: req.user.name,
time: req.user.time,
token: req.user.token,
});
});

Expand Down
16 changes: 10 additions & 6 deletions server/services/auth/strategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@
*/

const LocalStrategy = require('passport-local').Strategy;
const jwt = require('jsonwebtoken');
const { validateUser } = require('./functions');
const jwt = require('../jwt');

// form values sent need to have name and pass as input values...
const localStrategy = new LocalStrategy(async (username, password, done) => {
const localStrategy = new LocalStrategy({
passReqToCallback: true,
},
async (req, username, password, done) => {
try {
// on successful validation, no error will be thrown
let user = await validateUser(username, password);
let session = { // create a session.
name: user.name,
time: Date.now(),
ip: undefined, // TODO: Add geo-ip identificatio details in session.
token: undefined, // TODO: Also add the JWT authentication logic
}
return done(null, session);
ip: req.ip,
};
let token = jwt.sign(session); // sign a JWT with session details
session.token = token; // add the JWT to the session
return done(null, session); // pass the session down through callback
} catch(e) {
console.error(e); // TODO: remove the console commands once done, switch to logger
return done(null, false, { message: 'Incorrect username or password.'})
Expand Down
26 changes: 16 additions & 10 deletions server/services/databases.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,31 @@
* author: @yashdiniz
*/
const path = require('path'), crypto = require('crypto');
const { UUIDSize, maxModRolesforCourse, defaultProfilePic, remote, projectRoot } = require('../config');
const { UUIDSize, maxModRolesforCourse,
defaultProfilePic, remote, projectRoot } = require('../config');

const RxDB = require('rxdb');
const leveldown = require('leveldown');
RxDB.addRxPlugin(require('pouchdb-adapter-leveldb'));

// const sync = () => {
// syncHandlers.forEach(o => {
// o.handler.on('complete',
// async ()=> console.log('Database', o.db, 'synced.')
// ).on('error', e=>console.log(e));
// });
// };

// assert(condition, message) simplifies the code structure, by immediately throwing if condition is not met.
// assert(condition, message) simplifies the code structure,
// by immediately throwing if condition is not met.
/**
* Asserts a condition, and throws a message on violation.
* @param {boolean} condition Condition to be asserted.
* @param {string} message Message to throw on condition violation.
*/
const assert = (condition, message) => {
if(!condition) throw message || 'Assertion failed!';
else return true;
};

// generate a random number as UUID
// the UUID is made URL safe, while still maintaining base64, by basic substitution
/**
* Generates a random string of characters.
* @param {number} size Number of random characters to return.
*/
const generateUUID = (size) => crypto.rng(size || UUIDSize)
.toString('base64')
.replace(/\//gi,'.').replace(/\+/gi,'-').replace(/\=/gi,'_')
Expand Down Expand Up @@ -267,6 +270,9 @@ const focusa = db.then(db=> db.addCollections({

RxDB.addRxPlugin(require('pouchdb-adapter-http'));

/**
* Returns a Promise holding the state of replication process.
*/
const replicationState = async () =>
await focusa.then(focusa => {
let collections = Object.keys(focusa);
Expand Down
25 changes: 25 additions & 0 deletions server/services/jwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const { JWTsignOptions, JWTverifyOptions, JWTsecret } = require('../config');
const jwt = require('jsonwebtoken');

/**
* Signs a token using JWT.
* @param {string} username Username as subject of payload.
* @param {object} payload The other session information stored in payload.
*/
const sign = (payload) => {
return jwt.sign(payload, JWTsecret.private, {
...JWTsignOptions,
});
};

/**
* Verifies and returns the payload of the JWT token.
* @param {string} token The JWT token to verify.
*/
const verify = (token) => {
return jwt.verify(token, JWTsecret.public, {
...JWTverifyOptions,
});
};

module.exports = { sign, verify }

0 comments on commit 18113e5

Please sign in to comment.