Skip to content
This repository has been archived by the owner on Sep 13, 2022. It is now read-only.

OAuth2 integration for Discord #21

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,11 @@ config/*
/static/dist/
package-lock.json
index.php

#oauth2
oauth2/config/*
!oauth2/config/config.ini.example
oauth2/__pycache__
oauth2/env
oauth2/oauth2.sock
oauth2/server_info.json
34 changes: 34 additions & 0 deletions oauth2/bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-

import asyncio
import discord
import json
import os
from oauth2 import get_path, get_args

args = get_args()

class Bot(discord.Client):

async def on_ready(self):
await self.change_presence(status=discord.Status.invisible)
for server in self.servers:
for role in server.role_hierarchy:
if role.name == args.verified_role:
verified_role = role
with open(get_path('server_info.json')) as server_file:
user_info = json.load(server_file)
for member in self.get_all_members():
if member.id == user_info['active']['id']:
if member.top_role >= verified_role:
user_info[member.id]['verified'] = True
if 'authorized' not in user_info:
user_info['authorized'] = [user_info['active']['recent_ip']]
else:
user_info['authorized'].append(user_info['active']['recent_ip'])
else:
user_info[member.id]['verified'] = False
with open(get_path('server_info.json'), 'w') as server_file:
json.dump(user_info, server_file, indent=4)
await self.close()
6 changes: 6 additions & 0 deletions oauth2/config/config.ini.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
OAUTH2_CLIENT_ID: ##bot client id
OAUTH2_CLIENT_SECRET: ##bot client secret
OAUTH2_REDIRECT_URI: ##bot redirect uri
bot_token: ##bot token
verified_role: ##minimum role for access (default=@everyone)
two-factor_auth: ##whether 2FA is required for access or not (default=False)
158 changes: 158 additions & 0 deletions oauth2/oauth2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/python3

import os
import sys
import asyncio
import json
import subprocess
import configargparse
from datetime import datetime
from flask import Flask, g, session, redirect, request, url_for, jsonify
from requests_oauthlib import OAuth2Session
from bot import Bot, get_path

def get_path(path):
if not os.path.isabs(path):
path = os.path.join(os.path.dirname(__file__), path)
return path


def get_args():
if '-cf' not in sys.argv and '--config' not in sys.argv:
config_files = [get_path('config/config.ini')]
parser = configargparse.ArgParser(default_config_files=config_files)
parser.add_argument('-cf', '--config', is_config_file=True,
help='Configuration file')
parser.add_argument('-ocid', '--OAUTH2_CLIENT_ID', type=str, required=True)
parser.add_argument('-ocs', '--OAUTH2_CLIENT_SECRET', type=str, required=True)
parser.add_argument('-oru', '--OAUTH2_REDIRECT_URI', type=str, required=True)
parser.add_argument('-token', '--bot_token', type=str, required=True)
parser.add_argument('-role', '--verified_role', type=str, default='@everyone')
parser.add_argument('-2fa', 'two-factor_auth', action='store_true', default=False)
parser.add_argument('--workers', type=int)
parser.add_argument('--bind', type=str)
parser.add_argument('-m', type=str)
parser.add_argument('wsgi:app', type=str)

args = parser.parse_args()

return args

args = get_args()

OAUTH2_CLIENT_ID = args.OAUTH2_CLIENT_ID
OAUTH2_CLIENT_SECRET = args.OAUTH2_CLIENT_SECRET
OAUTH2_REDIRECT_URI = args.OAUTH2_REDIRECT_URI
bot_token = args.bot_token

API_BASE_URL = os.environ.get('API_BASE_URL', 'https://discordapp.com/api')
AUTHORIZATION_BASE_URL = API_BASE_URL + '/oauth2/authorize'
TOKEN_URL = API_BASE_URL + '/oauth2/token'

app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = OAUTH2_CLIENT_SECRET

if 'http://' in OAUTH2_REDIRECT_URI:
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = 'true'


def token_updater(token):
session['oauth2_token'] = token


def make_session(token=None, state=None, scope=None):
return OAuth2Session(
client_id=OAUTH2_CLIENT_ID,
token=token,
state=state,
scope=scope,
redirect_uri=OAUTH2_REDIRECT_URI,
auto_refresh_kwargs={
'client_id': OAUTH2_CLIENT_ID,
'client_secret': OAUTH2_CLIENT_SECRET,
},
auto_refresh_url=TOKEN_URL,
token_updater=token_updater)


@app.route('/login')
def index():
scope = request.args.get(
'scope',
'identify guilds')
discord = make_session(scope=scope.split(' '))
authorization_url, state = discord.authorization_url(AUTHORIZATION_BASE_URL)
session['oauth2_state'] = state
return redirect(authorization_url)


@app.route('/login/callback')
def callback():
if request.values.get('error'):
return request.values['error']
discord = make_session(state=session.get('oauth2_state'))
token = discord.fetch_token(
TOKEN_URL,
client_secret=OAUTH2_CLIENT_SECRET,
authorization_response=request.url)
session['oauth2_token'] = token
return redirect(url_for('.me'))


@app.route('/login/me', methods=["GET"])
def me():
discord = make_session(token=session.get('oauth2_token'))
user = discord.get(API_BASE_URL + '/users/@me').json()
guilds = discord.get(API_BASE_URL + '/users/@me/guilds').json()

with open(get_path('server_info.json')) as server_file:
data = json.load(server_file)

try:
if user['id'] not in data:
data[user['id']] = {'username': user['username'],
'recent_ip': request.headers["X-Forwarded-For"].split(',')[0]}
if args.two-factor_auth is True:
data[user['id']]['mfa_enabled'] = user['mfa_enabled']
data['active'] = data[user['id']]
data['active']['id'] = user['id']
with open(get_path('server_info.json'), 'w') as server_file:
json.dump(data, server_file, indent=4)
else:
data[user['id']]['last_login'] = str(datetime.today())
data[user['id']]['recent_ip'] = request.headers["X-Forwarded-For"].split(',')[0]
if args.two-factor_auth is True:
data[user['id']]['mfa_enabled'] = user['mfa_enabled']
data['active'] = data[user['id']]
data['active']['id'] = user['id']
with open(get_path('server_info.json'), 'w') as server_file:
json.dump(data, server_file, indent=4)

loop = asyncio.new_event_loop()
client = Bot(loop=loop)
asyncio.set_event_loop(loop)
client.loop = loop
loop.run_until_complete(client.login(bot_token))
loop.run_until_complete(client.connect())

with open(get_path('server_info.json')) as server_file:
data = json.load(server_file)

if 'verified' not in data[user['id']]:
return "ACCESS DENIED"
elif data[user['id']]['verified'] is False:
return "INSUFFICIENT ROLE FOR ACCESS"
elif args.two-factor_auth is True and data[user['id']]['mfa_enabled'] is False:
return "PLEASE ENABLE 2-FACTOR AUTHORIZATION IN DISCORD"
else:
return redirect('/')
except:
with open(get_path('user_info.json'), 'w') as server_file:
json.dump(user, server_file, indent=4)


if __name__ == '__main__':
app.run('158.69.213.118')


13 changes: 13 additions & 0 deletions oauth2/oauth2.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=Gunicorn instance to serve oauth2
After=network.target

[Service]
User=root
Group=www-data
WorkingDirectory=/var/www/html/PMSF/oauth2
Environment="PATH=/var/www/html/PMSF/oauth2/env/bin"
ExecStart=/var/www/html/PMSF/oauth2/env/bin/gunicorn --workers 3 --bind unix:oauth2.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target
5 changes: 5 additions & 0 deletions oauth2/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
discord.py
requests_oauthlib
flask
gunicorn
configargparse
4 changes: 4 additions & 0 deletions oauth2/wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from oauth2 import app

if __name__ == "__main__":
app.run()