Commit a4d77c9a authored by Jiri Vlasak's avatar Jiri Vlasak

Fix multi-process issue when authenticating

As uvicorn may be run with at maximum of 9 workers (because of the
number of connections to the database,) it may happen that one worker
starts the authentication process and another finishes it. However, the
global variable `tmp_auth_store` is not shared between these workers
(processes.)

This is fixed with storing temporary information to files.

This problem could be avoided by sticking with RESTful architecture.
However, authentication is challenging as OpenStreetMap use OAuth1 which
support only authorization. Therefore, for authentication purposes of
the damn server, RESTful architecture is broken.
parent 58a29e89
......@@ -13,6 +13,11 @@ The format is based on `Keep a Changelog`_ and this project adheres to
`Unreleased`_
=============
Fixed
-----
- Multi-process issue when authenticating.
0.7.1 - 2020-11-14
==================
......
......@@ -6,7 +6,9 @@
"""
import fastapi
import json
import jwt
import os
import pydantic
import rauth
import xml.etree.ElementTree as ET
......@@ -94,24 +96,25 @@ async def auth(tid: str):
request_token, request_token_secret = osm.get_request_token(
params={'oauth_callback': "{}/{}".format(conf.OAUTH_CALLBACK_URL, tid)}
)
tmp_auth_store[tid] = {}
tmp_auth_store[tid]["request_token"] = request_token
tmp_auth_store[tid]["request_token_secret"] = request_token_secret
t = {
"request_token": request_token,
"request_token_secret": request_token_secret,
}
with open("{}/damn_{}".format(conf.TMP_AUTH_STORE_DIR, tid), "w") as f:
json.dump(t, f)
authorize_url = osm.get_authorize_url(request_token)
return RedirectResponse(url=authorize_url)
@app.get("/authed/{tid}")
async def authed(tid: str, oauth_token: str, oauth_verifier: str):
assert tid in tmp_auth_store
request_token = tmp_auth_store[tid]["request_token"]
del(tmp_auth_store[tid]["request_token"])
request_token_secret = tmp_auth_store[tid]["request_token_secret"]
del(tmp_auth_store[tid]["request_token_secret"])
try:
assert oauth_token == request_token
with open("{}/damn_{}".format(conf.TMP_AUTH_STORE_DIR, tid), "r") as f:
t = json.load(f)
os.remove("{}/damn_{}".format(conf.TMP_AUTH_STORE_DIR, tid))
assert oauth_token == t["request_token"]
osm_s = osm.get_auth_session(
request_token,
request_token_secret,
t["request_token"],
t["request_token_secret"],
data={'oauth_verifier': oauth_verifier},
)
osm_r = osm_s.get("user/details")
......@@ -136,21 +139,30 @@ async def authed(tid: str, oauth_token: str, oauth_verifier: str):
u["tags"] = ""
await user.save(u)
await user.update(u)
tmp_auth_store[tid]["token"] = jwt.encode(
u,
conf.JWT_SECRET,
algorithm="HS256",
)
t = {
"token": jwt.encode(
u,
conf.JWT_SECRET,
algorithm="HS256",
).decode(),
}
with open("{}/damn_{}".format(conf.TMP_AUTH_STORE_DIR, tid), "w") as f:
json.dump(t, f)
return "Good."
@app.get("/token/{tid}")
async def authed(tid: str):
try:
t = tmp_auth_store[tid]["token"]
del(tmp_auth_store[tid])
return {"token": t}
with open("{}/damn_{}".format(conf.TMP_AUTH_STORE_DIR, tid), "r") as f:
t = json.load(f)
assert "token" in t
os.remove("{}/damn_{}".format(conf.TMP_AUTH_STORE_DIR, tid))
return t
except:
raise fastapi.HTTPException(status_code=401, detail="Token expired.")
raise fastapi.HTTPException(
status_code=401,
detail="Token expired or not yet ready.",
)
@app.get("/areas")
async def load_areas(since: str = None):
......
......@@ -17,3 +17,4 @@ OAUTH_AUTHORIZE_URL = "https://www.openstreetmap.org/oauth/authorize"
OAUTH_REQUEST_TOKEN_URL = "https://www.openstreetmap.org/oauth/request_token"
OAUTH_BASE_URL = "https://api.openstreetmap.org/api/0.6/"
OAUTH_CALLBACK_URL = "{}/authed".format(DAMN_SERVER)
TMP_AUTH_STORE_DIR = "/tmp"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment