Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f9274eb3f | |||
| 4043507e01 | |||
| 12fb02484a | |||
| 49179de73b | |||
| 89ea67456d | |||
| 46152032bb | |||
| 2d92b31272 | |||
| f93564931a |
@@ -0,0 +1,5 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: krgamestudios
|
||||
ko_fi: krgamestudios
|
||||
custom: ["https://www.paypal.com/donate/?hosted_button_id=73Q82T2ZHV8AA"]
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Node.js CI
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
jobs:
|
||||
build:
|
||||
name: Run test suites
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- run: npm test
|
||||
@@ -123,7 +123,7 @@ POST /auth/recover
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "kayneruse@gmail.com"
|
||||
"email": "example@example.com"
|
||||
}
|
||||
|
||||
|
||||
|
||||
Generated
+7545
-779
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "auth-server",
|
||||
"version": "1.4.10",
|
||||
"version": "1.4.14",
|
||||
"description": "An API centric auth server. Uses Sequelize and mariaDB by default.",
|
||||
"main": "server/server.js",
|
||||
"scripts": {
|
||||
"start": "node server/server.js",
|
||||
"dev": "npm run watch:server",
|
||||
"watch:server": "nodemon . --ext js,jsx,json --ignore 'node_modules/*'"
|
||||
"watch:server": "nodemon . --ext js,jsx,json --ignore 'node_modules/*'",
|
||||
"test": "jest --coverage --collectCoverageFrom=server/**/*.{js,jsx}"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -31,6 +32,7 @@
|
||||
"sequelize": "^6.6.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^27.5.1",
|
||||
"nodemon": "^2.0.12"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ const route = async (req, res) => {
|
||||
//validate the given details
|
||||
const validateErr = await validateDetails(req.body);
|
||||
if (validateErr) {
|
||||
return res.status(401).end(validateErr);
|
||||
return res.status(401).send(validateErr);
|
||||
}
|
||||
|
||||
//get the existing account
|
||||
@@ -29,6 +29,7 @@ const route = async (req, res) => {
|
||||
|
||||
//compare passwords
|
||||
const compare = utils.promisify(bcrypt.compare);
|
||||
|
||||
const match = await compare(req.body.password, account.hash);
|
||||
|
||||
if (!match) {
|
||||
@@ -47,11 +48,12 @@ const route = async (req, res) => {
|
||||
return res.status(403).send('this account has been banned');
|
||||
}
|
||||
|
||||
//generate the JWT
|
||||
const token = tokenGenerate(account.index, account.email, account.username, account.type, account.admin, account.mod);
|
||||
//generate the JWTs
|
||||
const tokens = tokenGenerate(account.index, account.email, account.username, account.type, account.admin, account.mod);
|
||||
|
||||
//finally
|
||||
res.status(200).json(token);
|
||||
res.status(200).json(tokens);
|
||||
return null;
|
||||
};
|
||||
|
||||
const validateDetails = async (body) => {
|
||||
|
||||
@@ -12,6 +12,7 @@ module.exports = (index, email, username, type, admin, mod) => {
|
||||
mod,
|
||||
};
|
||||
|
||||
//these are strings
|
||||
const accessToken = jwt.sign(content, process.env.SECRET_ACCESS, { expiresIn: '10m', issuer: 'auth' });
|
||||
const refreshToken = jwt.sign(content, process.env.SECRET_REFRESH, { expiresIn: '30d', issuer: 'auth' });
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
describe('POST /auth/login', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
//fix util with jest (used by bcrypt's compare)
|
||||
jest.doMock('util', () => ({
|
||||
promisify: f => async () => f()
|
||||
}));
|
||||
|
||||
//mock out bcrypt
|
||||
jest.doMock('bcryptjs', () => ({
|
||||
genSalt: async amount => {
|
||||
expect(amount).toBe(11);
|
||||
return 'salt';
|
||||
},
|
||||
hash: async (password, salt) => {
|
||||
expect(password).toBe('password');
|
||||
return 'hashed-password';
|
||||
},
|
||||
compare: (lhs, rhs) => {
|
||||
return lhs === rhs;
|
||||
}
|
||||
}));
|
||||
|
||||
//mock out jsonwebtoken
|
||||
jest.doMock('jsonwebtoken', () => ({
|
||||
sign: (content, secretAccess, opts) => {
|
||||
return JSON.stringify(content);
|
||||
},
|
||||
|
||||
verify: (token, secretAccess, callback) => {
|
||||
return callback(null, JSON.parse(token));
|
||||
},
|
||||
}));
|
||||
|
||||
//mock out the sequelize library
|
||||
jest.doMock('sequelize', () => {
|
||||
return {
|
||||
Op: {
|
||||
//
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//mock out the database object
|
||||
jest.doMock('../../server/database', () => {
|
||||
const mSequelize = {
|
||||
authenticate: jest.fn(),
|
||||
define: jest.fn(),
|
||||
};
|
||||
|
||||
const actualSequelize = jest.requireActual('sequelize');
|
||||
return { Sequelize: jest.fn(() => mSequelize), DataTypes: actualSequelize.DataTypes };
|
||||
});
|
||||
|
||||
//mock out the database models
|
||||
jest.doMock('../../server/database/models', () => ({
|
||||
accounts: {
|
||||
findOne: async (config) => { //can't find any (signup state)
|
||||
expect(config?.where?.email).toBe('email@example.com');
|
||||
return {
|
||||
index: 0,
|
||||
email: config?.where?.email,
|
||||
username: 'username',
|
||||
type: 'alpha',
|
||||
admin: false,
|
||||
mod: false,
|
||||
};
|
||||
},
|
||||
|
||||
update: async (values, config) => {
|
||||
//Do nothing
|
||||
}
|
||||
},
|
||||
|
||||
tokens: {
|
||||
create: async (record) => {
|
||||
//Do nothing
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
test('Basic valid login attempt', async () => {
|
||||
//arguments
|
||||
const req = {
|
||||
body: {
|
||||
email: 'email@example.com',
|
||||
password: 'password',
|
||||
}
|
||||
};
|
||||
|
||||
const res = {
|
||||
status: code => {
|
||||
expect(code).toBe(200);
|
||||
return {
|
||||
json: tokens => {
|
||||
//decode and analyze the JWT payload
|
||||
const accessToken = JSON.parse(tokens.accessToken);
|
||||
|
||||
expect(accessToken.email).toBe('email@example.com');
|
||||
expect(accessToken.username).toBe('username');
|
||||
},
|
||||
send: msg => { throw msg; },
|
||||
end: () => null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//test
|
||||
const route = require('../../server/auth/login');
|
||||
|
||||
const result = await route(req, res);
|
||||
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
describe('POST /auth/signup', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
//mock out bcrypt
|
||||
jest.doMock('bcryptjs', () => ({
|
||||
genSalt: async amount => {
|
||||
expect(amount).toBe(11);
|
||||
return 'salt';
|
||||
},
|
||||
hash: async (password, salt) => {
|
||||
expect(password).toBe('password');
|
||||
return 'hashed-password';
|
||||
}
|
||||
}));
|
||||
|
||||
//mock out nodemailer
|
||||
jest.doMock('nodemailer', () => ({
|
||||
createTransport: jest.fn(config => {
|
||||
//TODO: test config?
|
||||
return { //return a fake transport object
|
||||
sendMail: async email => {
|
||||
expect(email.to).toBe('email@example.com');
|
||||
return { //return a fake info object
|
||||
accepted: [ email.to ]
|
||||
}
|
||||
}
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
//mock out the sequelize library
|
||||
jest.doMock('sequelize', () => {
|
||||
return {
|
||||
Op: {
|
||||
//
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//mock out the database object
|
||||
jest.doMock('../../server/database', () => {
|
||||
const mSequelize = {
|
||||
authenticate: jest.fn(),
|
||||
define: jest.fn(),
|
||||
};
|
||||
|
||||
const actualSequelize = jest.requireActual('sequelize');
|
||||
return { Sequelize: jest.fn(() => mSequelize), DataTypes: actualSequelize.DataTypes };
|
||||
});
|
||||
|
||||
//mock out the database models
|
||||
jest.doMock('../../server/database/models', () => ({
|
||||
accounts: {
|
||||
findOne: () => null //can't find any (signup state)
|
||||
},
|
||||
|
||||
pendingSignups: {
|
||||
upsert: jest.fn(async record => {
|
||||
expect(record.email).toBe('email@example.com');
|
||||
expect(record.username).toBe('username');
|
||||
expect(record.hash).toBe('hashed-password');
|
||||
expect(record.contact).toBe(true);
|
||||
//token is a random UUID
|
||||
})
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
test('Basic valid signup attempt', async () => {
|
||||
//arguments
|
||||
const req = {
|
||||
body: {
|
||||
email: 'email@example.com',
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
contact: true
|
||||
}
|
||||
};
|
||||
|
||||
const res = {
|
||||
status: code => {
|
||||
expect(code).toBe(200);
|
||||
return {
|
||||
send: msg => expect(msg).toBe('Validation email sent!'),
|
||||
end: () => null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//test
|
||||
const route = require('../../server/auth/signup');
|
||||
|
||||
const result = await route(req, res);
|
||||
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -37,7 +37,6 @@ const TokenProvider = props => {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Authorization': `Bearer ${bearer}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
@@ -50,8 +49,7 @@ const TokenProvider = props => {
|
||||
const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*'
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: refreshToken
|
||||
@@ -91,8 +89,7 @@ const TokenProvider = props => {
|
||||
const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*'
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: refreshToken
|
||||
|
||||
Reference in New Issue
Block a user