Skip to content

Commit

Permalink
Connect/Disconnect Discord account
Browse files Browse the repository at this point in the history
  • Loading branch information
geneotech committed Jun 6, 2024
1 parent b436870 commit 49ce30d
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 10 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,7 @@ SESSION_SECRET="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
DB_PATH="./private/mmr.db"
REPORT_MATCH_APIKEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
SERVER_LIST_JSON="https://hypersomnia.xyz:8420/server_list_json"
DISCORD_CLIENT_SECRET="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
DISCORD_CLIENT_ID="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
DISCORD_BOT_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
```
4 changes: 2 additions & 2 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ if (!fs.existsSync(dbPath)) {

db.run(`
CREATE TABLE IF NOT EXISTS associations (
discord_id TEXT UNIQUE,
steam_id TEXT UNIQUE
child_id TEXT UNIQUE,
parent_id TEXT UNIQUE
)
`);
});
Expand Down
16 changes: 16 additions & 0 deletions public/assets/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,22 @@ footer > div:nth-child(3) > a:hover {
height: 24px;;
}

.btn .btn-disconnect {
color: #ff4d4d;
border: 1px solid #ff4d4d;
}

.btn .btn-disconnect:hover {
background: rgba(255, 77, 77, 0.5);
color: #fff;
}

.notice {
font-size: 0.85em;
color: #e7e7e7;
margin-top: 10px;
}

input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
Expand Down
97 changes: 97 additions & 0 deletions src/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ const fs = require('fs');
const UAParser = require('ua-parser-js');
const path = `${__dirname}/../private/users.json`;

const Database = require('better-sqlite3');
const axios = require('axios');
const querystring = require('querystring');

const dbPath = process.env.DB_PATH;

const discordClientId = process.env.DISCORD_CLIENT_ID;
const discordClientSecret = process.env.DISCORD_CLIENT_SECRET;
const redirectUri = process.env.URL + 'auth/discord/return';

function loadUsers() {
try {
const d = fs.readFileSync(path, 'utf8');
Expand Down Expand Up @@ -60,5 +70,92 @@ module.exports = function(passport) {
}
);

// Route to initiate Discord OAuth
router.get('/discord', (req, res) => {
const authUrl = `https://discord.com/api/oauth2/authorize?client_id=${discordClientId}&redirect_uri=${redirectUri}&response_type=code&scope=identify`;
res.redirect(authUrl);
});

// Route to disconnect Discord account
router.post('/discord/disconnect', async (req, res) => {
const db = new Database(dbPath);
const steamId = 'steam_' + req.user.id;

try {
db.prepare('DELETE FROM associations WHERE parent_id = ?').run(steamId);
res.redirect('/profile');
} catch (error) {
console.error('Error disconnecting Discord account:', error.message);
res.redirect('/profile');
}
});

// Discord OAuth callback
router.get('/discord/return', async (req, res) => {
const { code } = req.query;

if (!code) {
return res.redirect('/profile');
}

try {
// Exchange code for access token
const tokenResponse = await axios.post('https://discord.com/api/oauth2/token', querystring.stringify({
client_id: discordClientId,
client_secret: discordClientSecret,
grant_type: 'authorization_code',
code,
redirect_uri: redirectUri
}), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});

const { access_token } = tokenResponse.data;

// Get user information from Discord
const userResponse = await axios.get('https://discord.com/api/users/@me', {
headers: {
Authorization: `Bearer ${access_token}`
}
});

const discordUser = userResponse.data;

await axios.post('https://discord.com/api/oauth2/token/revoke', querystring.stringify({
token: access_token,
client_id: discordClientId,
client_secret: discordClientSecret
}), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});

// Store the association in the database
const db = new Database(dbPath);

const discordId = 'discord_' + discordUser.id;
const steamId = 'steam_' + req.user.id;

console.log("d: " + discordUser.id + " s: " + steamId);

const existingAssociation = db.prepare('SELECT * FROM associations WHERE child_id = ?').get(discordId);

if (!existingAssociation) {
db.prepare('INSERT OR REPLACE INTO associations (child_id, parent_id) VALUES (?, ?)').run(discordId, steamId);
} else {
console.log('Association already exists for Discord ID: ' + discordUser.id);
}

// Redirect to profile
res.redirect('/profile');
} catch (error) {
console.error(error.message);
res.redirect('/profile');
}
});

