fix: enrich me profile via userinfo and add org-member management plan
This commit is contained in:
@@ -26,6 +26,7 @@ class Settings(BaseSettings):
|
||||
authentik_client_id: str = ""
|
||||
authentik_client_secret: str = ""
|
||||
authentik_token_endpoint: str = ""
|
||||
authentik_userinfo_endpoint: str = ""
|
||||
|
||||
public_frontend_origins: Annotated[list[str], NoDecode] = ["https://member.ose.tw"]
|
||||
internal_shared_secret: str = ""
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
import httpx
|
||||
import jwt
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
@@ -19,11 +20,19 @@ class AuthentikTokenVerifier:
|
||||
jwks_url: str | None,
|
||||
audience: str | None,
|
||||
client_secret: str | None,
|
||||
base_url: str | None,
|
||||
userinfo_endpoint: str | None,
|
||||
verify_tls: bool,
|
||||
) -> None:
|
||||
self.issuer = issuer.strip() if issuer else None
|
||||
self.jwks_url = jwks_url.strip() if jwks_url else self._infer_jwks_url(self.issuer)
|
||||
self.audience = audience.strip() if audience else None
|
||||
self.client_secret = client_secret.strip() if client_secret else None
|
||||
self.base_url = base_url.strip() if base_url else None
|
||||
self.userinfo_endpoint = (
|
||||
userinfo_endpoint.strip() if userinfo_endpoint else self._infer_userinfo_endpoint(self.issuer, self.base_url)
|
||||
)
|
||||
self.verify_tls = verify_tls
|
||||
|
||||
if not self.jwks_url:
|
||||
raise ValueError("AUTHENTIK_JWKS_URL or AUTHENTIK_ISSUER is required")
|
||||
@@ -39,6 +48,50 @@ class AuthentikTokenVerifier:
|
||||
return normalized
|
||||
return normalized + "jwks/"
|
||||
|
||||
@staticmethod
|
||||
def _infer_userinfo_endpoint(issuer: str | None, base_url: str | None) -> str | None:
|
||||
if issuer:
|
||||
return issuer.rstrip("/") + "/userinfo/"
|
||||
if base_url:
|
||||
return base_url.rstrip("/") + "/application/o/userinfo/"
|
||||
return None
|
||||
|
||||
def _enrich_from_userinfo(self, principal: AuthentikPrincipal, token: str) -> AuthentikPrincipal:
|
||||
if principal.email and (principal.name or principal.preferred_username):
|
||||
return principal
|
||||
if not self.userinfo_endpoint:
|
||||
return principal
|
||||
|
||||
try:
|
||||
resp = httpx.get(
|
||||
self.userinfo_endpoint,
|
||||
timeout=5,
|
||||
verify=self.verify_tls,
|
||||
headers={"Authorization": f"Bearer {token}", "Accept": "application/json"},
|
||||
)
|
||||
except Exception:
|
||||
return principal
|
||||
|
||||
if resp.status_code >= 400:
|
||||
return principal
|
||||
|
||||
data = resp.json() if resp.content else {}
|
||||
sub = data.get("sub")
|
||||
if isinstance(sub, str) and sub and sub != principal.sub:
|
||||
return principal
|
||||
|
||||
email = principal.email or (data.get("email") if isinstance(data.get("email"), str) else None)
|
||||
name = principal.name or (data.get("name") if isinstance(data.get("name"), str) else None)
|
||||
preferred_username = principal.preferred_username or (
|
||||
data.get("preferred_username") if isinstance(data.get("preferred_username"), str) else None
|
||||
)
|
||||
return AuthentikPrincipal(
|
||||
sub=principal.sub,
|
||||
email=email,
|
||||
name=name,
|
||||
preferred_username=preferred_username,
|
||||
)
|
||||
|
||||
def verify_access_token(self, token: str) -> AuthentikPrincipal:
|
||||
try:
|
||||
header = jwt.get_unverified_header(token)
|
||||
@@ -78,12 +131,13 @@ class AuthentikTokenVerifier:
|
||||
if not sub:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="token_missing_sub")
|
||||
|
||||
return AuthentikPrincipal(
|
||||
principal = AuthentikPrincipal(
|
||||
sub=sub,
|
||||
email=claims.get("email"),
|
||||
name=claims.get("name"),
|
||||
preferred_username=claims.get("preferred_username"),
|
||||
)
|
||||
return self._enrich_from_userinfo(principal, token)
|
||||
|
||||
|
||||
@lru_cache
|
||||
@@ -94,6 +148,9 @@ def _get_verifier() -> AuthentikTokenVerifier:
|
||||
jwks_url=settings.authentik_jwks_url,
|
||||
audience=settings.authentik_audience,
|
||||
client_secret=settings.authentik_client_secret,
|
||||
base_url=settings.authentik_base_url,
|
||||
userinfo_endpoint=settings.authentik_userinfo_endpoint,
|
||||
verify_tls=settings.authentik_verify_tls,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user