Created
November 5, 2017 21:05
Revisions
-
evilsoft created this gist
Nov 5, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,39 @@ const { validateUser } = require('../data/users') const { compose, curry, objOf } = require('crocks') module.exports = ({ config, express, jwt, knex }) => { const { jwtSecret } = config const router = express.Router() const setCookie = res => payload => { res.cookie('token', jwt.sign(payload, jwtSecret)) res.redirect('/') } const render = curry( (res, locals) => res.render('login', locals) ) router.get('/', (req, res) => { render(res, {}) }) router.post('/', (req, res) => { const { body } = req validateUser({ knex }, body) .fork( compose(render(res), objOf('flash')), setCookie(res) ) }) router.get('/logout', (req, res) => { res.redirect('/') }) return router } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,83 @@ const Async = require('crocks/Async') const ReaderT = require('crocks/Reader/ReaderT') const crypto = require('crypto') const assign = require('crocks/helpers/assign') const assoc = require('crocks/helpers/assoc') const B = require('crocks/combinators/composeB') const curry = require('crocks/helpers/curry') const K = require('crocks/combinators/constant') const head = require('crocks/Maybe/head') const map = require('crocks/pointfree/map') const maybeToAsync = require('crocks/Async/maybeToAsync') const safe = require('crocks/Maybe/safe') const AsyncReader = ReaderT(Async) const { ask, liftFn } = AsyncReader const { fromPromise } = Async const table = 'users' const unique = [ 'email', 'userName' ] // liftQuery :: (a -> Promise b e) -> AsyncReader Object (Async e b) const liftQuery = fn => ask() .chain(liftFn(fromPromise(fn))) // mergeEnv :: Object -> Object const mergeEnv = assign({ table, unique }) // maybeEqual :: a -> a -> Maybe a const maybeEqual = curry( x => safe(y => y === x) ) // hash :: Object -> String const hash = ({ salt='', data }) => crypto.createHash('md5') .update(data.concat(salt), 'utf8') .digest('hex') // hashPassword :: Record -> Object const hashPassword = rec => { const { password: data, salt } = rec return assoc('password', hash({ data, salt }), rec) } // compareHashed :: String -> Object -> Object const compareHashed = curry( (hashed, { password, id }) => B(map(K({ id })), maybeEqual(password))(hashed) ) // getCreds :: Creds -> AsyncReader Object (Async e [ Record ]) const getCreds = ({ username }) => liftQuery(({ knex, table }) => knex(table) .select([ 'id', 'password', 'salt' ]) .where({ userName: username }) .orWhere({ email: username }) ) // validatePassword :: Object -> Record -> Async e Record const validatePassword = curry( (creds, rec) => { const { salt, id, password: hashed } = rec const { password } = creds return Async.of(hashPassword({ id, salt, password })) .chain(maybeToAsync('Invalid Credentials', compareHashed(hashed))) } ) // validateUser :: Object -> Creds -> Async e Record exports.validateUser = curry( (env, creds) => AsyncReader.of(creds) .chain(getCreds) .chain(liftFn(maybeToAsync('Invalid Credentials', head))) .chain(liftFn(validatePassword(creds))) .runWith(mergeEnv(env)) )