From 138afd7c8e70baf5471d1343597fc5a16866c439 Mon Sep 17 00:00:00 2001 From: n Date: Wed, 9 Jul 2025 06:47:17 +0100 Subject: [PATCH] latest --- Dockerfile | 6 ++++-- app/main.py | 23 +++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2b2a0e0..a992af8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,5 +10,7 @@ RUN uv pip install --no-cache-dir --upgrade -r /code/requirements.txt COPY ./app /code/app # If running behind a proxy like Nginx or Traefik add --proxy-headers -# CMD ["uv", "run", "fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"] -CMD ["uv", "run", "fastapi", "run", "app/main.py", "--port", "80"] +# CMD ["uv", "run", "fastapi", "run", "app/main.py", "--proxy-headers"] +CMD ["uv", "run", "fastapi", "run", "app/main.py"] + +EXPOSE 8000 diff --git a/app/main.py b/app/main.py index 361be67..3074573 100644 --- a/app/main.py +++ b/app/main.py @@ -1,13 +1,15 @@ from collections import OrderedDict from dotenv import load_dotenv -from fastapi import FastAPI +from fastapi import FastAPI, Query, Request, Response, status from fastapi.responses import RedirectResponse from uuid import uuid4 from urllib.parse import urlencode -from typing import Union +from typing import Annotated, Union import requests import os +UUIDPattern = "[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}" +UUIDQuery = Query(min_length=36, max_length=36, pattern=f"^{UUIDPattern}$") load_dotenv() client_id = os.environ['MONZO_CLIENT_ID'] client_secret = os.environ['MONZO_CLIENT_SECRET'] @@ -16,11 +18,11 @@ callback_uri = os.environ['MONZO_CALLBACK_URI'] app = FastAPI() @app.get("/") -def read_root(): return {"version": "v0.0.4"} +def read_root(): return {"version": "v0.0.8"} @app.get("/redirect", response_class=RedirectResponse) -def read_redirect(): +def read_redirect(res: Response): state = uuid4() # TODO: store state in a cookie to check it later query = urlencode(OrderedDict( @@ -29,12 +31,17 @@ def read_redirect(): state=state, response_type="code", )) + res.set_cookie(key="monzo-api:state", value=state) return f"https://auth.monzo.com/?{query}" @app.get("/callback") -def read_callback(code: str, state: str): - # TODO: check that the state is a valid uuid +def read_callback(code: str, state: Annotated[str, UUIDQuery], req: Request, res: Response): + stored_state = req.cookies["monzo-api:state"] + if (state != stored_state): + res.status_code = status.HTTP_400_BAD_REQUEST + return { 'error': True, 'data': f"Callback state '{state}' does not match stored state '{stored_state}'." } + # TODO: check the state with the user's cookie # TODO: check that the code is a valid jwt data = { @@ -44,5 +51,5 @@ def read_callback(code: str, state: str): 'redirect_uri': callback_uri, 'code': code, } - response = requests.post("https://api.monzo.com/oauth2/token", data=data) - return response.json() + token = requests.post("https://api.monzo.com/oauth2/token", data=data) + return { 'error': False, 'data': token.json() }