Replace propagation chain + check-device with prompt=none OAuth handshake
Client apps now do a silent OAuth round-trip (prompt=none) to account on first visit. If user is logged in on account, they get silently logged in. If not, the result is cached (5 min) to avoid repeated handshakes. Grant verification now uses direct DB query instead of aiohttp HTTP calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -63,23 +63,44 @@ def create_oauth_blueprint(app_name: str) -> Blueprint:
|
||||
@bp.get("/login/")
|
||||
async def login():
|
||||
next_url = request.args.get("next", "/")
|
||||
prompt = request.args.get("prompt", "")
|
||||
state = secrets.token_urlsafe(32)
|
||||
qsession["oauth_state"] = state
|
||||
qsession["oauth_next"] = next_url
|
||||
|
||||
device_id = request.cookies.get(cookie_name, "")
|
||||
redirect_uri = app_url(app_name, "/auth/callback")
|
||||
authorize_url = account_url(
|
||||
f"/auth/oauth/authorize?client_id={app_name}"
|
||||
params = (
|
||||
f"?client_id={app_name}"
|
||||
f"&redirect_uri={redirect_uri}"
|
||||
f"&device_id={device_id}"
|
||||
f"&state={state}"
|
||||
)
|
||||
if prompt:
|
||||
params += f"&prompt={prompt}"
|
||||
authorize_url = account_url(f"/auth/oauth/authorize{params}")
|
||||
return redirect(authorize_url)
|
||||
|
||||
@bp.get("/callback")
|
||||
@bp.get("/callback/")
|
||||
async def callback():
|
||||
# Handle prompt=none error (user not logged in on account)
|
||||
error = request.args.get("error")
|
||||
if error == "login_required":
|
||||
next_url = qsession.pop("oauth_next", "/")
|
||||
qsession.pop("oauth_state", None)
|
||||
import time as _time
|
||||
qsession["_pnone_at"] = _time.time()
|
||||
device_id = request.cookies.get(cookie_name, "")
|
||||
if device_id:
|
||||
from shared.browser.app.redis_cacher import get_redis
|
||||
_redis = get_redis()
|
||||
if _redis:
|
||||
await _redis.set(
|
||||
f"prompt:{app_name}:{device_id}", b"none", ex=300
|
||||
)
|
||||
return redirect(next_url)
|
||||
|
||||
code = request.args.get("code")
|
||||
state = request.args.get("state")
|
||||
expected_state = qsession.pop("oauth_state", None)
|
||||
@@ -129,6 +150,7 @@ def create_oauth_blueprint(app_name: str) -> Blueprint:
|
||||
qsession[SESSION_USER_KEY] = user_id
|
||||
if grant_token:
|
||||
qsession[GRANT_TOKEN_KEY] = grant_token
|
||||
qsession.pop("_pnone_at", None)
|
||||
|
||||
# Emit login activity for cart adoption
|
||||
ident = current_cart_identity()
|
||||
@@ -168,6 +190,7 @@ def create_oauth_blueprint(app_name: str) -> Blueprint:
|
||||
qsession.pop(SESSION_USER_KEY, None)
|
||||
qsession.pop(GRANT_TOKEN_KEY, None)
|
||||
qsession.pop("cart_sid", None)
|
||||
qsession.pop("_pnone_at", None)
|
||||
# Redirect through account to revoke grants + clear account session
|
||||
return redirect(account_url("/auth/sso-logout/"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user