zulip-fediverse-auth/auth.py

116 lines
3.7 KiB
Python

from flask import Flask, Response, redirect
from flask import request
from markupsafe import escape
from mastodon import Mastodon
from email.headerregistry import Address
import jwt
import shelve
from gevent.pywsgi import WSGIServer
import os
SECRET = os.environ["SECRET"]
ZULIP = f"https://{os.environ['ZULIP']}/accounts/login/jwt/"
REDIRECT = f"https://{os.environ['ZULIP']}/callback"
SHELVE_LOCATION = os.environ.get("DB_DIR", "/var/lib/fedi-zulip")
PORT = int(os.environ.get("PORT", "5000"))
app = Flask(__name__)
@app.get("/")
def index():
return f"""
<!DOCTYPE html>
<html lang="en">
<body>
<h1>Login to Pleroma Chat Using Handle</h1>
<p>
You can use this page to login to {os.environ['ZULIP']} using your
Pleroma, Akkoma or Mastodon handle. Format is <code>nickname@server</code>.
</p>
<form action="/login" method="post">
<label for="nickname">Fediverse handle</label>
<br>
<input type="email" id="nickname" name="nickname">
<button>login</button>
</form>
</body>
</html>
"""
@app.post("/login")
def login():
instance = Address(addr_spec=request.form["nickname"]).domain
try:
with shelve.open(SHELVE_LOCATION) as apps:
app = apps.get(instance)
if app == None:
(client, secret) = Mastodon.create_app(
"zulip",
scopes=["read"],
redirect_uris=REDIRECT,
api_base_url=f"https://{instance}",
)
apps[instance] = (client, secret)
(client, secret) = apps[instance]
masto = Mastodon(
client_id=client,
client_secret=secret,
api_base_url=f"https://{instance}",
)
oauth = Mastodon.auth_request_url(
masto,
scopes=["read"],
force_login=True,
redirect_uris=REDIRECT,
state=instance,
)
return redirect(oauth)
except Exception as e:
print(e)
return Response("fail", status=400)
@app.get("/callback")
def callback():
oauth = request.args.get("code")
instance = request.args.get("state")
if oauth != None and instance != None:
try:
with shelve.open(SHELVE_LOCATION) as apps:
app = apps.get(instance)
if app != None:
masto = Mastodon(
client_id=app[0],
client_secret=app[1],
api_base_url=f"https://{instance}",
)
Mastodon.log_in(masto, code=oauth, scopes=["read"])
creds = Mastodon.account_verify_credentials(masto)
token = jwt.encode(
{"email": f"{creds.acct}@{instance}"},
SECRET,
algorithm="HS256",
)
return f"""
<!DOCTYPE html>
<html lang="en">
<body>
<form name="zulip" action="{ZULIP}" method="post">
<input type="hidden" name="token" value={escape(token)}>
<button>login to zulip</button>
</form>
<script type="text/javascript">
document.zulip.submit();
</script>
</body>
</html>
"""
except:
pass
return Response("fail", status=400)
http_server = WSGIServer(("127.0.0.1", PORT), app)
http_server.serve_forever()