aboutsummaryrefslogtreecommitdiff
path: root/jb/views
diff options
context:
space:
mode:
authorstuppie2026-09-01 14:17:10 -0600
committerstuppie2026-09-01 14:17:10 -0600
commit81261e52931d055df5830e29b9bf5ef81ba9134e (patch)
tree9ce5817cdb0953080f78950ea42c869683ed5643 /jb/views
parentf5a1882de073ea6859c226395daefeb566e9e802 (diff)
downloadamt-jb-81261e52931d055df5830e29b9bf5ef81ba9134e.tar.gz
amt-jb-81261e52931d055df5830e29b9bf5ef81ba9134e.zip
add a magic token flow specifically for amt account link. gr api manager add more logging and error handling
Diffstat (limited to 'jb/views')
-rw-r--r--jb/views/auth.py58
1 files changed, 50 insertions, 8 deletions
diff --git a/jb/views/auth.py b/jb/views/auth.py
index 33c9452..dc63ca3 100644
--- a/jb/views/auth.py
+++ b/jb/views/auth.py
@@ -1,10 +1,3 @@
-"""Redis-backed magic-link authentication.
-
-The user service is deliberately not coupled to this module. Once that service
-has resolved an email address to its stable user identifier, call
-``create_magic_token`` and put the returned token in the emailed login URL.
-"""
-
from typing import Annotated
from urllib.parse import urlencode
@@ -16,12 +9,18 @@ from jb.api.auth import (
create_session,
get_authenticated_user,
)
-from jb.api.magic_token import consume_magic_token, create_magic_token
+from jb.api.magic_token import (
+ consume_amt_account_link_token,
+ consume_magic_token,
+ create_amt_account_link_token,
+ create_magic_token,
+)
from jb.config import settings
from jb.dependencies import get_gr_api_manager
from jb.managers.gr_api import GRApiManager
from jb.models.auth import (
AccountLogin,
+ AmtAccountLink,
MagicLinkExchangeRequest,
User,
)
@@ -44,6 +43,20 @@ def request_mock_magic_link(body: AccountLogin) -> dict[str, str]:
return {"magic_link": f"/auth/magic-link/?{query}"}
+@auth_router.post("/link-amt/request")
+def link_amt_account(body: AmtAccountLink) -> dict[str, str]:
+ """Create a mock AMT account-link email in development."""
+ if not settings.debug:
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
+
+ # TODO: Derive amt_worker_id from a server-validated AMT assignment and
+ # send this link by email instead of returning it.
+ user = User(email=body.email)
+ token = create_amt_account_link_token(user, body.amt_worker_id)
+ query = urlencode({"token": token})
+ return {"magic_link": f"/auth/link-amt/?{query}"}
+
+
@auth_router.get("/magic-link/", response_class=HTMLResponse, include_in_schema=False)
def magic_link_landing_page() -> HTMLResponse:
"""Serve the SPA without redeeming the token; email prefetches are harmless."""
@@ -57,6 +70,12 @@ def magic_link_landing_page() -> HTMLResponse:
)
+@auth_router.get("/link-amt/", response_class=HTMLResponse, include_in_schema=False)
+def link_amt_account_landing_page() -> HTMLResponse:
+ """Serve the account-link SPA without consuming the one-time token."""
+ return magic_link_landing_page()
+
+
@auth_router.post("/magic-link/exchange", status_code=status.HTTP_204_NO_CONTENT)
def exchange_magic_link(
body: MagicLinkExchangeRequest,
@@ -82,6 +101,29 @@ def exchange_magic_link(
)
+@auth_router.post("/link-amt/exchange", status_code=status.HTTP_204_NO_CONTENT)
+def exchange_amt_account_link(
+ body: MagicLinkExchangeRequest,
+ response: Response,
+ gr_api: Annotated[GRApiManager, Depends(get_gr_api_manager)],
+) -> None:
+ """Validate the email link, then transition the bound AMT account."""
+ token_data = consume_amt_account_link_token(body.token)
+ user = User(email=token_data.email)
+ user = gr_api.transition_user_from_amt(user, token_data.amt_worker_id)
+
+ session_token = create_session(user.product_user_id)
+ response.set_cookie(
+ key=SESSION_COOKIE_NAME,
+ value=session_token,
+ max_age=settings.session_token_ttl_seconds,
+ httponly=True,
+ secure=not settings.debug,
+ samesite="lax",
+ path="/",
+ )
+
+
@auth_router.get("/session", response_model=User)
def get_session(
user: Annotated[User, Depends(get_authenticated_user)],