Converted the account system to an auth system
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
WEB_PROTOCOL=http
|
||||||
|
WEB_ADDRESS=localhost
|
||||||
WEB_PORT=3200
|
WEB_PORT=3200
|
||||||
|
|
||||||
DB_HOSTNAME=database
|
DB_HOSTNAME=database
|
||||||
@@ -6,3 +8,10 @@ DB_USERNAME=auth
|
|||||||
DB_PASSWORD=venusaur
|
DB_PASSWORD=venusaur
|
||||||
DB_TIMEZONE=Australia/Sydney
|
DB_TIMEZONE=Australia/Sydney
|
||||||
|
|
||||||
|
MAIL_SMTP=smtp.example.com
|
||||||
|
MAIL_USERNAME=foobar@example.com
|
||||||
|
MAIL_PASSWORD=examplepassword
|
||||||
|
MAIL_PHYSICAL=42 Placeholder Ave, Placeholder, 0000, USA
|
||||||
|
|
||||||
|
SECRET_ACCESS=access
|
||||||
|
SECRET_REFRESH=refresh
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
An API centric auth server. Uses Sequelize and mariaDB by default.
|
An API centric auth server. Uses Sequelize and mariaDB by default.
|
||||||
|
|
||||||
|
TODO: Document the API
|
||||||
|
|||||||
Generated
+3827
File diff suppressed because it is too large
Load Diff
@@ -23,10 +23,14 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
|
"jsonwebtoken": "^8.5.1",
|
||||||
"mariadb": "^2.5.2",
|
"mariadb": "^2.5.2",
|
||||||
|
"node-cron": "^2.0.3",
|
||||||
|
"nodemailer": "^6.5.0",
|
||||||
"sequelize": "^6.5.0"
|
"sequelize": "^6.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
"nodemon": "^2.0.7"
|
"nodemon": "^2.0.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
const { accounts } = require('../database/models');
|
||||||
|
|
||||||
|
//auth/account
|
||||||
|
const route = async (req, res) => {
|
||||||
|
const account = await accounts.findOne({
|
||||||
|
where: {
|
||||||
|
username: req.user.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
res.status(401).send('Unknown account');
|
||||||
|
}
|
||||||
|
|
||||||
|
//respond with the private-facing data
|
||||||
|
res.status(200).json({
|
||||||
|
contact: await account.contact
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//libraries
|
||||||
|
const utils = require('util');
|
||||||
|
const bcrypt = require('bcryptjs');
|
||||||
|
var cron = require('node-cron');
|
||||||
|
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
const { accounts } = require('../database/models');
|
||||||
|
|
||||||
|
//auth/deletion
|
||||||
|
const route = async (req, res) => {
|
||||||
|
const account = await accounts.findOne({
|
||||||
|
where: {
|
||||||
|
username: req.user.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//compare the user's password
|
||||||
|
const compare = utils.promisify(bcrypt.compare);
|
||||||
|
const match = await compare(req.body.password, account.hash);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return res.status(401).send('incorrect password');
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the deletion time (2 days from now)
|
||||||
|
const interval = new Date(new Date().setDate(new Date().getDate() + 2)); //wow
|
||||||
|
await accounts.update({
|
||||||
|
deletion: interval
|
||||||
|
},
|
||||||
|
{
|
||||||
|
where: {
|
||||||
|
username: req.user.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//finally
|
||||||
|
return res.status(200).end();
|
||||||
|
};
|
||||||
|
|
||||||
|
//actually delete the accounts
|
||||||
|
cron.schedule('0 * * * *', async () => {
|
||||||
|
await accounts.destroy({
|
||||||
|
where: {
|
||||||
|
deletion: {
|
||||||
|
[Op.lt]: Sequelize.fn('NOW')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
+18
-4
@@ -1,10 +1,24 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
//the routes
|
//middleware
|
||||||
//TODO: import the routes here
|
const authToken = require('../utilities/token-auth');
|
||||||
|
|
||||||
//basic route management
|
//signup -> validate -> login all without a token
|
||||||
//TODO: define the routes here
|
router.post('/signup', require('./signup'));
|
||||||
|
router.get('/validation', require('./validation'));
|
||||||
|
router.post('/login', require('./login'));
|
||||||
|
|
||||||
|
//refresh token
|
||||||
|
router.post('/token', require('./token'));
|
||||||
|
|
||||||
|
//middleware
|
||||||
|
router.use(authToken);
|
||||||
|
|
||||||
|
//basic account management (needs a token)
|
||||||
|
router.delete('/logout', require('./logout'));
|
||||||
|
router.get('/account', require('./account'));
|
||||||
|
router.patch('/update', require('./update'));
|
||||||
|
router.delete('/deletion', require('./deletion'));
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
//libraries
|
||||||
|
const utils = require('util');
|
||||||
|
const bcrypt = require('bcryptjs');
|
||||||
|
|
||||||
|
const { accounts } = require('../database/models');
|
||||||
|
const generate = require('../utilities/token-generate');
|
||||||
|
|
||||||
|
//utilities
|
||||||
|
const validateEmail = require('../utilities/validate-email');
|
||||||
|
|
||||||
|
//auth/login
|
||||||
|
const route = async (req, res) => {
|
||||||
|
//validate the given details
|
||||||
|
const validateErr = await validateDetails(req.body);
|
||||||
|
if (validateErr) {
|
||||||
|
return res.status(401).send(validateErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the existing account
|
||||||
|
const account = await accounts.findOne({
|
||||||
|
where: {
|
||||||
|
email: req.body.email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return res.status(401).send('incorrect email or password');
|
||||||
|
}
|
||||||
|
|
||||||
|
//compare passwords
|
||||||
|
const compare = utils.promisify(bcrypt.compare);
|
||||||
|
const match = await compare(req.body.password, account.hash);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return res.status(401).send('incorrect email or password');
|
||||||
|
}
|
||||||
|
|
||||||
|
//cancel deletion if any
|
||||||
|
await accounts.update({ deletion: null }, {
|
||||||
|
where: {
|
||||||
|
id: account.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//generate the JWT
|
||||||
|
const tokens = generate(account.username, account.privilege);
|
||||||
|
|
||||||
|
//finally
|
||||||
|
res.status(200).json(tokens);
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateDetails = async (body) => {
|
||||||
|
//basic formatting (with an exception for the default admin account)
|
||||||
|
if (!validateEmail(body.email) && body.email != `admin@${process.env.WEB_ADDRESS}`) {
|
||||||
|
return 'invalid email';
|
||||||
|
}
|
||||||
|
//TODO: restore default admin account
|
||||||
|
|
||||||
|
//check for existing (banned)
|
||||||
|
//TODO: restore banning
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const destroy = require('../utilities/token-destroy');
|
||||||
|
|
||||||
|
//auth/logout
|
||||||
|
const route = (req, res) => {
|
||||||
|
destroy(req.body.token);
|
||||||
|
|
||||||
|
return res.status(200).end();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
//libraries
|
||||||
|
const bcrypt = require('bcryptjs');
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
|
const { accounts, pendingSignups } = require('../database/models');
|
||||||
|
|
||||||
|
|
||||||
|
//utilities
|
||||||
|
const uuid = require('../utilities/uuid');
|
||||||
|
const validateEmail = require('../utilities/validate-email');
|
||||||
|
const validateUsername = require('../utilities/validate-username');
|
||||||
|
|
||||||
|
//auth/signup
|
||||||
|
const route = async (req, res) => {
|
||||||
|
//validate the given details
|
||||||
|
const validateErr = await validateDetails(req.body);
|
||||||
|
if (validateErr) {
|
||||||
|
return res.status(401).send(validateErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate the password hash
|
||||||
|
const hash = await bcrypt.hash(req.body.password, await bcrypt.genSalt(11));
|
||||||
|
|
||||||
|
//generate the validation field
|
||||||
|
const token = uuid();
|
||||||
|
|
||||||
|
//register signup
|
||||||
|
const signupErr = await registerPendingSignup(req.body, hash, token);
|
||||||
|
if (signupErr) {
|
||||||
|
return res.status(500).send(signupErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//send the validation email
|
||||||
|
const emailErr = await sendValidationEmail(req.body.email, req.body.username, token);
|
||||||
|
if (emailErr) {
|
||||||
|
return res.status(500).send(emailErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//finally
|
||||||
|
res.status(200).send("Validation email sent!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validateDetails = async (body) => {
|
||||||
|
//basic formatting
|
||||||
|
if (!validateEmail(body.email)) {
|
||||||
|
return 'invalid email';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validateUsername(body.username)) {
|
||||||
|
return 'invalid username';
|
||||||
|
}
|
||||||
|
|
||||||
|
//check for existing (banned)
|
||||||
|
//TODO: re-add banned email checks
|
||||||
|
|
||||||
|
//check for existing email
|
||||||
|
const emailRecord = await accounts.findOne({
|
||||||
|
where: {
|
||||||
|
email: body.email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (emailRecord) {
|
||||||
|
return 'email already exists';
|
||||||
|
}
|
||||||
|
|
||||||
|
//check for existing username
|
||||||
|
const usernameRecord = await accounts.findOne({
|
||||||
|
where: {
|
||||||
|
username: body.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (usernameRecord) {
|
||||||
|
return 'username already exists';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerPendingSignup = async (body, hash, token) => {
|
||||||
|
const record = await pendingSignups.upsert({
|
||||||
|
email: body.email,
|
||||||
|
username: body.username,
|
||||||
|
hash: hash,
|
||||||
|
contact: body.contact,
|
||||||
|
token: token
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendValidationEmail = async (email, username, token) => {
|
||||||
|
const addr = `${process.env.WEB_PROTOCOL}://${process.env.WEB_ADDRESS}/auth/validation?username=${username}&token=${token}`;
|
||||||
|
const msg = `Hello ${username}!
|
||||||
|
|
||||||
|
Please visit the following link to validate your account: ${addr}
|
||||||
|
|
||||||
|
You can contact us directly at our physical mailing address here: ${process.env.MAIL_PHYSICAL}
|
||||||
|
`;
|
||||||
|
|
||||||
|
let transporter, info;
|
||||||
|
|
||||||
|
//what exactly is a transport?
|
||||||
|
try {
|
||||||
|
transporter = nodemailer.createTransport({
|
||||||
|
host: process.env.MAIL_SMTP,
|
||||||
|
port: 465,
|
||||||
|
secure: true,
|
||||||
|
auth: {
|
||||||
|
user: process.env.MAIL_USERNAME,
|
||||||
|
pass: process.env.MAIL_PASSWORD
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
return `failed to create a mail transport: ${e}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send mail with defined transport object
|
||||||
|
try {
|
||||||
|
info = await transporter.sendMail({
|
||||||
|
from: `signup@${process.env.WEB_ADDRESS}`, //WARNING: google overwrites this
|
||||||
|
to: email,
|
||||||
|
subject: 'Email Validation',
|
||||||
|
text: msg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
return `failed to send validation mail: ${e}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.accepted[0] != email) {
|
||||||
|
return 'validation email failed to send';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
const refresh = require('../utilities/token-refresh');
|
||||||
|
|
||||||
|
//auth/token
|
||||||
|
module.exports = async (req, res) => {
|
||||||
|
const refreshToken = req.body.token;
|
||||||
|
|
||||||
|
return refresh(refreshToken, (err, tokens) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(err).end();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send(tokens);
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
const bcrypt = require('bcryptjs');
|
||||||
|
const { accounts } = require('../database/models');
|
||||||
|
|
||||||
|
//auth/update
|
||||||
|
const route = async (req, res) => {
|
||||||
|
//generate the password hash
|
||||||
|
let hash;
|
||||||
|
|
||||||
|
if (req.body.password) {
|
||||||
|
hash = await bcrypt.hash(req.body.password, await bcrypt.genSalt(11));
|
||||||
|
}
|
||||||
|
|
||||||
|
//update the account
|
||||||
|
await accounts.update({
|
||||||
|
contact: req.body.contact,
|
||||||
|
hash: hash
|
||||||
|
}, {
|
||||||
|
where: {
|
||||||
|
username: req.user.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//respond with an OK
|
||||||
|
res.status(200).end();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
const { pendingSignups, accounts } = require('../database/models');
|
||||||
|
|
||||||
|
//auth/validation
|
||||||
|
const route = async (req, res) => {
|
||||||
|
//get the existing pending signup
|
||||||
|
const info = await pendingSignups.findOne({
|
||||||
|
where: {
|
||||||
|
username: req.query.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//check the given info
|
||||||
|
if (!info) {
|
||||||
|
return res.status(401).send('validation failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.token != req.query.token) {
|
||||||
|
return res.status(401).send('tokens do not match');
|
||||||
|
}
|
||||||
|
|
||||||
|
//move data to the accounts table
|
||||||
|
accounts.create({
|
||||||
|
email: info.email,
|
||||||
|
username: info.username,
|
||||||
|
hash: info.hash,
|
||||||
|
contact: info.contact
|
||||||
|
});
|
||||||
|
|
||||||
|
//delete the pending signup
|
||||||
|
pendingSignups.destroy({
|
||||||
|
where: {
|
||||||
|
username: req.query.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//finally
|
||||||
|
res.status(200).send('Validation succeeded!');
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const sequelize = require('..');
|
||||||
|
|
||||||
|
module.exports = sequelize.define('accounts', {
|
||||||
|
id: {
|
||||||
|
type: Sequelize.INTEGER(11),
|
||||||
|
allowNull: false,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
|
||||||
|
privilege: {
|
||||||
|
type: Sequelize.ENUM,
|
||||||
|
values: ['administrator', 'moderator', 'alpha', 'beta', 'gamma', 'normal'],
|
||||||
|
defaultValue: 'normal'
|
||||||
|
},
|
||||||
|
|
||||||
|
email: {
|
||||||
|
type: 'varchar(320)',
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
|
||||||
|
username: {
|
||||||
|
type: 'varchar(320)',
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
|
||||||
|
hash: 'varchar(100)', //for passwords
|
||||||
|
|
||||||
|
contact: {
|
||||||
|
type: Sequelize.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false
|
||||||
|
},
|
||||||
|
|
||||||
|
deletion: {
|
||||||
|
type: 'DATETIME',
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: null
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
//TODO: models
|
tokens: require('./tokens'),
|
||||||
};
|
accounts: require('./accounts'),
|
||||||
|
pendingSignups: require('./pending-signups')
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const sequelize = require('..');
|
||||||
|
|
||||||
|
module.exports = sequelize.define('pendingSignups', {
|
||||||
|
email: {
|
||||||
|
type: 'varchar(320)',
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
|
||||||
|
username: {
|
||||||
|
type: 'varchar(320)',
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
|
||||||
|
hash: 'varchar(100)', //for passwords
|
||||||
|
|
||||||
|
contact: {
|
||||||
|
type: Sequelize.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false
|
||||||
|
},
|
||||||
|
|
||||||
|
token: 'varchar(100)' //uuid
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const sequelize = require('..');
|
||||||
|
|
||||||
|
module.exports = sequelize.define('tokens', {
|
||||||
|
token: 'varchar(320)',
|
||||||
|
});
|
||||||
@@ -25,5 +25,6 @@ app.get('*', (req, res) => {
|
|||||||
|
|
||||||
//startup
|
//startup
|
||||||
server.listen(process.env.WEB_PORT || 3200, (err) => {
|
server.listen(process.env.WEB_PORT || 3200, (err) => {
|
||||||
|
database.sync();
|
||||||
console.log(`listening to localhost:${process.env.WEB_PORT || 3200}`);
|
console.log(`listening to localhost:${process.env.WEB_PORT || 3200}`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
//middleware to authenticate the JWT token
|
||||||
|
module.exports = (req, res, next) => {
|
||||||
|
const authHeader = req.headers['authorization'];
|
||||||
|
const token = authHeader?.split (' ')[1]; //'Bearer token'
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return res.status(401).end();
|
||||||
|
}
|
||||||
|
|
||||||
|
jwt.verify(token, process.env.SECRET_ACCESS, (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(403).end();
|
||||||
|
}
|
||||||
|
|
||||||
|
req.user = user;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const { tokens } = require('../database/models');
|
||||||
|
|
||||||
|
module.exports = (token) => {
|
||||||
|
tokens.destroy({
|
||||||
|
where: {
|
||||||
|
token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const { tokens } = require('../database/models');
|
||||||
|
|
||||||
|
//generates a JWT token based on the given arguments
|
||||||
|
module.exports = (username, privilege) => {
|
||||||
|
const content = {
|
||||||
|
username,
|
||||||
|
privilege
|
||||||
|
};
|
||||||
|
|
||||||
|
const accessToken = jwt.sign(content, process.env.SECRET_ACCESS, { expiresIn: '1m' });
|
||||||
|
const refreshToken = jwt.sign(content, process.env.SECRET_REFRESH);
|
||||||
|
|
||||||
|
tokens.create({ token: refreshToken });
|
||||||
|
|
||||||
|
return { accessToken, refreshToken };
|
||||||
|
};
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const { tokens } = require('../database/models');
|
||||||
|
|
||||||
|
const generate = require('./token-generate');
|
||||||
|
const destroy = require('./token-destroy');
|
||||||
|
|
||||||
|
module.exports = (token, callback) => {
|
||||||
|
if (!token) {
|
||||||
|
return callback(401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenRecord = tokens.findOne({
|
||||||
|
where: {
|
||||||
|
token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!tokenRecord) {
|
||||||
|
return callback(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
jwt.verify(token, process.env.SECRET_REFRESH, (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return callback(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = generate(user.username, user.privilege);
|
||||||
|
|
||||||
|
destroy(token);
|
||||||
|
|
||||||
|
return callback(null, result);
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
//lazy
|
||||||
|
module.exports = (bytes = 16) => crypto.randomBytes(bytes).toString("hex");
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
|
||||||
|
module.exports = email => {
|
||||||
|
return emailRegex.test(email);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
module.exports = username => {
|
||||||
|
if (!username) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username.length < 8 && username.length > 100) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAlpha(username)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAlpha = (str) => {
|
||||||
|
//starting from beginning ^
|
||||||
|
//to the end $
|
||||||
|
//check first letter is alpha or underscore [A-Za-z_]
|
||||||
|
//check the remaining 0 or more (*) letters are alpha, numeric or underscore [A-Za-z0-9_]
|
||||||
|
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(str);
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#Signup
|
||||||
|
POST http://127.0.0.1:3200/auth/signup HTTP/1.1
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "kayneruse@gmail.com",
|
||||||
|
"username": "Ratstail91",
|
||||||
|
"password": "helloworld"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
#Login
|
||||||
|
POST http://127.0.0.1:3200/auth/login HTTP/1.1
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "kayneruse@gmail.com",
|
||||||
|
"password": "helloworld"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
#Query data
|
||||||
|
GET http://127.0.0.1:3200/auth/account HTTP/1.1
|
||||||
|
Authorization: Bearer
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
#Logout
|
||||||
|
DELETE http://127.0.0.1:3200/auth/logout HTTP/1.1
|
||||||
|
Authorization: Bearer
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
#Refresh
|
||||||
|
POST http://127.0.0.1:3200/auth/token HTTP/1.1
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
#Update account data
|
||||||
|
PATCH http://127.0.0.1:3200/auth/update HTTP/1.1
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer
|
||||||
|
|
||||||
|
{
|
||||||
|
"contact": "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
#Delete account
|
||||||
|
DELETE http://127.0.0.1:3200/auth/deletion HTTP/1.1
|
||||||
|
Authorization: Bearer
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"password": "helloworld"
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#use this while debugging
|
||||||
|
CREATE DATABASE IF NOT EXISTS auth;
|
||||||
|
CREATE USER IF NOT EXISTS 'auth'@'%' IDENTIFIED BY 'venusaur';
|
||||||
|
GRANT ALL PRIVILEGES ON auth.* TO 'auth'@'%';
|
||||||
Reference in New Issue
Block a user