feat(auth): Authentik OIDC sign-in + Gravatar avatars
Adds optional SSO via Authentik (or any OIDC provider) alongside the existing password flow, and pulls profile images from the provider's `picture` claim or Gravatar so the sharing UI stops looking anonymous. Password login stays available as a recovery path; JIT provisioning and admin-group mapping are env-configurable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,41 @@ class Settings(BaseSettings):
|
||||
access_token_expire_minutes: int = Field(default=525600, env="ACCESS_TOKEN_EXPIRE_MINUTES") # 1 year
|
||||
refresh_token_expire_days: int = Field(default=3650, env="REFRESH_TOKEN_EXPIRE_DAYS") # 10 years
|
||||
|
||||
# ── OIDC / Authentik single sign-on ────────────────────────────────
|
||||
# Disabled by default; enable by setting OIDC_ENABLED=true and the
|
||||
# issuer + client credentials. When enabled the login page shows a
|
||||
# "Sign in with {label}" button alongside the username/password form.
|
||||
oidc_enabled: bool = Field(default=False, env="OIDC_ENABLED")
|
||||
oidc_issuer: Optional[str] = Field(default=None, env="OIDC_ISSUER")
|
||||
oidc_client_id: Optional[str] = Field(default=None, env="OIDC_CLIENT_ID")
|
||||
oidc_client_secret: Optional[str] = Field(default=None, env="OIDC_CLIENT_SECRET")
|
||||
# Absolute URL the IdP redirects back to. Must match the Redirect URI
|
||||
# configured on the Authentik side exactly.
|
||||
oidc_redirect_uri: Optional[str] = Field(default=None, env="OIDC_REDIRECT_URI")
|
||||
oidc_scopes: str = Field(default="openid profile email", env="OIDC_SCOPES")
|
||||
oidc_provider_label: str = Field(default="Authentik", env="OIDC_PROVIDER_LABEL")
|
||||
# When true, a successful OIDC login for a subject we've never seen
|
||||
# auto-creates a local user + their /photos/{username} folder. When
|
||||
# false, unknown subjects get 403 and must be pre-provisioned.
|
||||
oidc_allow_signup: bool = Field(default=True, env="OIDC_ALLOW_SIGNUP")
|
||||
# Comma-separated Authentik group names. Any group-claim match
|
||||
# promotes the user to role=admin; otherwise role=user. Role is
|
||||
# refreshed on every sign-in so removals demote automatically.
|
||||
oidc_admin_groups: str = Field(default="", env="OIDC_ADMIN_GROUPS")
|
||||
# Starlette session cookie secret — only used to hold PKCE/state
|
||||
# during the brief OIDC round-trip. Falls back to secret_key when
|
||||
# unset.
|
||||
session_secret: Optional[str] = Field(default=None, env="SESSION_SECRET")
|
||||
|
||||
@property
|
||||
def oidc_admin_group_list(self) -> list[str]:
|
||||
raw = (self.oidc_admin_groups or "").strip()
|
||||
return [g.strip() for g in raw.split(",") if g.strip()]
|
||||
|
||||
@property
|
||||
def effective_session_secret(self) -> str:
|
||||
return self.session_secret or self.secret_key
|
||||
|
||||
@property
|
||||
def cors_origins(self) -> list[str]:
|
||||
"""Parse the ALLOWED_ORIGINS env var into a list. Accepts:
|
||||
|
||||
Reference in New Issue
Block a user