diff options
| author | stuppie | 2026-09-01 14:17:10 -0600 |
|---|---|---|
| committer | stuppie | 2026-09-01 14:17:10 -0600 |
| commit | 81261e52931d055df5830e29b9bf5ef81ba9134e (patch) | |
| tree | 9ce5817cdb0953080f78950ea42c869683ed5643 /jb/views/auth.py | |
| parent | f5a1882de073ea6859c226395daefeb566e9e802 (diff) | |
| download | amt-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/auth.py')
| -rw-r--r-- | jb/views/auth.py | 58 |
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)], |
