Silent SSO via sso_hint cookie
- Federation sets sso_hint=1 on .rose-ash.com after magic link login - Client apps: before_request checks sso_hint, triggers silent OAuth once per session (sso_checked flag prevents loops) - Logout clears sso_hint cookie on all apps Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, Awaitable, Sequence
|
from typing import Callable, Awaitable, Sequence
|
||||||
|
|
||||||
from quart import Quart, request, g, send_from_directory
|
from quart import Quart, request, g, redirect, send_from_directory
|
||||||
|
|
||||||
from shared.config import init_config, config, pretty
|
from shared.config import init_config, config, pretty
|
||||||
from shared.models import KV # ensure shared models imported
|
from shared.models import KV # ensure shared models imported
|
||||||
@@ -122,6 +122,24 @@ def create_base_app(
|
|||||||
for fn in before_request_fns:
|
for fn in before_request_fns:
|
||||||
app.before_request(fn)
|
app.before_request(fn)
|
||||||
|
|
||||||
|
# Silent SSO: if federation set sso_hint cookie, trigger OAuth once
|
||||||
|
if name != "federation":
|
||||||
|
from urllib.parse import quote as _quote
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
async def _sso_check():
|
||||||
|
from quart import session as qs
|
||||||
|
if request.path.startswith("/auth/"):
|
||||||
|
return
|
||||||
|
if qs.get("uid"):
|
||||||
|
return
|
||||||
|
if qs.get("sso_checked"):
|
||||||
|
return
|
||||||
|
if not request.cookies.get("sso_hint"):
|
||||||
|
return
|
||||||
|
qs["sso_checked"] = True
|
||||||
|
return redirect(f"/auth/login/?next={_quote(request.url, safe='')}")
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
async def _csrf_protect():
|
async def _csrf_protect():
|
||||||
await protect()
|
await protect()
|
||||||
|
|||||||
@@ -125,8 +125,10 @@ def create_oauth_blueprint(app_name: str) -> Blueprint:
|
|||||||
async def logout():
|
async def logout():
|
||||||
qsession.pop(SESSION_USER_KEY, None)
|
qsession.pop(SESSION_USER_KEY, None)
|
||||||
qsession.pop("cart_sid", None)
|
qsession.pop("cart_sid", None)
|
||||||
# Redirect to blog home — avoids re-auth loop on apps that require login
|
qsession.pop("sso_checked", None)
|
||||||
from shared.infrastructure.urls import blog_url
|
from shared.infrastructure.urls import blog_url
|
||||||
return redirect(blog_url("/"))
|
resp = redirect(blog_url("/"))
|
||||||
|
resp.delete_cookie("sso_hint", domain=".rose-ash.com", path="/")
|
||||||
|
return resp
|
||||||
|
|
||||||
return bp
|
return bp
|
||||||
|
|||||||
Reference in New Issue
Block a user