diff --git a/bp/auth/routes.py b/bp/auth/routes.py index ee40a33..ce04927 100644 --- a/bp/auth/routes.py +++ b/bp/auth/routes.py @@ -67,12 +67,18 @@ def register(url_prefix="/auth"): if redirect_uri != expected_redirect: return "Invalid redirect_uri", 400 + # Account's own device id — always available via factory hook + account_did = g.device_id + # Not logged in if not g.get("user"): if prompt == "none": - # Silent check — no interactive login, return error + # Silent check — pass account_did so client can watch for future logins sep = "&" if "?" in redirect_uri else "?" - return redirect(f"{redirect_uri}{sep}error=login_required&state={state}") + return redirect( + f"{redirect_uri}{sep}error=login_required" + f"&state={state}&account_did={account_did}" + ) authorize_path = request.full_path store_login_redirect_target() return redirect(url_for("auth.login_form", next=authorize_path)) @@ -110,7 +116,10 @@ def register(url_prefix="/auth"): s.add(oauth_code) sep = "&" if "?" in redirect_uri else "?" - return redirect(f"{redirect_uri}{sep}code={code}&state={state}") + return redirect( + f"{redirect_uri}{sep}code={code}&state={state}" + f"&account_did={account_did}" + ) # --- Grant verification (internal endpoint) ------------------------------ @@ -276,6 +285,20 @@ def register(url_prefix="/auth"): # Fresh account session ID for grant tracking qsession[ACCOUNT_SESSION_KEY] = secrets.token_urlsafe(32) + # Signal login for this device so client apps can detect it + try: + from shared.browser.app.redis_cacher import get_redis + import time as _time + _redis = get_redis() + if _redis: + await _redis.set( + f"did_auth:{g.device_id}", + str(_time.time()).encode(), + ex=30 * 24 * 3600, + ) + except Exception: + current_app.logger.exception("[auth] failed to set did_auth in Redis") + redirect_url = pop_login_redirect_target() return redirect(redirect_url, 303) @@ -296,6 +319,15 @@ def register(url_prefix="/auth"): except SQLAlchemyError: current_app.logger.exception("[auth] failed to revoke grants") + # Clear login signal for this device + try: + from shared.browser.app.redis_cacher import get_redis + _redis = get_redis() + if _redis: + await _redis.delete(f"did_auth:{g.device_id}") + except Exception: + pass + qsession.pop(SESSION_USER_KEY, None) qsession.pop(ACCOUNT_SESSION_KEY, None) from shared.infrastructure.urls import blog_url @@ -318,6 +350,15 @@ def register(url_prefix="/auth"): except SQLAlchemyError: current_app.logger.exception("[auth] failed to revoke grants") + # Clear login signal for this device + try: + from shared.browser.app.redis_cacher import get_redis + _redis = get_redis() + if _redis: + await _redis.delete(f"did_auth:{g.device_id}") + except Exception: + pass + qsession.pop(SESSION_USER_KEY, None) qsession.pop(ACCOUNT_SESSION_KEY, None) from shared.infrastructure.urls import blog_url