135 lines
4.9 KiB
Python
135 lines
4.9 KiB
Python
from urllib.parse import urlparse
|
|
from secrets import token_urlsafe
|
|
from flask import Flask, render_template, url_for, redirect, request, session
|
|
from authlib.integrations.flask_client import OAuth
|
|
from requests import post
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
SECRET_KEY = token_urlsafe(32)
|
|
app.secret_key = SECRET_KEY
|
|
|
|
|
|
oauth = OAuth(app)
|
|
|
|
|
|
# Required for using authlib to process OAuth.
|
|
# Many fields are blank or wrong, but that's because we'll fill them later.
|
|
|
|
|
|
gotosocial = oauth.register(
|
|
name='gotosocial',
|
|
client_id='',
|
|
client_secret='',
|
|
access_token_url='/oauth/token',
|
|
access_token_params={'response_type': 'token',
|
|
'grant_type': 'authorization_code',
|
|
'client_id': '',
|
|
'client_secret': ''},
|
|
authorize_url='/oauth/authorize',
|
|
authorize_params={'grant_type': 'authorization_code'},
|
|
api_base_url='/api',
|
|
client_kwargs={'scope': 'read'},
|
|
)
|
|
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template('index.html')
|
|
|
|
# Initialization of OAuth flow.
|
|
# Once we know what domain the instance is at, we need to do
|
|
# an API call to register the app and get a client_id and client_secret.
|
|
|
|
|
|
@app.route('/set_domain', methods=['POST'])
|
|
def set_domain():
|
|
if (request.method == 'POST'):
|
|
try:
|
|
# First, let's parse the domain URL and clean up if necessary
|
|
domain_parse = urlparse(request.form["domain"])
|
|
if (domain_parse):
|
|
if (domain_parse.scheme):
|
|
domain = domain_parse.geturl()
|
|
else:
|
|
domain = f'https://{domain_parse.geturl()}'
|
|
|
|
# We need to construct our request and send it to the instance.
|
|
# This is done with the Requests library, since authlib doesn't
|
|
# provide a method to handle that.
|
|
payload = {
|
|
'client_name': 'gotosocial-fe',
|
|
'redirect_uris': url_for('index', _external=True)
|
|
}
|
|
response = post(f'{domain}/api/v1/apps', data=payload)
|
|
client_data = response.json()
|
|
|
|
# With any luck, we'll have the client id and secret.
|
|
# This sets up the OAuth object we registered earlier with
|
|
# The correct parameters from the data we recieved.
|
|
oauth.gotosocial.client_id = client_data['client_id']
|
|
oauth.gotosocial.client_secret = client_data['client_secret']
|
|
oauth.gotosocial.access_token_params = {
|
|
'response_type': 'token',
|
|
'grant_type': 'authorization_code',
|
|
'client_id': client_data['client_id'],
|
|
'client_secret': client_data['client_secret']
|
|
}
|
|
oauth.gotosocial.access_token_url = f'{domain}{oauth.gotosocial.access_token_url}'
|
|
oauth.gotosocial.authorize_url = f'{domain}{oauth.gotosocial.authorize_url}'
|
|
oauth.gotosocial.api_base_url = f'{domain}{oauth.gotosocial.api_base_url}'
|
|
|
|
# Now we need to redirect the user to their instance
|
|
# for authentication.
|
|
return redirect('/login')
|
|
else:
|
|
# If we don't get a url that's parseable.
|
|
return "Did you even submit anything?"
|
|
except:
|
|
# If something bad happens in the app registration with
|
|
# the domain, assume it's not a valid instance domain.
|
|
return "Are you sure you're putting in a GoToSocial instance url?"
|
|
else:
|
|
return "You can't do a GET request here."
|
|
|
|
|
|
# Now we send the user to their domain so they can authenticate on
|
|
# our behalf and give us permission to use their account with the
|
|
# specified scope(s).
|
|
@app.route('/login')
|
|
def login():
|
|
redirect_uri = url_for('authorize', _external=True)
|
|
return oauth.gotosocial.authorize_redirect(redirect_uri)
|
|
|
|
# Once we're back, do a quick credentials verification to make sure
|
|
# we have a valid access token.
|
|
|
|
|
|
@app.route('/authorize')
|
|
def authorize():
|
|
token = oauth.gotosocial.authorize_access_token()
|
|
session['oauth_token'] = token
|
|
response = oauth.gotosocial.get(
|
|
'api/v1/accounts/verify_credentials')
|
|
response.raise_for_status()
|
|
return redirect(url_for('home', _external=True))
|
|
|
|
# If the flow succeeded, the user should get dropped onto this
|
|
# page, with a very basic home timeline displayed using the API
|
|
# call we should be able to do with our access token.
|
|
|
|
|
|
@app.route('/home')
|
|
def home():
|
|
# TODO: Long-term shoukd make sure we store the token in
|
|
# localStorage or sessionStorage and try to retrieve it
|
|
# from there first.
|
|
token = session['oauth_token']
|
|
|
|
response = oauth.gotosocial.get(
|
|
'api/v1/timelines/home', token=token)
|
|
response.raise_for_status()
|
|
home_timeline = response.json()
|
|
return render_template(f'{url_for("home")}/index.html', home_timeline=home_timeline)
|