Add authentication

This commit is contained in:
Isaac 2022-05-06 21:17:53 +01:00
parent 425e7ab151
commit a1823a750e
8 changed files with 105 additions and 5 deletions

View File

@ -1,5 +1,7 @@
CONFIG_PATH=./user/config.yml
DISCORD_SECRET=
DISCORD_TOKEN=
DB_ENCRYPTION_KEY=
DB_CONNECTION_URL=""
HTTP_BIND=3000
HTTP_EXTERNAL=

View File

@ -32,6 +32,9 @@
"dependencies": {
"@eartharoid/dbf": "^0.0.1",
"@eartharoid/dtf": "^2.0.1",
"@fastify/cookie": "^6.0.0",
"@fastify/jwt": "^5.0.1",
"@fastify/oauth2": "^5.0.0",
"@prisma/client": "^3.13.0",
"discord.js": "^13.6.0",
"dotenv": "^16.0.0",
@ -40,6 +43,7 @@
"leeks.js": "^0.2.4",
"leekslazylogger": "^4.1.7",
"node-dir": "^0.1.17",
"node-fetch": "2",
"semver": "^7.3.7",
"terminal-link": "^2.1.1",
"yaml": "^1.10.2"

View File

@ -1,9 +1,50 @@
const fastify = require('fastify')();
const oauth = require('@fastify/oauth2');
const { randomBytes } = require('crypto');
const { short } = require('leeks.js');
const { join } = require('path');
const { readFiles } = require('node-dir');
module.exports = client => {
// oauth2 plugin
fastify.register(oauth, {
callbackUri: `${process.env.HTTP_EXTERNAL}/auth/callback`,
credentials: {
auth: oauth.DISCORD_CONFIGURATION,
client: {
id: client.user.id,
secret: process.env.DISCORD_SECRET,
},
},
name: 'discord',
scope: ['identify'],
startRedirectPath: '/auth/login',
});
// cookies plugin
fastify.register(require('@fastify/cookie'));
// jwt plugin
fastify.register(require('@fastify/jwt'), {
cookie: {
cookieName: 'token',
signed: false,
},
secret: randomBytes(16).toString('hex'),
});
// auth
fastify.decorate('authenticate', async (req, res) => {
try {
const data = await req.jwtVerify();
if (data.payload.expiresAt < Date.now()) res.redirect('/auth/login');
} catch (err) {
res.send(err);
}
});
// logging
fastify.addHook('onResponse', (req, res, done) => {
done();
const status = (res.statusCode >= 500
@ -22,8 +63,12 @@ module.exports = client => {
? '&e'
: '&a') + response_time + 'ms';
client.log.info.http(short(`${req.ip} ${req.method} ${req.routerPath ?? '*'} &m-+>&r ${status}&b in ${response_time}`));
done();
});
fastify.addHook('onError', async (req, res, err) => client.log.error.http(err));
// route loading
const dir = join(__dirname, '/routes');
readFiles(dir,
@ -43,12 +88,15 @@ module.exports = client => {
.replace('/index', '') || '/'; // remove index
const route = require(file);
Object.keys(route).forEach(method => fastify[method](path, {
Object.keys(route).forEach(method => fastify.route({
config: { client },
...route[method],
method: method.toUpperCase(),
path,
...route[method](fastify),
})); // register route
}
// start server
fastify.listen(process.env.HTTP_BIND, (err, addr) => {
if (err) client.log.error.http(err);
else client.log.success.http(`Listening at ${addr}`);

1
src/lib/http.js Normal file
View File

@ -0,0 +1 @@
module.exports.domain = process.env.HTTP_EXTERNAL.match(/http(s?):\/\/(?<domain>[a-zA-Z0-9\-_.]+)(:\d+)?/).groups.domain;

View File

@ -0,0 +1,7 @@
module.exports.get = fastify => ({
handler: (req, res) => {
const { client } = res.context.config;
return client.guilds.cache.get(req.params.guild);
},
onRequest: [fastify.authenticate],
});

View File

@ -0,0 +1,9 @@
module.exports.get = fastify => ({
handler: async (req, res) => {
const { client } = res.context.config;
const user = await client.users.fetch(req.user.payload.id);
console.log(req.user.payload.username, user?.tag);
res.send(client.guilds.cache);
},
onRequest: [fastify.authenticate],
});

View File

@ -0,0 +1,29 @@
const fetch = require('node-fetch');
const { domain } = require('../../lib/http');
module.exports.get = () => ({
handler: async function (req, res) { // must NOT use arrow function syntax
const {
access_token, expires_in,
} = await this.discord.getAccessTokenFromAuthorizationCodeFlow(req);
const user = await (await fetch('https://discordapp.com/api/users/@me', { headers: { 'Authorization': `Bearer ${access_token}` } })).json();
const payload = {
avatar: `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`,
discriminator: user.discriminator,
expiresAt: Date.now() + (expires_in * 1000),
id: user.id,
username: user.username,
};
const token = this.jwt.sign({ payload });
res
.setCookie('token', token, {
domain: domain,
httpOnly: true,
path: '/',
sameSite: true,
secure: false,
})
.redirect('/');
},
});

View File

@ -1,6 +1,6 @@
module.exports.get = {
module.exports.get = () => ({
handler: (req, res) => {
const { client } = res.context.config;
return `Hello, I am ${client.user.username}!`;
},
};
});