From ebcd8c584f9f3bea541668e593e2c9f564d3878e Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Sat, 26 Jun 2021 11:22:00 +0100 Subject: [PATCH] :sparkles: Re: #64 - Implements a 'Remember Me' dropdown in the Login form --- src/utils/Auth.js | 38 ++++++++++++++++++++++++++++++++++---- src/views/Login.vue | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/utils/Auth.js b/src/utils/Auth.js index 563c5e13..88e15d12 100644 --- a/src/utils/Auth.js +++ b/src/utils/Auth.js @@ -1,8 +1,18 @@ import sha256 from 'crypto-js/sha256'; import { cookieKeys, localStorageKeys } from './defaults'; +/** + * Generates a 1-way hash, in order to be stored in local storage for authentication + * @param {String} user The username of user + * @returns {String} The hashed token + */ const generateUserToken = (user) => sha256(user.toString()).toString().toLowerCase(); +/** + * Checks if the user is currently authenticated + * @param {Array[Object]} users An array of user objects pulled from the config + * @returns {Boolean} Will return true if the user is logged in, else false + */ export const isLoggedIn = (users) => { const validTokens = users.map((user) => generateUserToken(user)); let userAuthenticated = false; @@ -20,6 +30,15 @@ export const isLoggedIn = (users) => { return userAuthenticated; }; +/** + * Checks credentials entered by the user against those in the config + * Returns an object containing a boolean indicating success/ failure + * along with a message outlining what's not right + * @param {String} username The username entered by the user + * @param {String} pass The password entered by the user + * @param {String[]} users An array of valid user objects + * @returns {Object} An object containing a boolean result and a message + */ export const checkCredentials = (username, pass, users) => { let response; if (!username) { @@ -40,12 +59,23 @@ export const checkCredentials = (username, pass, users) => { return response || { correct: false, msg: 'User not found' }; }; -export const login = (username, pass) => { +/** + * Sets the cookie value in order to login the user locally + * @param {String} username - The users username + * @param {String} pass - Password, not yet hashed + * @param {Number} timeout - A desired timeout for the session, in ms + */ +export const login = (username, pass, timeout) => { + const now = new Date(); + const expiry = new Date(now.setTime(now.getTime() + timeout)).toGMTString(); const userObject = { user: username, hash: sha256(pass).toString().toLowerCase() }; - document.cookie = `authenticationToken=${generateUserToken(userObject)}; max-age=600`; + document.cookie = `authenticationToken=${generateUserToken(userObject)}; expires=${expiry}`; localStorage.setItem(localStorageKeys.USERNAME, username); }; +/** + * Removed the browsers cookie, causing user to be logged out + */ export const logout = () => { document.cookie = 'authenticationToken=null'; localStorage.removeItem(localStorageKeys.USERNAME); @@ -57,8 +87,8 @@ export const logout = () => { * But if auth is configured, then will verify user is correctly * logged in and then check weather they are of type admin, and * return false if any conditions fail - * @param users[] : Array of users - * @returns Boolean : True if admin privileges + * @param {String[]} - Array of users + * @returns {Boolean} - True if admin privileges */ export const isUserAdmin = (users) => { if (!users || users.length === 0) return true; // Authentication not setup diff --git a/src/views/Login.vue b/src/views/Login.vue index 3d16e0bb..4d723e1e 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -4,6 +4,14 @@

Dashy

+ +

{{ message }}

@@ -30,6 +38,13 @@ export default { password: '', message: '', status: 'waiting', // wating, error, success + timeout: { label: 'Never', time: 0 }, + dropDownMenu: [ // Data for timeout dropdown menu, label + value + { label: 'Never', time: 0 }, // Time is specified in ms + { label: '4 Hours', time: 14400 * 1000 }, + { label: '1 Day', time: 86400 * 1000 }, + { label: '1 Week', time: 604800 * 1000 }, + ], }; }, components: { @@ -38,11 +53,12 @@ export default { }, methods: { submitLogin() { + const timeout = this.timeout.time || 0; const response = checkCredentials(this.username, this.password, this.appConfig.auth || []); this.message = response.msg; // Show error or success message to the user this.status = response.correct ? 'success' : 'error'; if (response.correct) { // Yay, credentials were correct :) - login(this.username, this.password); // Login, to set the cookie + login(this.username, this.password, timeout); // Login, to set the cookie setTimeout(() => { // Wait a short while, then redirect back home router.push({ path: '/' }); }, 250); @@ -130,4 +146,30 @@ export default { 100% { transform: scale(1); } } +.v-select.login-time-dropdown { + margin: 0.5rem 0; + .vs__dropdown-toggle { + border-color: var(--login-form-color); + background: var(--login-form-background); + span.vs__selected { + color: var(--login-form-color); + } + .vs__actions svg path { fill: var(--login-form-color); } + } + ul.vs__dropdown-menu { + background: var(--login-form-background); + border-color: var(--login-form-color); + li { + color: var(--login-form-color); + &:hover { + color: var(--login-form-background); + background: var(--login-form-color); + } + &.vs__dropdown-option--highlight { + color: var(--login-form-background) !important; + background: var(--login-form-color); + } + } + } +}