From edff9381275d90d7be5f078430554ae592a27f5d Mon Sep 17 00:00:00 2001 From: Max Nanis Date: Fri, 11 Sep 2026 11:27:58 -0700 Subject: Current jb flask testing state. --- jb/api/magic_token.py | 2 +- jb/main.py | 1 + jb/managers/email_manager.py | 16 ++++++++-------- jb/settings.py | 2 +- jb/views/auth.py | 30 ++++++++++++++++++++---------- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/jb/api/magic_token.py b/jb/api/magic_token.py index 910dc12..b60d5e9 100644 --- a/jb/api/magic_token.py +++ b/jb/api/magic_token.py @@ -9,7 +9,7 @@ from jb.models.auth import AmtAccountLink MAGIC_TOKEN_PREFIX = "auth:magic:" AMT_ACCOUNT_LINK_TOKEN_PREFIX = "auth:amt-account-link:" -MAGIC_TOKEN_TTL: int = 5 * 60 # 5 minutes, in seconds +MAGIC_TOKEN_TTL: int = 10 * 60 # 10 minutes, in seconds def redis_token_key(token: str, prefix: str = MAGIC_TOKEN_PREFIX) -> str: diff --git a/jb/main.py b/jb/main.py index 9f4f000..e30bb38 100644 --- a/jb/main.py +++ b/jb/main.py @@ -22,6 +22,7 @@ app = FastAPI( version="1.0.0", ) + app.add_middleware( CORSMiddleware, allow_origins=["*"], diff --git a/jb/managers/email_manager.py b/jb/managers/email_manager.py index 78acc9d..7298c60 100644 --- a/jb/managers/email_manager.py +++ b/jb/managers/email_manager.py @@ -10,8 +10,7 @@ assert settings.mautic_api_key auth_headers = {"Authorization": f"Basic {settings.mautic_api_key.get_secret_value()}"} -def get_or_create_contact(email: str, amt_worker_id: str | None = None): - # amt_worker_id = "A2Z2FRA128FNW" +def get_or_create_contact(email: str, amt_worker_id: str | None = None) -> int: body = {"email": email} if amt_worker_id: body["amt_worker_id"] = amt_worker_id @@ -20,8 +19,7 @@ def get_or_create_contact(email: str, amt_worker_id: str | None = None): json=body, headers=auth_headers, ).json() - contact_id = res["contact"]["id"] - return contact_id + return int(res["contact"]["id"]) def send_login_email_from_url(mautic_url: str, magic_link: str) -> None: @@ -34,6 +32,7 @@ def send_login_email_from_url(mautic_url: str, magic_link: str) -> None: } body = {"tokens": email_tokens} response = requests.post(url=mautic_url, json=body, headers=auth_headers) + try: response.raise_for_status() except requests.exceptions.HTTPError: @@ -41,20 +40,21 @@ def send_login_email_from_url(mautic_url: str, magic_link: str) -> None: print(response.text) raise d = response.json() + assert d.get("success"), f"Failed to send email: {d.get('failed')}" print("Email sent successfully") -def send_login_email(email: str, magic_token: str): +def send_login_email(email: str, magic_token: str) -> None: contact_id = get_or_create_contact(email=email) mautic_url = ( f"{MAUTIC_BASE_URL}/api/emails/{EMAIL_TEMPLATE_ID}/contact/{contact_id}/send" ) magic_link = f"{settings.base_url}auth/magic-link/?token={magic_token}" - return send_login_email_from_url(mautic_url, magic_link) + send_login_email_from_url(mautic_url, magic_link) -def send_amt_link_email(email: str, magic_token: str): +def send_amt_link_email(email: str, magic_token: str) -> None: # Don't actually associate the email with the worker ID # until they click the link contact_id = get_or_create_contact(email=email) @@ -62,4 +62,4 @@ def send_amt_link_email(email: str, magic_token: str): f"{MAUTIC_BASE_URL}/api/emails/{EMAIL_TEMPLATE_ID}/contact/{contact_id}/send" ) magic_link = f"{settings.base_url}auth/link-amt/?token={magic_token}" - return send_login_email_from_url(mautic_url, magic_link) + send_login_email_from_url(mautic_url, magic_link) diff --git a/jb/settings.py b/jb/settings.py index a425b31..1c02075 100644 --- a/jb/settings.py +++ b/jb/settings.py @@ -52,7 +52,7 @@ class Settings(GRLBaseSettings): sns_path: str | None = Field(default=None) - session_token_ttl_seconds: int = Field(default=30 * 24 * 60 * 60, gt=0) + session_token_ttl_seconds: int = Field(default=30 * 24 * 60 * 60, gt=0) # 30 days session_jwt_secret: SecretStr | None = Field(default=None, min_length=32) magic_token_salt: SecretStr | None = Field(default=None, min_length=32) diff --git a/jb/views/auth.py b/jb/views/auth.py index 44fef10..bf39b51 100644 --- a/jb/views/auth.py +++ b/jb/views/auth.py @@ -2,7 +2,7 @@ from typing import Annotated from urllib.parse import urlencode from fastapi import APIRouter, Depends, HTTPException, Response, status -from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse from jb.api.auth import ( SESSION_COOKIE_NAME, @@ -103,6 +103,12 @@ def link_amt_account(body: AmtAccountLink) -> dict[str, str]: """Link an AMT account and login.""" email = str(body.email) amt_worker_id = body.amt_worker_id + + # TODO! Prevent a user that's already transitioned their account, from being + # TODO! able to continuously create this special link token. + # TODO! Max Notes: this seems to be handled within the gr-api, and that + # TODO! can raise, the following line would / should fail if needed. + token = create_amt_account_link_token(email=email, amt_worker_id=amt_worker_id) if settings.debug: @@ -110,7 +116,7 @@ def link_amt_account(body: AmtAccountLink) -> dict[str, str]: return {"magic_link": f"{settings.base_url}auth/link-amt/?{query}"} send_amt_link_email(email=email, magic_token=token) - return {"detail": "Link sent. Check your inbox and follow the link to log in."} + return {} @auth_router.get("/link-amt/", response_class=HTMLResponse, include_in_schema=False) @@ -119,14 +125,18 @@ def link_amt_account_landing_page( token: str | None = None, ) -> HTMLResponse: """Serve the account-link SPA without consuming the one-time token.""" - if settings.debug: - if token is None: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="token is required", - ) - response = RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER) - _exchange_amt_account_link(token, response, gr_api) + + # TODO! Try catch any of this, and if it fails, show the user a + # TODO! failed HTML page. As of now, it shows them a failed JSON response. + + if token is None: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="token is required", + ) + _response = RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER) + _exchange_amt_account_link(token=token, response=_response, gr_api=gr_api) + return HTMLResponse( BASE_HTML, headers={ -- cgit v1.2.3