akif/arclet-boards
Arclet Boards — private Kanban workspaces with AppID, DataK3, shared web and MCP actions.
A remix template · 1 remixes · Remix akif/arclet-boards
app/infrastructure/identity.py
"""Trust only the verified person identity injected by Ignite's gateway.
The gateway strips inbound identity headers. This module must never be exposed
directly as a publicly reachable uvicorn service without that gateway.
See the current AppID Ignite recipe and AppID's documented amr claims.
"""
import base64
import json
import os
import time
from app.domain.model import Invalid, Person, email
def person_from(headers) -> Person | None:
if headers.get("x-dodil-auth-source") != "pool":
return None
pool = os.environ.get("DODIL_USER_POOL", "").strip()
if not pool or "/" not in pool:
return None
try:
user = json.loads(headers.get("x-dodil-user", ""))
encoded = headers.get("x-dodil-user-jwt", "").split(".")[1]
claims = json.loads(base64.urlsafe_b64decode(encoded + "=" * (-len(encoded) % 4)))
if not isinstance(user, dict) or not isinstance(claims, dict):
return None
expected = f"https://appid.dodil.io/{pool}"
audience = claims.get("aud")
audiences = audience if isinstance(audience, list) else [audience]
amr = claims.get("amr", [])
if not isinstance(amr, list) or not ({"pwd", "ext"} & set(amr)):
return None
if "client_credentials" in amr or claims.get("iss") != expected:
return None
if f"pool:{pool.split('/', 1)[1]}" not in audiences:
return None
if float(claims.get("exp", 0)) <= time.time():
return None
sub = claims.get("sub")
address = email(claims.get("email", ""))
if not isinstance(sub, str) or not sub or user.get("sub") != sub:
return None
if email(user.get("email", "")) != address:
return None
connection = claims.get("connection", "")
if connection not in ("local", "oidc") and not connection.startswith("oauth:"):
return None
return Person(sub=sub, email=address)
except (ValueError, IndexError, TypeError, KeyError, Invalid, AttributeError):
return None