diff --git a/app/main.py b/app/main.py index abbf3f0..5d47d31 100644 --- a/app/main.py +++ b/app/main.py @@ -2042,17 +2042,18 @@ def _verify_broker_secret(request: Request) -> str: def resolve_member_email(identity: str) -> str: - """Resolve a member identity (email OR Cloudron username) to their email. + """Resolve a member identity (email, Cloudron username, or uid) to email. - If it looks like an email (contains "@"), return it lowercased. Otherwise - treat it as a Cloudron username and look up the corresponding email from - the Cloudron user list (synchronous, using urllib since this runs outside - the async request path of httpx). + Cloudron's OIDC `sub` claim is the user ID (uid-…), and email-invited users + have no username at all. So accept three identity shapes: + - email ("@") → return as-is + - uid-… → match on the user id + - username → match on the username field (legacy accounts) """ identity = identity.strip() if "@" in identity: return identity.lower() - # Username → email via Cloudron user list. + # uid / username → email via Cloudron user list (one fetch covers both). import urllib.request req = urllib.request.Request( @@ -2062,6 +2063,9 @@ def resolve_member_email(identity: str) -> str: with urllib.request.urlopen(req, timeout=15) as r: data = json.loads(r.read().decode()) users = data.get("users", []) + for u in users: + if u.get("id") == identity: + return (u.get("email") or "").lower() for u in users: if u.get("username") == identity: return (u.get("email") or "").lower()