Add artdag to OAuth clients + POST /auth/oauth/token endpoint
Standard HTTP token exchange for clients that don't share the coop DB. Returns user_id, username, display_name, grant_token in exchange for a valid authorization code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -43,7 +43,7 @@ from .services import (
|
||||
SESSION_USER_KEY = "uid"
|
||||
ACCOUNT_SESSION_KEY = "account_sid"
|
||||
|
||||
ALLOWED_CLIENTS = {"blog", "market", "cart", "events", "federation"}
|
||||
ALLOWED_CLIENTS = {"blog", "market", "cart", "events", "federation", "artdag"}
|
||||
|
||||
|
||||
def register(url_prefix="/auth"):
|
||||
@@ -121,6 +121,69 @@ def register(url_prefix="/auth"):
|
||||
f"&account_did={account_did}"
|
||||
)
|
||||
|
||||
# --- OAuth2 token exchange (for external clients like artdag) -------------
|
||||
|
||||
@auth_bp.post("/oauth/token")
|
||||
@auth_bp.post("/oauth/token/")
|
||||
async def oauth_token():
|
||||
"""Exchange an authorization code for user info + grant token.
|
||||
|
||||
Used by clients that don't share the coop database (e.g. artdag).
|
||||
Accepts JSON: {code, client_id, redirect_uri}
|
||||
Returns JSON: {user_id, username, display_name, grant_token}
|
||||
"""
|
||||
data = await request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "invalid_request"}), 400
|
||||
|
||||
code = data.get("code", "")
|
||||
client_id = data.get("client_id", "")
|
||||
redirect_uri = data.get("redirect_uri", "")
|
||||
|
||||
if client_id not in ALLOWED_CLIENTS:
|
||||
return jsonify({"error": "invalid_client"}), 400
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
async with get_session() as s:
|
||||
async with s.begin():
|
||||
result = await s.execute(
|
||||
select(OAuthCode)
|
||||
.where(OAuthCode.code == code)
|
||||
.with_for_update()
|
||||
)
|
||||
oauth_code = result.scalar_one_or_none()
|
||||
|
||||
if not oauth_code:
|
||||
return jsonify({"error": "invalid_grant"}), 400
|
||||
|
||||
if oauth_code.used_at is not None:
|
||||
return jsonify({"error": "invalid_grant"}), 400
|
||||
|
||||
if oauth_code.expires_at < now:
|
||||
return jsonify({"error": "invalid_grant"}), 400
|
||||
|
||||
if oauth_code.client_id != client_id:
|
||||
return jsonify({"error": "invalid_grant"}), 400
|
||||
|
||||
if oauth_code.redirect_uri != redirect_uri:
|
||||
return jsonify({"error": "invalid_grant"}), 400
|
||||
|
||||
oauth_code.used_at = now
|
||||
user_id = oauth_code.user_id
|
||||
grant_token = oauth_code.grant_token
|
||||
|
||||
user = await s.get(User, user_id)
|
||||
if not user:
|
||||
return jsonify({"error": "invalid_grant"}), 400
|
||||
|
||||
return jsonify({
|
||||
"user_id": user_id,
|
||||
"username": user.email or "",
|
||||
"display_name": user.name or "",
|
||||
"grant_token": grant_token,
|
||||
})
|
||||
|
||||
# --- Grant verification (internal endpoint) ------------------------------
|
||||
|
||||
@auth_bp.get("/internal/verify-grant")
|
||||
|
||||
Reference in New Issue
Block a user