Add basic login and register functionality

This commit is contained in:
Kevin Thomas
2021-07-22 23:55:30 -07:00
parent b4e791a21c
commit 9f5a3a5ad8
13 changed files with 300 additions and 25 deletions

6
.gitignore vendored
View File

@@ -102,3 +102,9 @@ dist
# TernJS port file
.tern-port
# Local dev certs
certs/*
# SQLite DB
db.sqlite3

43
package-lock.json generated
View File

@@ -7,10 +7,12 @@
"": {
"version": "0.1.0",
"dependencies": {
"axios": "^0.21.1",
"body-parser": "^1.19.0",
"connect-ensure-login": "^0.1.1",
"cookie-parser": "^1.4.5",
"core-js": "^3.6.5",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-session": "^1.17.2",
"passport": "^0.4.1",
@@ -3147,6 +3149,14 @@
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"devOptional": true
},
"node_modules/axios": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
"dependencies": {
"follow-redirects": "^1.10.0"
}
},
"node_modules/babel-eslint": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -4841,6 +4851,18 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
@@ -7443,7 +7465,6 @@
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
"dev": true,
"funding": [
{
"type": "individual",
@@ -19132,6 +19153,14 @@
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
"devOptional": true
},
"axios": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
"requires": {
"follow-redirects": "^1.10.0"
}
},
"babel-eslint": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@@ -20503,6 +20532,15 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
@@ -22561,8 +22599,7 @@
"follow-redirects": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
"dev": true
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg=="
},
"for-in": {
"version": "1.0.2",

View File

@@ -4,14 +4,17 @@
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"server": "node server/bin/www",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.21.1",
"body-parser": "^1.19.0",
"connect-ensure-login": "^0.1.1",
"cookie-parser": "^1.4.5",
"core-js": "^3.6.5",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-session": "^1.17.2",
"passport": "^0.4.1",

View File

@@ -1,15 +1,17 @@
const express = require('express');
const session = require('express-session')
const cors = require('cors')
const passport = require('passport');
const path = require('path');
const cookieParser = require('cookie-parser');
const db = require('./db');
const ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
const crypto = require('crypto');
const bodyParser = require("body-parser")
const indexRouter = require('./routes/index');
const authRouter = require('./routes/auth');
const myaccountRouter = require('./routes/myaccount');
const usersRouter = require('./routes/users');
const db = require('./db');
const app = express();
@@ -20,7 +22,7 @@ app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));
app.use(session({ secret: "cats", resave: false, saveUninitialized: false }));
app.use(function(req, res, next) {
const msgs = req.session.messages || [];
res.locals.messages = msgs;
@@ -28,8 +30,10 @@ app.use(function(req, res, next) {
req.session.messages = [];
next();
});
app.use(bodyParser.json());
app.use(passport.initialize());
app.use(passport.authenticate('session'));
app.use(cors())
// Define routes
app.use('/', indexRouter);

93
server/bin/www Normal file
View File

@@ -0,0 +1,93 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
const app = require('../app');
const debug = require('debug')('example:server');
const http = require('http');
const https = require('https')
const fs = require('fs')
/**
* Get port from environment and store in Express.
*/
const port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* HTTPS options.
*/
const httpsOptions = {
key: fs.readFileSync('./certs/key.pem'),
cert: fs.readFileSync('./certs/cert.pem')
}
/**
* Create HTTP server.
*/
const server = https.createServer(httpsOptions, app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
const port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
const bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
const addr = server.address();
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}

View File

@@ -3,20 +3,16 @@ const passport = require('passport');
const router = express.Router();
/* GET users listing. */
router.get('/login', function(req, res, next) {
res.render('login');
router.post('/login/password', passport.authenticate('local'), function(req, res, next) {
if(req.user) {
res.json(req.user);
} else {
res.statusCode = 403;
}
});
router.post('/login/password', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureMessage: true
}));
router.get('/logout', function(req, res, next) {
req.logout();
res.redirect('/');
});
module.exports = router;

View File

@@ -1,9 +1,8 @@
const express = require('express');
const router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { user: req.user });
res.json({ user: req.user });
});
module.exports = router;

View File

@@ -4,11 +4,8 @@ const db = require('../db');
const router = express.Router();
router.get('/new', function(req, res, next) {
res.render('signup');
});
router.post('/', function(req, res, next) {
console.log("REQ: ", req.body)
const salt = crypto.randomBytes(16);
crypto.pbkdf2(req.body.password, salt, 10000, 32, 'sha256', function(err, hashedPassword) {
if (err) { return next(err); }
@@ -28,7 +25,6 @@ router.post('/', function(req, res, next) {
};
req.login(user, function(err) {
if (err) { return next(err); }
res.redirect('/');
});
});
});

View File

@@ -1,7 +1,10 @@
<template>
<v-form v-model="valid">
<v-container>
<v-col cols="12" class="mb-4">
<v-col
cols="12"
class="mb-4"
>
<h1 class="display-2 font-weight-bold mb-3">
Login
</h1>
@@ -31,6 +34,13 @@
required
/>
</v-col>
<v-btn
class="mx-3 mb-5"
@click="login"
>
Login
</v-btn>
</v-container>
</v-form>
</template>
@@ -47,6 +57,22 @@ export default {
passwordRules: [
v => !!v || 'Password is required'
]
})
}),
methods: {
login () {
this.$http.post('https://localhost:3000/login/password', {
username: this.username,
password: this.password
})
.then(response => {
if (response.status === 200) {
this.$router.push('/')
}
})
.catch(function (error) {
console.error(error.response)
})
}
}
}
</script>

View File

@@ -0,0 +1,89 @@
<template>
<v-form v-model="valid">
<v-container>
<v-col
cols="12"
class="mb-4"
>
<h1 class="display-2 font-weight-bold mb-3">
Register
</h1>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="name"
:rules="[rules.required]"
label="Name"
required
/>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="username"
:rules="[rules.required]"
label="Username"
required
/>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="password"
type="password"
:rules="[rules.required]"
label="Password"
required
/>
</v-col>
<v-btn
class="mx-3 mb-5"
@click="register"
>
Register
</v-btn>
</v-container>
</v-form>
</template>
<script>
export default {
data: () => ({
valid: false,
name: '',
username: '',
password: '',
rules: {
required: v => !!v || 'Required'
}
}),
methods: {
register () {
this.$http.post('https://localhost:3000/users', {
name: this.name,
username: this.username,
password: this.password
})
.then(response => {
if (response.status === 200) {
this.$router.push('/')
}
})
.catch(function (error) {
console.error(error.response)
})
}
}
}
</script>

View File

@@ -2,6 +2,9 @@ import Vue from 'vue'
import App from './App.vue'
import router from './router'
import vuetify from './plugins/vuetify'
import Axios from 'axios'
Vue.prototype.$http = Axios
Vue.config.productionTip = false

View File

@@ -17,6 +17,14 @@ const routes = [
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/Signin.vue')
},
{
path: '/register',
name: 'Signup',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/Signup.vue')
}
]

15
src/views/Signup.vue Normal file
View File

@@ -0,0 +1,15 @@
<template>
<Register />
</template>
<script>
import Register from '../components/Register'
export default {
name: 'Signup',
components: {
Register
}
}
</script>