The Discord User Manager allows users to join your Discord server by authenticating with an OAuth 2.0 provider such as Google or Microsoft. This is useful if your school or work is using G Suite from Google or Office 365 from Microsoft and you want to restrict the members of your Discord server to the members of your organization or domain. This allows you to have more control over who is able to join your Discord server without having to manage Discord invites.
The Discord User Manager is built with JavaScript using Express and the Pug template engine with Bootstrap for HTML, CSS, and JavaScript framework. The Discord User Manager project has a dependency on the following packages:
Package | Semantic Version | Description |
---|---|---|
bcrypt | ^5.0.0 | Pasword hashing used for logging into local accounts. Passwords for OAuth accounts are not stored locally. |
connect-flash | ^0.1.1 | Used for passing messages between http requests. Flash messages use session storage to persists messages between http requests. |
connect-session-sequelize | ^6.0.0 | SQL Session store using Sequelize as a storage backend. |
cookie-parser | ~1.4.4 | Parse the Cookie header and populate req.cookies property with an object keyed by the cookie name. |
debug | ~2.6.9 | A JavaScript debugging utility. |
discord.js | ^12.3.1 | A powerful library for interacting with the Discord API. |
dotenv | ^8.1.0 | Loads environment variables from a .env file into the process.env global variable. |
express | ~4.16.1 | A fast, unopinionated, minimalist web framework for Node.js. |
express-session | ^1.16.2 | Express middleware which will populate the req.session object. |
helmet | ^3.21.0 | Helps to secure your Express web application. |
http-errors | ~1.6.3 | Used to create HTTP error responses (for unmatched route handling for example). |
jsonwebtoken | ^8.5.1 | Impelements JSON Web Token. |
morgan | ~1.9.1 | HTTP request logger middleware for Node.js |
passport | ^0.4.0 | Passport is Express-compatible authentication middleware for Node.js. |
passport-discord | ^0.1.3 | Passport strategy for authentication with Discord through the OAuth 2.0 API. |
passport-google-oauth20 | ^2.0.0 | Passport strategy for authenticating with Google using the OAuth 2.0 API. |
passport-local | ^1.0.0 | Passport strategy for authenticating with username and password. |
pug | ^3.0.0 | High performance template engine. |
sequelize | ^5.18.4 | A promise-based Node.js ORM for Postgres, MySQL, MariaDB, SQLite, and Microsoft SQL Server. |
sqlite3 | ^5.0.0 | Asynchronous, non-blocking SQLite3 bindings for Node.js. |
In addition to the regular dependencies, the Discord User Manager also has the following development dependecies.
Package | Semantic Version | Description |
---|---|---|
@types/jest | ^24.0.19 | Type definitinos for Jest. |
browser-sync | ^2.26.7 | Automatically syncronize changes to multiple devices during development. |
connect-browser-sync | ^2.1.0 | Injects the necessary BrowserSync <script> tags into the HTML pages. |
eslint | ^6.3.0 | ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. |
eslint-config-prettier | ^6.11.0 | Turns off all rules that are unnecessary or might conflict with Prettier. |
eslint-plugin-jest | ^22.19.0 | ESLint plugin for Jest. |
eslint-plugin-jquery | ^1.5.1 | Adds support for JQuery in ESLint |
eslint-plugin-prettier | ^3.1.4 | Runs Prettier as an ESLint rule and reports differences as individual ESLint issues. |
jest | ^24.9.0 | A JavaScript testing framework. |
nodemon | ^1.19.2 | A tool that automatically restarts the Node.JS application when file changes are detected. |
open | ^7.2.0 | Open stuff like URLs, files, executables. |
prettier | ^2.1.0 | Opinionated Code Formatter. |
sequelize-cli | ^5.5.1 | The Sequelize Command Line Interface (CLI). |
stoppable | ^1.1.0 | Node's server.close() - the way you probably expected it to work by default. |
yargs | ^14.0.0 | Build interactive command line tools by parsing arguments and generating an elegant user interface. |
Discord User Manager requires Node.js (version 10 or higher) in order to be installed.
Either download the source from https://github.com/jpvanoosten/discord-user-manager or install it from the command line:
git clone https://github.com/jpvanoosten/discord-user-manager.git
Make sure the current working directory is set to the directory where the repo was cloned to:
cd discord-user-manager
Install package dependencies:
npm install
or if using Yarn:
yarn
The Discord User Manager uses environment variables to configure a lot of functionality. The .env.example file contains all of the environment variables used by the Discord User Manager.
The following table describes the configurable environment varaibles.
Variable | Default Value | Description |
---|---|---|
NODE_ENV | development | When running in production, make sure the value of this variable is production. When running in development, some additional features (such as debug logging and browser sync) will be available. |
PORT | 3000 | What port the Express server should listen on. |
DEBUG | discord-user-manager:* | Conrols what debug messages are logged. |
USE_BROWSER_SYNC | false | Whether BrowserSync should be enabled or not. |
ADMIN_USERNAME | admin | The username for the default administrator account. |
ADMIN_PASSWORD | The password for the default administrator account. | |
SESSION_SECRET | The secret used to sign the session ID cookie. | |
GOOGLE_CLIENT_ID | OAuth 2.0 Client ID from the Google Developer Console. | |
GOOGLE_CLIENT_SECRET | OAuth 2.0 Cient Secret from the Google Developer Console. | |
GOOGLE_REDIRECT_URI | http://localhost:3000/google/callback | The path in your application that users are redirected to after they have authenticated with Google. This is configured in the Google Developer Console Credentials. |
DISCORD_CLIENT_ID | OAuth 2.0 Client ID from the Discord Developer Portal. | |
DISCORD_CLIENT_SECRET | OAuth 2.0 Client Secret from the Discord Developer Portal. | |
DISCORD_REDIRECT_URI | http://localhost:3000/discord/callback | The path in your appliation that users are redirected to after they have authenticated with Discord. |
DISCORD_BOT_TOKEN | The unique token used to authenticate the Bot with the DiscordJS client. | |
DISCORD_SERVER_ID | The unique identifier for the Discord server. | |
TEST_USER_DISCORD_ID | The unique identifer for a Discord user. (Only used for testing). | |
TEST_USER_ACCESS_TOKEN | An access token for a Discord user. (Only used for testing). |
The Discord User Manager server can either be manually configured by copying the .env.example
file to .env
and modifying the environment varaibles, or you can run the configure
command to auto-configure many of the required environment variables. To auto-generate the .env
file based on the .env.example file, run the following command:
npm run configure
or
yarn configure
The configure
command will configure the .env
file with automatically generated passwords and secrets based on the contents of the .env.example file.
If the .env
file already exsits in the root directory of the project, then the configure
command will not overwrite the file. If you want to force the .env
file to be overwritten, then you can pass the aditional --force
arguments:
npm run configure --force
or
yarn configure --force
NOTE: The
.env
file contains sensitive secret information that could allow administrator access to your user's personal information and your Discord Bot. Never commit the.env
file into version control and never use the .env.example file to store secret information since the .env.example file is part of version control.
In order to allow your users to authenticate with Google, you must first create a Google Cloud Project in the Google Developer Console.
In order to use OAuth 2.0 to allow users to login to your application, you need to configure a Web application for the Google Cloud Project.
Give the Web application a Name. This is the name that will appear on the OAuth 2.0 login screen.
Under the Authorised redirect URIs section, click the + ADD URI button to add a redirect URI to be used with requests from a web server.
In order to allow for testing the application on a locally running server, you must add http://localhost:3000/google/callback
redirect URI.
Also add the redirect URI that is specified in the GOOGLE_REDIRECT_URI
configuration environment variable specified in your .env
file in your production environment.
Click the CREATE button on the bottom of the page to create the OAuth client credentials.
GOOGLE_CLIENT_ID
variable in the .env
file (never commit this value to version control).GOOGLE_CLIENT_SECRET
variable in the .env
file (never commit this value to version control).In order to allow users to log into Discord and join the Discord server, you must create a Discord application and add a Discord bot in the Discord Developer Portal.
DISCORD_CLIENT_ID
variable in the .env
file (never commit this value to version control).DISCORD_CLIENT_SECRET
variable in the .env
file (never commit this value to version control).http://localhost:3000/discord/callback
as a redirect URI.DISCORD_REDIRECT_URI
environment variable in the .env
file in your production environment.A Discord Bot is a privileged user that can perform certain actions within your Discord server on your behalf.
DISCORD_BOT_TOKEN
environment variable in the .env
file.The Server Members Intent is required to allow your bot to receive the
guildMemberAdd
, andguildMemberRemove
events. These are considered privilaged events which requires this option to be enabled. See https://discord.com/developers/docs/topics/gateway#gateway-intents for more information.
Before the Discord User Manager application can start working to manage the users in your Discord Server, you need to add the Discord application and bot that you created in the previous step to your Discord server.
The Discord User Manager bot has now been added to your server and is able to perform user management tasks for you.
The bot may appear offline but this is normal if the Discord User Manager application is not yet running.
Now that the Discord bot has been added to your Discord server, a few more settings need to be configured on the Discord User Manager server.
Enabling the Developer Mode option will allow you to copy the server ID, user ID, channel ID, and message ID from the Discord application (even when using the desktop application).
DISCORD_SERVER_ID
in the .env
file.To start the Discord User Manager application, open a terminal and navigate to the root folder where you cloned the discord-user-manager git repository and run the following command:
npm run start
Or if using Yarn:
yarn start
If the service starts correctly, you should see Listening on port 3000
printed to the console.
Open your browser and navigate to http://localhost:3000.
If everything worked correctly, you should see the home page for the Discord User Manager application (as shown in the image above).
If you return to your Discord server, you should see that the Discord User Manager bot is online.
To see if the bot responds to commands, type !ping
in one of the text channels of your server (make sure the bot can read the messages of the channel you use to type bot commands).
If the bot is running correctly, it should reply with Pong.
printed to the same text channel.
If you want to see a list of possible commands the bot can respond to, use the !help
bot command. The bot should respond with a direct message that contains list of bot commands that it knows about.
You can create custom bot commands by adding a JavaScript file to the discord/commands folder. The file should export an object with the following properties:
Property | Type | Required | Description |
---|---|---|---|
name | string | ✔ | The name that is used to identify the command. |
description | string | A brief description of the command. Used to diplay documentation using the !help bot command. |
|
aliases | string[] | An array of aliases for the command. | |
usage | string | A list of expected arguments for the command. | |
args | boolean | Set to true if this command expects arguments. If true and no arguments are provided, an error is generated displaying the usage string. |
|
cooldown | number | The time in seconds that the command can be exectued again. | |
guildOnly | boolean | true if this command can only be executed in the context of a guild channel. If true , an error is generated if the command is executed in a direct message to the bot. |
|
permissions | Discord.PermissionResolvable[] | An array of permission flags that the user executing the command must have. See https://discord.js.org/#/docs/main/stable/class/Permissions?scrollTo=s-FLAGS | |
execute | function (message: Discord.Message, args: string[]) | ✔ | The function that is executed when the command is received by the bot. The message parameter is the Discord message that was received. The args parameter are any additional arguments that were provided in the message. If the command doesn't take any additional arguments, the args parameter can be omitted. |
An example command file looks like this:
// Error messages can be logged to a debug stream.
const debug = require("debug")("discord-user-manager:discord:commands:kick");
// The DiscordJS API
const discord = require("discord.js");
module.exports = {
// The command name (excluding the command prefix).
name: "kick",
// Command aliases.
aliases: ["k"],
// A description of the command.
description: "Kick a user from the Discord guild server.",
// Does this command expect any additional arguments?
args: true,
// Specify the expected arguments.
usage: "<guildUser>",
// The user executing the command must have the following permissions.
permissions: [discord.Permissions.FLAGS.KICK_MEMBERS],
// Can this command only be run in the context of a guild channel?
guildOnly: true,
// Elapsed time (in seconds) between subsequent execution of the command.
cooldown: 1,
// The function that executes the command.
execute: async (message) => {
if (!message.mentions.users.size) {
return message.reply("No user mentioned in kick command.");
}
const user = message.mentions.users.first();
if (user === message.author) {
return message.reply("You can't kick yourself.");
}
const member = message.guild.member(user);
if (member) {
try {
await member.kick("Kicked by bot kick command.");
message.reply(`Successfully kicked ${user.tag}`);
} catch (err) {
debug(err);
message.reply(`Unable to kick ${user.tag}`);
}
} else {
message.reply(`User ${user.tag} is not a guild member.`);
}
},
};
// TODO: This section still needs to be filled in.
Jest is used to perform unit testing on the server. Some of the test cases require a valid Discord user in order to run. A valid OAuth 2.0 access token is required for the test user. Use the utils/getDiscordOAuthToken.js
script to populate the TEST_USER_ACCESS_TOKEN
and TEST_USER_DISCORD_ID
values in the .env
file.
Note: If the
TEST_USER_DISCORD_ID
andTEST_USER_ACCESS_TOKEN
keys are not in the.env
file, nothing will be replaced. Please make sure these keys exist in the.env
file before running this script.
Using the terminal:
node utils/getDiscordOAuthToke.js
Using npm:
npm run oauth:discord
Using Yarn:
yarn oauth:discord
To run the unit tests for the Discord bot, open a terminal in the root folder of the project and use the following command:
npm run test
or if you are using yarn:
yarn test
If all tests pass, you should see the following output in the terminal:
PASS discord/DiscordAdapter.test.js
√ resolve welcome channel (2ms)
√ resolve default role (1ms)
√ can add user to role (599ms)
√ add user to invalid role (30ms)
√ add invalid user to role (1ms)
√ welcome URL
√ add existing user (168ms)
√ add invalid user
√ remove invalid user (1ms)
√ send log to channel (1ms)
----------------------------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------------------------------|----------|----------|----------|----------|-------------------|
All files | 50.88 | 23.04 | 54.55 | 51.02 | |
discord-user-manager | 100 | 100 | 100 | 100 | |
settings.js | 100 | 100 | 100 | 100 | |
discord-user-manager/config | 100 | 100 | 100 | 100 | |
config.js | 100 | 100 | 100 | 100 | |
discord-user-manager/discord | 59.92 | 31.4 | 67.57 | 59.69 | |
DiscordAdapter.js | 59.77 | 31.4 | 67.57 | 59.53 |... 45,646,648,651 |
config.js | 100 | 100 | 100 | 100 | |
discord-user-manager/discord/commands | 13.95 | 0 | 0 | 14.46 | |
args-info.js | 16.67 | 0 | 0 | 16.67 | 6,7,8,9,12 |
help.js | 9.76 | 0 | 0 | 10.53 |... 69,70,72,73,76 |
kick.js | 18.75 | 0 | 0 | 18.75 |... 26,27,29,30,33 |
ping.js | 50 | 100 | 0 | 50 | 7 |
prune.js | 14.29 | 0 | 0 | 14.29 |... 33,35,37,39,40 |
discord-user-manager/discord/reactions | 20 | 0 | 0 | 20 | |
add_role.js | 20 | 0 | 0 | 20 |... 36,37,38,40,43 |
discord-user-manager/models | 95.83 | 66.67 | 100 | 95.83 | |
index.js | 95 | 66.67 | 100 | 95 | 13 |
user.js | 100 | 100 | 100 | 100 | |
----------------------------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 10 passed, 10 total
Snapshots: 0 total
Time: 4.33s
Ran all test suites.
Some common reasons why some of the tests fail: