Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ////////// Express Server Backend //////////
- // server.js (EXCERPT)
- const express = require("express");
- const bodyParser = require('body-parser');
- var cookieParser = require('cookie-parser');
- const authRouter = require('./routes/auth.js');
- app.use(bodyParser.urlencoded({ extended: false }));
- app.use(bodyParser.json());
- app.use(cookieParser());
- // Set up auth routes
- app.use(authRouter);
- var server = require('http').createServer(app);
- server.listen(port, () => {
- console.log(`${new Date()} Server started on ${port}`);
- });
- // routes/auth.js (FULL FILE (minus the ldap part commented below))
- const express = require('express');
- const router = express.Router();
- const jwt = require('jsonwebtoken');
- //const bcrypt = require('bcryptjs');
- const uuidv1 = require('uuid/v1');
- // These are sqlite database methods defined in user.js, not including here as it will be dependent on how you are storing user data on the server side
- const { findUserByEmail, findUserByUserid, createUser } = require('../db/user.js');
- const SECRET_KEY = process.env.SOME_LONG_UNGUESSABLE_STRING;
- // Auth API
- const authViaLdaps = async (userid, password, cb) => {
- let authResult = false;
- // Doing an ldap test auth call, if the user/password is valid, it returns true, otherwise false for failed auth attempt
- // Code not included because it's rather specific to my project
- return authResult;
- }
- const createSplitAccessToken = (user, expiresIn = '1h') => {
- const accessToken = jwt.sign(
- { ...user },
- SECRET_KEY,
- {
- expiresIn,
- jwtid: uuidv1()
- }
- );
- // Split jwt into two parts so we can send it as two separate cookies
- const arr = accessToken.split(/\./);
- return {
- hpayload: `${arr[0]}.${arr[1]}`,
- signature: arr[2]
- };
- }
- router.post('/api/users', (req, res) => {
- const email = req.body.email;
- const userid = req.body.userid;
- let method, param;
- if (email) {
- method = findUserByEmail;
- param = email;
- } else if (userid) {
- method = findUserByUserid;
- param = userid;
- } else {
- res.status(400).send({
- "user_found": false
- });
- }
- method(param, (err, user) => {
- if (err) return res.status(500).send('Server error!');
- found = user ? true : false;
- res.status(200).send({
- "user_found": found
- });
- });
- });
- router.post('/api/register', async (req, res) => {
- const userid = req.body.userid;
- const name = req.body.name;
- const email = req.body.email;
- const password = req.body.password;
- // Not using bcrypt with a saved pw in database anymore, using ldap
- // const password = bcrypt.hashSync(req.body.password);
- const result = await authViaLdaps(userid, password);
- if (!result) {
- return res.status(401).send('Invalid password!');
- }
- // Check if the user already exists
- findUserByUserid(userid, async (err, user) => {
- // if (err) {
- // console.error(`Error when trying to find user by id: ${err}`);
- // return res.status(500).send('Server error!');
- // }
- if (user) {
- console.error(`User already exists and attempted to re-register: ${userid}`);
- return res.status(405).send('User already registered!');
- } else {
- // userid, name, email, registeredTs, lastAccessTs
- createUser([ userid, name, email, new Date().convertDateToLogDate(), new Date().convertDateToLogDate() ], (err) => {
- if (err) return res.status(500).send("Server error!");
- res.status(200).send({ registrationSuccessful: true });
- // Client should redirect to login
- });
- }
- });
- });
- const hour = 3600000;
- const setTokenCookies = (res, user, rememberMe) => {
- const splitAccessToken = createSplitAccessToken(user, rememberMe ? '7d' : '1hr');
- const expireDate = rememberMe ? new Date(new Date().getTime() + hour * 24 * 7) : new Date(new Date().getTime() + hour);
- res.cookie('hpayloadCookie', splitAccessToken.hpayload, {
- // secure: true, // Can't remember why I disabled this setting
- sameSite: true,
- expires: expireDate
- });
- res.cookie('signatureCookie', splitAccessToken.signature, {
- httpOnly: true,
- // secure:true,
- sameSite: true,
- expires: new Date('2038-01-19 04:14:07') // Distant future
- });
- };
- router.post('/api/login', (req, res) => {
- const userid = req.body.userid;
- const password = req.body.password;
- const rememberMe = req.body.rememberMe;
- findUserByUserid(userid, async (err, user) => {
- if (err) {
- console.error(`Error when trying to find user by id: ${err}`);
- return res.status(500).send('Server error!');
- }
- if (!user) {
- console.error(`User not found on attempted login: ${userid}`);
- return res.status(404).send('User not found!');
- }
- // const result = bcrypt.compareSync(password, user.password);
- const result = await authViaLdaps(userid, password);
- if (!result) {
- console.error(`Ldap password not valid for user: ${userid}`);
- return res.status(401).send('Password not valid!');
- }
- console.log(`Login for ${userid} successful!`);
- setTokenCookies(res, user, rememberMe);
- res.status(200).send('Login successful!');
- });
- });
- // Verify the token
- function verifyToken(hpayloadCookie, signatureCookie)
- {
- return jwt.verify(`${hpayloadCookie}.${signatureCookie}`, SECRET_KEY);
- }
- const verifyCookies = (req, res, next) =>
- {
- console.log(`/api/verify called`);
- const hpayloadCookie = req.cookies.hpayloadCookie;
- const signatureCookie = req.cookies.signatureCookie;
- if (!hpayloadCookie || !signatureCookie) {
- console.error(`Cookies not found!`);
- const status = 401;
- const message = 'Unauthorized';
- res.status(status).json({ status, message });
- }
- try {
- // If the verify fails, it will throw an error
- const decoded = verifyToken(hpayloadCookie, signatureCookie);
- const userid = decoded.userid;
- const exp = new Date(decoded.exp * 1000).getTime();
- const now = new Date().getTime();
- if (exp <= now) {
- throw new Error ('Cookie is expired');
- }
- next();
- } catch (err) {
- const status = 401;
- const message = 'Unauthorized';
- console.error(`Cookie authentication was NOT successful! ${err}`);
- res.status(status).json({ status, message });
- }
- }
- router.get('/api/verify', verifyCookies, (req, res, next) => {
- // console.log(`here01`);
- const status = 200;
- const message = 'Cookie authentication successful!';
- console.log(message);
- res.status(status).json({ status, message });
- });
- // END AUTH API
- module.exports = router;
- ////////// VueJS Client Website //////////
- // main.js (EXCERPT)
- import axios from 'axios';
- axios.defaults.withCredentials = true; // Required to send cookies and receive via Axios
- // utils/cookieUtilities.js (FULL FILE)
- export function getCookie(cname) {
- var name = cname + '=';
- var decodedCookie = decodeURIComponent(document.cookie);
- var ca = decodedCookie.split(';');
- for (var i = 0; i < ca.length; i++) {
- var c = ca[ i ];
- while (c.charAt(0) === ' ') {
- c = c.substring(1);
- }
- if (c.indexOf(name) === 0) {
- return c.substring(name.length, c.length);
- }
- }
- return '';
- }
- export function decodeHpayloadCookie(hpayloadCookieString) {
- const arr = hpayloadCookieString.split('.');
- const header = JSON.parse(window.atob(arr[ 0 ]));
- const payload = JSON.parse(window.atob(arr[ 1 ]));
- return { header, payload };
- }
- export function getPayload() {
- const hpayloadCookieString = getCookie('hpayloadCookie');
- if (hpayloadCookieString === '') {
- return undefined;
- }
- const { payload } = decodeHpayloadCookie(hpayloadCookieString);
- return payload;
- }
- // store.js (EXCERPT)
- import Vue from 'vue';
- import Vuex from 'vuex';
- import axios from 'axios';
- import { getPayload } from '@/utils/cookieUtilities';
- Vue.use(Vuex);
- export default new Vuex.Store({
- state: {
- tokenPayload: null,
- logoutHandler: null,
- loginStatus: null
- },
- getters: {
- isAuthenticated(state) {
- // eslint-disable-next-line
- return state.tokenPayload ? true : false;
- }
- },
- mutations: {
- setAuthData(state, tokenPayload) {
- state.tokenPayload = tokenPayload;
- },
- clearAuthData(state) {
- state.tokenPayload = null;
- },
- setLogoutHandler(state, logoutHandler) {
- state.logoutHandler = logoutHandler;
- },
- setLoginStatus(state, loginStatus) {
- state.loginStatus = loginStatus;
- }
- },
- actions: {
- setLogoutTimer({ state, dispatch }, msToExpire) {
- if (!msToExpire) {
- msToExpire = new Date(state.tokenPayload.exp * 1000).getTime() - new Date().getTime();
- }
- // timeout handler
- const logoutHandler = setTimeout(() => {
- dispatch('logout');
- }, msToExpire);
- this.commit('setLogoutHandler', logoutHandler);
- },
- register({ commit, dispatch }, authData) {
- commit('setLoginStatus', 'Authenticating');
- axios
- .post('/api/register', {
- userid: authData.userid,
- email: authData.email,
- password: authData.password,
- name: authData.name
- })
- .then(res => {
- // console.log(JSON.stringify(res, undefined, 2));
- if (res.data.registrationSuccessful === true) {
- commit('setLoginStatus', 'Successful');
- console.log(`Registration successful!`);
- router.replace('/login');
- } else {
- console.error(`Unknown login failure!`);
- commit('setLoginStatus', 'Unknown error');
- }
- })
- .catch(error => {
- if (error.response) {
- const res = error.response;
- console.error(`Registration failed!`);
- if (res.status === 401) {
- commit('setLoginStatus', 'Invalid password');
- } else if (res.status === 405) {
- commit('setLoginStatus', 'User already registered');
- } else if (res.status === 500) {
- commit('setLoginStatus', 'Server error');
- } else {
- commit('setLoginStatus', 'Unknown error');
- }
- } else {
- commit('setLoginStatus', 'Communication error');
- console.error(error);
- }
- });
- },
- login({ commit, state, dispatch }, authData) {
- commit('setLoginStatus', 'Authenticating');
- axios
- .post('/api/login', {
- userid: authData.userid,
- password: authData.password,
- rememberMe: authData.rememberMe
- })
- .then(res => {
- if (res.status === 200) {
- commit('setLoginStatus', 'Successful');
- const payload = getPayload();
- commit('setAuthData', payload);
- dispatch('getAvailableCriteria');
- dispatch('setLogoutTimer');
- console.log(state.toRoute); // NOTE I didn't include toRoute in the store in this example, I'm basically saving the route in my navigation guards when a user tries to access a URL, if you need similar logic I can send more details, just ask
- if (state.toRoute) {
- // This option is for cases where a token-bearing user tries to navigate directly to a subpath rather than using the UI links to get there
- router.replace({ path: state.toRoute.path, query: state.toRoute.query });
- } else {
- router.replace('/dashboard');
- }
- } else {
- console.error(`Unknown login failure!`);
- commit('setLoginStatus', 'Unknown error');
- }
- })
- .catch(error => {
- if (error.response) {
- const res = error.response;
- if (res.status === 401) {
- console.error(`Login attempt failed due to invalid password!`);
- commit('setLoginStatus', 'Invalid password');
- } else if (res.status === 404) {
- console.error(`Login attempt failed due to user not found!`);
- commit('setLoginStatus', 'User not found');
- } else if (res.status === 500) {
- console.error(`Login attempt failed due to server error when looking up user!`);
- commit('setLoginStatus', 'Server error');
- }
- } else {
- commit('setLoginStatus', 'Communication error');
- console.error(error);
- }
- });
- },
- tryAutoLogin({ commit, state, dispatch }) {
- const payload = getPayload();
- if (!payload) {
- return;
- }
- axios
- .get('/api/verify')
- .then(res => {
- // console.log(res);
- if (res.status === 200) {
- console.log(`Auto cookie login successful!`);
- commit('setAuthData', payload);
- dispatch('getAvailableCriteria');
- dispatch('setLogoutTimer');
- if (state.toRoute) {
- // console.log(state.toRoute);
- // console.log(router.currentRoute);
- // This option is for cases where a token-bearing user tries to navigate directly to a subpath rather than using the UI links to get there
- // This additional if check is needed to prevent router errors when trying to replace the curr path with the same path on auto login success
- if (state.toRoute.path !== router.currentRoute.path) {
- router.replace({ path: state.toRoute.path, query: state.toRoute.query });
- }
- } else {
- router.replace('/dashboard');
- }
- } else {
- console.warn(`Auto login failed! Response status: ${res.status}`);
- }
- })
- .catch(error => {
- console.warn(`Auto login failed! ${error}`);
- });
- },
- clearAuthData({ commit }) {
- console.log(`Clearing hpayloadCookie`);
- document.cookie = 'hpayloadCookie= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
- commit('clearAuthData');
- },
- logout({ state, dispatch }) {
- dispatch('clearAuthData');
- if (state.logoutHandler) {
- // console.log(`Clearing logout timeout handler`);
- clearTimeout(state.logoutHandler);
- }
- router.replace('/login');
- }
- }
- });
- // Example in random Vue component, say you want to get the userid from the token or check if authenticated
- computed: {
- isAuthenticated() {
- return this.$store.getters.isAuthenticated;
- },
- userid() {
- return this.$store.state.tokenPayload.userid;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement