Added discord auth support

This commit is contained in:
Akif9748 2022-09-17 19:33:51 +03:00
parent e1aeb8fd14
commit 598e48e25d
16 changed files with 115 additions and 12 deletions

View file

@ -1 +1,2 @@
MONGO_DB_URL = mongodb://localhost:27017/akf-forum MONGO_DB_URL = mongodb://localhost:27017/akf-forum
DISCORD_CLIENT = discord_app_id

3
.gitignore vendored
View file

@ -4,6 +4,9 @@ node_modules/
# Environment variables # Environment variables
.env .env
# Config files
config.json
# Test files # Test files
test.js test.js

View file

@ -4,11 +4,18 @@ A Node.js based forum software.
## Installation ## Installation
- Clone or download this repo. - Clone or download this repo.
- Run `npm i` to install **dependencies**. - Run `npm i` to install **dependencies**.
- Enter your database credentials in `.env`.
- Run `npm start` for run it. - Run `npm start` for run it.
### Extra ### Extra
Run `node util/reset` to **reset the database** for duplicate key errors, and run `node util/admin` for give admin perms to first member. Run `node util/reset` to **reset the database** for duplicate key errors, and run `node util/admin` for give admin perms to first member.
Edit `config.json` for default themes of users, and forum name... Edit `config.json` for default themes (`black` or `default`) of users, and forum name, meta description, character limits, discord auth enabler, global ratelimit.
### DISCORD AUTH:
`discord_auth: true` in config.json.
Enter application id to `.env`.
Create a redirect url in discord developer portal:
`https://forum_url.com/discord_auth/hash`
## API ## API
Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn about API in `APIDOCS.md`. Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn about API in `APIDOCS.md`.
@ -35,11 +42,12 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
| To do | Is done? | | To do | Is done? |
| ----- | -------- | | ----- | -------- |
| Profile Message | ⚪ | | Profile Message | ⚪ |
| Better Auth | ⚪ | | Better Auth for API way | ⚪ |
| mod role, permissions | ⚪ | | mod role, permissions | ⚪ |
| upload other photos, model for it | ⚪ | | upload other photos, model for it | ⚪ |
| categories page is need a update | ⚪ | | categories page is need a update, thread count in category | ⚪ |
| preview for send messages in markdown format | ⚪ | | preview for send messages in markdown format | ⚪ |
| DC auth will store code for taking tokens, and create secret model setting | ⚪ |
## Major Version History ## Major Version History
- V4: Caching - V4: Caching

View file

@ -12,5 +12,7 @@
"enabled": true, "enabled": true,
"max": 25, "max": 25,
"windowMs": 60000 "windowMs": 60000
} },
"discord_auth": true
} }

17
config.json.example Normal file
View file

@ -0,0 +1,17 @@
{
"def_theme": "default",
"forum_name": "akf",
"description": "Akf-forum!",
"limits": {
"title": 128,
"message": 1024,
"names": 25,
"desp": 256
},
"global_ratelimit": {
"enabled": true,
"max": 25,
"windowMs": 60000
},
"discord_auth": false
}

View file

@ -4,7 +4,7 @@ const { urlencoded: BP } = require('body-parser'),
SES = require('express-session'); SES = require('express-session');
const const
{ def_theme, forum_name, description, limits, global_ratelimit: RLS } = require("./config.json"), { def_theme, forum_name, description, limits, global_ratelimit: RLS, discord_auth } = require("./config.json"),
{ UserModel, BanModel } = require("./models"), { UserModel, BanModel } = require("./models"),
port = process.env.PORT || 3000, port = process.env.PORT || 3000,
mongoose = require("mongoose"), mongoose = require("mongoose"),
@ -50,4 +50,10 @@ for (const file of fs.readdirSync("./routes"))
app.all("*", (req, res) => res.error(404, "We have not got this page.")); app.all("*", (req, res) => res.error(404, "We have not got this page."));
app.listen(port, () => console.log(`${forum_name}-forum on port:`, port)); const server = app.listen(port, () => console.log(`${forum_name}-forum on port:`, port));
if (discord_auth) {
const { address } = server.address();
console.log(`https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT}&redirect_uri=http%3A%2F%2F${address == '::' ? 'localhost:' + port : address}%2Fdiscord_auth%2Fhash&response_type=token&scope=identify`)
app.set("discord_auth", `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT}&redirect_uri=http%3A%2F%2F${address == '::' ? 'localhost:' + port : address}%2Fdiscord_auth%2Fhash&response_type=token&scope=identify`);
}

View file

@ -2,6 +2,7 @@ const mongoose = require("mongoose")
const { def_theme } = require("../config.json"); const { def_theme } = require("../config.json");
const schema = new mongoose.Schema({ const schema = new mongoose.Schema({
id: { type: String, unique: true }, id: { type: String, unique: true },
discordID: { type: String, unique: true },
name: { type: String, maxlength: 25 }, name: { type: String, maxlength: 25 },
avatar: { type: String, default: "/images/avatars/default.jpg" }, avatar: { type: String, default: "/images/avatars/default.jpg" },
time: { type: Date, default: Date.now }, time: { type: Date, default: Date.now },

1
package-lock.json generated
View file

@ -18,6 +18,7 @@
"express-session": "^1.17.2", "express-session": "^1.17.2",
"mongoose": "^6.6.1", "mongoose": "^6.6.1",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"node-fetch": "^2.6.7",
"request-ip": "^3.3.0" "request-ip": "^3.3.0"
}, },
"engines": { "engines": {

View file

@ -34,6 +34,7 @@
"express-session": "^1.17.2", "express-session": "^1.17.2",
"mongoose": "^6.6.1", "mongoose": "^6.6.1",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"node-fetch": "^2.6.7",
"request-ip": "^3.3.0" "request-ip": "^3.3.0"
} }
} }

58
routes/discord_auth.js Normal file
View file

@ -0,0 +1,58 @@
const { Router } = require("express")
const { UserModel } = require("../models");
const fetch = require("node-fetch");
const app = Router();
app.get("/hash", (req, res) => res.send('<script>location.href=location.href.replace("#","?").replace("discord_auth/hash","discord_auth");</script>'))
app.get("/", async (req, res) => {
const { access_token, token_type } = req.query;
if (!access_token) return;
try {
const discord = await fetch('https://discord.com/api/users/@me', {
headers: { authorization: `${token_type} ${access_token}` }
}).then(res => res.json());
const forum = await UserModel.findOne({ discordID: discord.id });
if (req.user) {
if (req.user.discordID)
return res.error(403, "Your forum account is already linked to a discord account.");
if (forum)
return res.error(403, "This discord account is already linked to a forum account.");
req.user.discordID = discord.id;
await req.user.save();
return res.send("Your discord account has been linked to your forum account.");
}
if (forum) {
req.session.userID = forum.id;
return res.redirect("/");
}
let name = discord.username + discord.discriminator;
while (await UserModel.findOne({ name }))
name += Math.floor(Math.random() * 2);
const user2 = new UserModel({
name, discordID: discord.id,
avatar: `https://cdn.discordapp.com/avatars/${discord.id}/${discord.avatar}.png?size=256`
});
await user2.takeId();
await user2.save();
req.session.userID = user2.id;
res.redirect("/");
} catch (error) {
res.error(500, "Something went wrong");
console.error(error);
}
});
module.exports = app;

View file

@ -3,7 +3,7 @@ const { Router } = require("express");
const app = Router(); const app = Router();
const bcrypt = require("bcrypt"); const bcrypt = require("bcrypt");
app.get("/", (req, res) => res.reply("login", { redirect: req.query.redirect, user: null })); app.get("/", (req, res) => res.reply("login", { redirect: req.query.redirect, user: null, discord: req.app.get("discord_auth") }));
app.post("/", async (req, res) => { app.post("/", async (req, res) => {
req.session.userID = null; req.session.userID = null;

View file

@ -4,7 +4,7 @@ const bcrypt = require("bcrypt");
const rateLimit = require('express-rate-limit'); const rateLimit = require('express-rate-limit');
const app = Router(); const app = Router();
app.get("/", (req, res) => res.reply("register", { user: null })); app.get("/", (req, res) => res.reply("register", { user: null, discord: req.app.get("discord_auth") }));
app.post("/", rateLimit({ app.post("/", rateLimit({
windowMs: 24 * 60 * 60_000, max: 5, standardHeaders: true, legacyHeaders: false, windowMs: 24 * 60 * 60_000, max: 5, standardHeaders: true, legacyHeaders: false,

View file

@ -27,7 +27,7 @@ app.get("/:id", async (req, res) => {
const message = await MessageModel.count({ authorID: id }); const message = await MessageModel.count({ authorID: id });
const thread = await ThreadModel.count({ authorID: id }); const thread = await ThreadModel.count({ authorID: id });
res.reply("user", { member, counts: { message, thread } }) res.reply("user", { member, counts: { message, thread}, discord: req.app.get("discord_auth") })
} }
else res.error(404, `We don't have any user with id ${id}.`); else res.error(404, `We don't have any user with id ${id}.`);

View file

@ -7,6 +7,7 @@
<link rel="stylesheet" href="/css/login.css" /> <link rel="stylesheet" href="/css/login.css" />
<%- include("extra/navbar") %> <%- include("extra/navbar") %>
<a href="<%=discord%>" class="btn-outline-primary">Login with discord</a>
<h1 class="title">Login</h1> <h1 class="title">Login</h1>

View file

@ -8,9 +8,11 @@
<link rel="stylesheet" href="/css/login.css" /> <link rel="stylesheet" href="/css/login.css" />
<%- include("extra/navbar") %> <%- include("extra/navbar") %>
<a href="<%=discord%>" class="btn-outline-primary">Login with discord</a>
<h1 class="title">Register</h1> <h1 class="title">Register</h1>
<form action="/register" method="post"> <form action="/register" method="post">
<input type="text" name="username" maxlength="25" placeholder="Username" class="input" required> <input type="text" name="username" maxlength="25" placeholder="Username" class="input" required>
<input type="password" name="password" maxlength="25" placeholder="Password" class="input" required> <input type="password" name="password" maxlength="25" placeholder="Password" class="input" required>

View file

@ -13,7 +13,9 @@
<div class="content"> <div class="content">
<% if (!member.discordID && user?.id === member.id) { %>
<a href="<%=discord%>" class="btn-outline-primary">Login with discord</a>
<% } %>
<% if (user?.admin || user?.id === member.id) { %> <% if (user?.admin || user?.id === member.id) { %>
<a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a> <a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a>
<link rel="stylesheet" href="/css/modal.css" /> <link rel="stylesheet" href="/css/modal.css" />