feat(auth): admin-configurable OIDC provider (oidc_settings)
Persist OIDC config in a single-row oidc_settings table; non-empty DB fields override the OIDC_* env vars (env = bootstrap default). The Authlib client is rebuilt when config changes. - GET/PUT /auth/oidc/settings — admin only, via JWT OR API key. The API-key path is the recovery channel when OIDC-only mode is on and OIDC is misconfigured (avoids total lockout). - client_secret is write-only: never returned (has_client_secret bool), preserved when the field is left blank on update. - /auth/config, login/link/callback now use the effective (DB|env) config so enabling OIDC needs no redeploy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
22
app/models/oidc_settings.py
Normal file
22
app/models/oidc_settings.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime
|
||||
from sqlalchemy.sql import func
|
||||
from app.core.config import Base
|
||||
|
||||
|
||||
class OidcSettings(Base):
|
||||
"""Single-row (id=1) runtime OIDC configuration.
|
||||
|
||||
When a row exists its non-empty fields override the OIDC_* env vars,
|
||||
so the provider can be configured from the admin UI without a redeploy.
|
||||
"""
|
||||
__tablename__ = "oidc_settings"
|
||||
|
||||
id = Column(Integer, primary_key=True, default=1)
|
||||
enabled = Column(Boolean, default=False, nullable=False)
|
||||
issuer = Column(String(255), nullable=True)
|
||||
client_id = Column(String(255), nullable=True)
|
||||
client_secret = Column(String(512), nullable=True)
|
||||
redirect_uri = Column(String(512), nullable=True)
|
||||
scopes = Column(String(255), nullable=True)
|
||||
post_login_redirect = Column(String(512), nullable=True)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
Reference in New Issue
Block a user