diff --git a/.gitignore b/.gitignore
index 55be276..0e56eec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+refs/
+
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..5431ca1
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..f1198c9
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sinkhole.iml b/.idea/sinkhole.iml
new file mode 100644
index 0000000..35a5e63
--- /dev/null
+++ b/.idea/sinkhole.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app.py b/src/app.py
new file mode 100644
index 0000000..fa5d4cd
--- /dev/null
+++ b/src/app.py
@@ -0,0 +1,111 @@
+from flask import Flask, request, redirect, abort, url_for
+from urllib.parse import quote_plus
+from base64 import b64encode
+import datetime
+import requests
+import uuid
+import os
+
+
+app = Flask(__name__)
+
+STATE = quote_plus(str(uuid.uuid4()))
+TOKEN = None
+AGENT = f"sinkhole v0.0.1"
+CLIENT_ID = os.getenv('REDDIT_APP_ID', '')
+CLIENT_SECRET = os.getenv('CLIENT_SECRET', '')
+REDIRECT_URI = quote_plus(os.getenv('REDIRECT_URI', ''))
+DURATION = quote_plus(os.getenv('DURATION', 'temporary'))
+SCOPE = ','.join([
+ 'identity',
+ 'edit',
+ 'flair',
+ 'history',
+ 'modconfig',
+ 'modflair',
+ 'modlog',
+ 'modposts',
+ 'modwiki',
+ 'mysubreddits',
+ 'privatemessages',
+ 'read',
+ 'report',
+ 'save',
+ 'submit',
+ 'subscribe',
+ 'vote',
+ 'wikiedit',
+ 'wikiread'
+])
+
+
+def get_bearer_token(
+ code: str
+) -> dict:
+ token_resp = requests.post(
+ "https://www.reddit.com/api/v1/access_token",
+ headers={
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'User-Agent': AGENT,
+ 'Authorization': f"Basic {b64encode(f'{CLIENT_ID}:{CLIENT_SECRET}'.encode()).decode('utf-8')}"
+ },
+ data=f"grant_type=authorization_code&code={code}&redirect_uri={REDIRECT_URI}"
+ )
+
+ token_data = token_resp.json()
+ expiration_ts = (datetime.datetime.now() + datetime.timedelta(seconds=token_data['expires_in'])).isoformat()
+ return {
+ 'access_token': token_data['access_token'],
+ 'token_type': token_data['token_type'],
+ 'expiration_ts': expiration_ts,
+ 'scope': token_data['scope']
+ }
+
+
+@app.route("/")
+def index():
+ if TOKEN:
+ return "You are authorized, way to go!"
+ auth_url = f"https://www.reddit.com/api/v1/authorize.compact?client_id={quote_plus(CLIENT_ID)}" \
+ f"&response_type=code" \
+ f"&state={STATE}" \
+ f"&redirect_uri={REDIRECT_URI}" \
+ f"&duration={DURATION}" \
+ f"&scope={SCOPE}"
+ return f"Please navigate here to authorize."
+
+
+@app.route('/home', methods=['POST'])
+def home():
+ token = request.form.get('token')
+ return {
+ 'TOKEN': request.form.get('token'),
+ 'TOKEN_TYPE': request.form.get('token_type'),
+ 'TOKEN_EXPIRATION': request.form.get('token_expire_dt'),
+ 'TOKEN_SCOPE': request.form.get('token_scope')
+ }
+
+
+@app.route("/callback", methods=['GET', 'POST'])
+def auth_callback(**kwargs):
+ error = request.args.get('error')
+ state = request.args.get('state')
+ code = request.args.get('code')
+
+ if not error:
+ if state == STATE:
+ # get the bearer token and redirect
+ token_info = get_bearer_token(code)
+ return redirect(url_for('home',
+ token=token_info.get('access_token'),
+ token_type=token_info.get('token_type'),
+ token_expire_dt=token_info.get('expiration_ts'),
+ token_scope=token_info.get('scope')
+ ),
+ code=307)
+ else:
+ return f"Not authorized! State {state} didn't match {STATE} that was sent!"
+ else:
+ abort(403)
+