return router;
};
28 changes: 26 additions & 2 deletions src/profile.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
const express = require('express');
const router = express.Router();
const Database = require('better-sqlite3');
const axios = require('axios');

const dbPath = process.env.DB_PATH;

router.get('/', async (req, res) => {
const db = new Database(dbPath);
const steamId = 'steam_' + req.user.id;
const association = db.prepare('SELECT child_id FROM associations WHERE parent_id = ?').get(steamId);

let discordData = null;
if (association) {
const discordId = association.child_id.split('_')[1];
try {
const response = await axios.get(`https://discord.com/api/users/${discordId}`, {
headers: {
'Authorization': `Bot ${process.env.DISCORD_BOT_TOKEN}`
}
});
discordData = response.data;
} catch (error) {
console.error('Error fetching Discord user data:', error.message);
}
}

router.get('/', function (req, res) {
res.render('profile', {
page: 'Profile',
user: req.user
user: req.user,
discordData
});
});

Expand Down
48 changes: 42 additions & 6 deletions views/profile.ejs
Original file line number Diff line number Diff line change
@@ -1,17 +1,53 @@
<%- include('overall_header'); %>

<h1><%= user.displayName %></h1>
<form method="POST" action="/logout">
<div class="btn-group">
<div class="btn">
<button type="submit" aria-label="Logout">Logout</span></button>
<button type="submit" aria-label="Logout">Logout</button>
</div>
</div>
</form>
<img class="avatar" src="<%= user.photos[2].value %>" alt="avatar" width="184" height="184">
<h1><i class="fa-brands fa-steam"></i> Steam</h1>
<a href="https://steamcommunity.com/profiles/<%= user.id %>">
<img class="avatar" src="<%= user.photos[2].value %>" alt="avatar" width="184" height="184">
</a>
<h3><a href="https://steamcommunity.com/profiles/<%= user.id %>"><%= user.displayName %></a></h3>
<dl>
<dt>Steam ID</dt>
<dd><span class="tocopy"><code><%= user.id %></code><button class="copy"><i class="fa-solid fa-copy"></i></button></span></dd>
<dd><strong>Steam ID:</strong> <span class="tocopy"><code><%= user.id %></code><button class="copy"><i class="fa-solid fa-copy"></i></button></span></dd>

<h1><i class="fa-brands fa-discord"></i> Discord</h1>
<% if (discordData) { %>
<a href="https://discord.com/users/<%= discordData.id %>">
<img class="avatar" src="https://cdn.discordapp.com/avatars/<%= discordData.id %>/<%= discordData.avatar %>.png" alt="discord avatar" width="184" height="184">
</a>
<h3><a href="https://discord.com/users/<%= discordData.id %>"><%= discordData.username %>#<%= discordData.discriminator %></a></h3>
<dd><strong>Discord ID:</strong> <span class="tocopy"><code><%= discordData.id %></code><button class="copy"><i class="fa-solid fa-copy"></i></button></span></dd>
<dd>
<form action="/auth/discord/disconnect" method="post">
<div class="btn-group">
<div class="btn">
<button type="submit" class="btn-disconnect" aria-label="Disconnect Discord Account"><i class="fa-brands fa-discord"></i> Disconnect Discord Account</button>
</div>
</div>
</form>
</dd>
<dd class="notice">
Once you disconnect, your Steam and Discord accounts will exist separately again.<br>No ranks or MMR will be lost.
</dd>
<% } else { %>
<dd>
<form action="/auth/discord" method="get">
<div class="btn-group">
<div class="btn">
<button type="submit" aria-label="Connect Discord Account"><i class="fa-brands fa-discord"></i> Connect Discord Account</button>
</div>
</div>
</form>
</dd>
<dt>Matches played with Discord account<br>will count towards your Steam account.</dt>
<dd class="notice">
No merging ever takes place.<br>You may later disconnect the two accounts without losing any progress.</dd>
<% } %>
</dl>

<%- include('overall_footer'); %>

0 comments on commit 49ce30d

Please sign in to comment.