Fixed auth flow for KEYWARDEN_AUTH_MODE, native; oidc; hybrid work as intended. no login page for OIDC mode, assumes internal IdP for now. will fix when social login implemented
This commit is contained in:
1 parent
68bbb53976
commit
ea7470231f
5 files changed
+70
-27
No files matched your search
+6
-4
@@ -19,9 +19,9 @@ KEYWARDEN_ADMIN_EMAIL=admin@example.com
|
||||
KEYWARDEN_ADMIN_PASSWORD=password
|
||||
|
||||
# Auth mode: native | oidc | hybrid
|
||||
# native: Uses JWT authentication based on Username / Password
|
||||
# oidc: Disables in-build auth, exclusively uses OIDC configuration
|
||||
# hybrid: Allows native auth, prefers oidc
|
||||
# native: Uses Keywarden username/password login only. OIDC/social options are hidden and disabled.
|
||||
# oidc: Disables built-in login and uses OIDC only. Users can be auto-onboarded from IdP claims.
|
||||
# hybrid: Supports OIDC + Keywarden native login; login screen prefers OIDC/social options first.
|
||||
|
||||
KEYWARDEN_AUTH_MODE=native
|
||||
|
||||
@@ -41,14 +41,16 @@ KEYWARDEN_AUTH_MODE=native
|
||||
# KEYWARDEN_OIDC_EMAIL_CLAIM=email
|
||||
# KEYWARDEN_OIDC_EMAIL_VERIFIED_CLAIM=email_verified
|
||||
# KEYWARDEN_OIDC_USERNAME_CLAIM=preferred_username
|
||||
# KEYWARDEN_OIDC_ALLOW_AUTO_ONBOARDING=true
|
||||
# KEYWARDEN_OIDC_GROUPS_CLAIM=groups
|
||||
# KEYWARDEN_OIDC_SYNC_ADMIN_FROM_GROUPS=false
|
||||
# KEYWARDEN_OIDC_SYNC_ADMIN_FROM_GROUPS=true # defaults to true when KEYWARDEN_AUTH_MODE=oidc
|
||||
# KEYWARDEN_OIDC_ADMIN_GROUPS=authentik Admins
|
||||
# KEYWARDEN_OIDC_ADMIN_DEMOTE_ON_MISS=false
|
||||
|
||||
# Social login (optional)
|
||||
# KEYWARDEN_SITE_ID=1
|
||||
# KEYWARDEN_SOCIAL_REQUIRE_VERIFIED_EMAIL=true
|
||||
# KEYWARDEN_SOCIAL_ALLOW_AUTO_ONBOARDING=false
|
||||
# KEYWARDEN_SOCIAL_GOOGLE_ENABLED=false
|
||||
# KEYWARDEN_SOCIAL_GOOGLE_CLIENT_ID=
|
||||
# KEYWARDEN_SOCIAL_GOOGLE_CLIENT_SECRET=
|
||||
|
||||
@@ -61,6 +61,13 @@ class KeywardenSocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
if user:
|
||||
sociallogin.connect(request, user)
|
||||
self._sync_identity(user, sociallogin)
|
||||
return
|
||||
|
||||
if not getattr(settings, "KEYWARDEN_SOCIAL_ALLOW_AUTO_ONBOARDING", False):
|
||||
self._reject(
|
||||
request,
|
||||
"Social auto-onboarding is disabled. Ask an administrator to create/link your account.",
|
||||
)
|
||||
|
||||
def populate_user(self, request, sociallogin, data):
|
||||
user = super().populate_user(request, sociallogin, data)
|
||||
|
||||
@@ -133,7 +133,7 @@ def profile(request):
|
||||
context = {
|
||||
"user": request.user,
|
||||
"auth_mode": getattr(settings, "KEYWARDEN_AUTH_MODE", "hybrid"),
|
||||
"oidc_enabled": bool(getattr(settings, "KEYWARDEN_OIDC_ENABLED", False)),
|
||||
"oidc_enabled": bool(getattr(settings, "KEYWARDEN_OIDC_LOGIN_ENABLED", False)),
|
||||
"oidc_identity": oidc_identity,
|
||||
"oidc_provider_id": getattr(settings, "KEYWARDEN_OIDC_PROVIDER_ID", "oidc"),
|
||||
"oidc_issuer": getattr(settings, "KEYWARDEN_OIDC_ISSUER", ""),
|
||||
@@ -149,25 +149,32 @@ def profile(request):
|
||||
|
||||
def login_view(request):
|
||||
auth_mode = getattr(settings, "KEYWARDEN_AUTH_MODE", "hybrid")
|
||||
if auth_mode == "oidc":
|
||||
oidc_login_enabled = bool(getattr(settings, "KEYWARDEN_OIDC_LOGIN_ENABLED", False))
|
||||
social_providers = (
|
||||
getattr(settings, "KEYWARDEN_SOCIAL_LOGIN_PROVIDERS", [])
|
||||
if auth_mode == "hybrid"
|
||||
else []
|
||||
)
|
||||
|
||||
if auth_mode == "oidc" and oidc_login_enabled:
|
||||
return redirect("/oidc/authenticate/")
|
||||
|
||||
has_sso_options = bool(
|
||||
getattr(settings, "KEYWARDEN_OIDC_ENABLED", False)
|
||||
or getattr(settings, "KEYWARDEN_SOCIAL_LOGIN_PROVIDERS", [])
|
||||
oidc_login_enabled
|
||||
or social_providers
|
||||
)
|
||||
show_native_login = (
|
||||
request.method == "POST"
|
||||
or request.GET.get("native") == "1"
|
||||
or (auth_mode == "native" and not has_sso_options)
|
||||
or auth_mode == "native"
|
||||
)
|
||||
|
||||
# native or hybrid -> render Django's built-in login view
|
||||
return auth_views.LoginView.as_view(
|
||||
template_name="accounts/login.html",
|
||||
extra_context={
|
||||
"social_providers": getattr(settings, "KEYWARDEN_SOCIAL_LOGIN_PROVIDERS", []),
|
||||
"oidc_enabled": bool(getattr(settings, "KEYWARDEN_OIDC_ENABLED", False)),
|
||||
"social_providers": social_providers,
|
||||
"oidc_enabled": oidc_login_enabled,
|
||||
"oidc_login_url": "/oidc/authenticate/",
|
||||
"oidc_provider_label": _oidc_provider_label(),
|
||||
"show_native_login": show_native_login,
|
||||
|
||||
@@ -22,6 +22,12 @@ def env_csv(name: str, default: str = "") -> list[str]:
|
||||
return [item.strip() for item in raw.split(",") if item.strip()]
|
||||
|
||||
|
||||
# Auth mode: native | oidc | hybrid
|
||||
KEYWARDEN_AUTH_MODE_REQUESTED = os.getenv("KEYWARDEN_AUTH_MODE", "hybrid").lower()
|
||||
if KEYWARDEN_AUTH_MODE_REQUESTED not in {"native", "oidc", "hybrid"}:
|
||||
KEYWARDEN_AUTH_MODE_REQUESTED = "hybrid"
|
||||
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
SECRET_KEY = os.getenv("KEYWARDEN_SECRET_KEY")
|
||||
@@ -70,12 +76,15 @@ KEYWARDEN_SOCIAL_APPLE_CONFIGURED = bool(
|
||||
and KEYWARDEN_SOCIAL_APPLE_CLIENT_SECRET
|
||||
)
|
||||
|
||||
KEYWARDEN_SOCIAL_AUTH_ENABLED = any(
|
||||
KEYWARDEN_SOCIAL_AUTH_ENABLED = (
|
||||
KEYWARDEN_AUTH_MODE_REQUESTED == "hybrid"
|
||||
and any(
|
||||
(
|
||||
KEYWARDEN_SOCIAL_GOOGLE_CONFIGURED,
|
||||
KEYWARDEN_SOCIAL_GITHUB_CONFIGURED,
|
||||
KEYWARDEN_SOCIAL_APPLE_CONFIGURED,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Default to secure cookies and respect TLS termination headers.
|
||||
@@ -331,7 +340,8 @@ OIDC_OP_JWKS_ENDPOINT = os.getenv("KEYWARDEN_OIDC_JWKS_ENDPOINT")
|
||||
OIDC_RP_SIGN_ALGO = os.getenv("KEYWARDEN_OIDC_SIGN_ALGO", "RS256")
|
||||
OIDC_RP_SCOPES = os.getenv("KEYWARDEN_OIDC_SCOPES", "openid email profile")
|
||||
OIDC_STORE_ACCESS_TOKEN = env_bool("KEYWARDEN_OIDC_STORE_ACCESS_TOKEN", default=False)
|
||||
OIDC_CREATE_USER = True
|
||||
KEYWARDEN_OIDC_ALLOW_AUTO_ONBOARDING = env_bool("KEYWARDEN_OIDC_ALLOW_AUTO_ONBOARDING", default=True)
|
||||
OIDC_CREATE_USER = KEYWARDEN_OIDC_ALLOW_AUTO_ONBOARDING
|
||||
|
||||
KEYWARDEN_OIDC_PROVIDER_ID = os.getenv("KEYWARDEN_OIDC_PROVIDER_ID", "oidc")
|
||||
KEYWARDEN_OIDC_ISSUER = os.getenv("KEYWARDEN_OIDC_ISSUER", "").strip()
|
||||
@@ -340,7 +350,10 @@ KEYWARDEN_OIDC_EMAIL_VERIFIED_CLAIM = os.getenv("KEYWARDEN_OIDC_EMAIL_VERIFIED_C
|
||||
KEYWARDEN_OIDC_REQUIRE_VERIFIED_EMAIL = env_bool("KEYWARDEN_OIDC_REQUIRE_VERIFIED_EMAIL", default=True)
|
||||
KEYWARDEN_OIDC_USERNAME_CLAIM = os.getenv("KEYWARDEN_OIDC_USERNAME_CLAIM", "preferred_username")
|
||||
KEYWARDEN_OIDC_GROUPS_CLAIM = os.getenv("KEYWARDEN_OIDC_GROUPS_CLAIM", "groups")
|
||||
KEYWARDEN_OIDC_SYNC_ADMIN_FROM_GROUPS = env_bool("KEYWARDEN_OIDC_SYNC_ADMIN_FROM_GROUPS", default=False)
|
||||
KEYWARDEN_OIDC_SYNC_ADMIN_FROM_GROUPS = env_bool(
|
||||
"KEYWARDEN_OIDC_SYNC_ADMIN_FROM_GROUPS",
|
||||
default=(KEYWARDEN_AUTH_MODE_REQUESTED == "oidc"),
|
||||
)
|
||||
KEYWARDEN_OIDC_ADMIN_DEMOTE_ON_MISS = env_bool("KEYWARDEN_OIDC_ADMIN_DEMOTE_ON_MISS", default=False)
|
||||
KEYWARDEN_OIDC_ADMIN_GROUPS = env_csv("KEYWARDEN_OIDC_ADMIN_GROUPS")
|
||||
|
||||
@@ -358,16 +371,18 @@ KEYWARDEN_OIDC_ENABLED = bool(
|
||||
)
|
||||
)
|
||||
|
||||
KEYWARDEN_OIDC_LOGIN_ENABLED = KEYWARDEN_OIDC_ENABLED and KEYWARDEN_AUTH_MODE_REQUESTED in {"oidc", "hybrid"}
|
||||
|
||||
KEYWARDEN_SOCIAL_LOGIN_PROVIDERS = []
|
||||
if KEYWARDEN_SOCIAL_GOOGLE_CONFIGURED:
|
||||
if KEYWARDEN_SOCIAL_AUTH_ENABLED and KEYWARDEN_SOCIAL_GOOGLE_CONFIGURED:
|
||||
KEYWARDEN_SOCIAL_LOGIN_PROVIDERS.append(
|
||||
{"id": "google", "name": "Google", "login_url": "/accounts/sso/google/login/"}
|
||||
)
|
||||
if KEYWARDEN_SOCIAL_GITHUB_CONFIGURED:
|
||||
if KEYWARDEN_SOCIAL_AUTH_ENABLED and KEYWARDEN_SOCIAL_GITHUB_CONFIGURED:
|
||||
KEYWARDEN_SOCIAL_LOGIN_PROVIDERS.append(
|
||||
{"id": "github", "name": "GitHub", "login_url": "/accounts/sso/github/login/"}
|
||||
)
|
||||
if KEYWARDEN_SOCIAL_APPLE_CONFIGURED:
|
||||
if KEYWARDEN_SOCIAL_AUTH_ENABLED and KEYWARDEN_SOCIAL_APPLE_CONFIGURED:
|
||||
KEYWARDEN_SOCIAL_LOGIN_PROVIDERS.append(
|
||||
{"id": "apple", "name": "Apple", "login_url": "/accounts/sso/apple/login/"}
|
||||
)
|
||||
@@ -378,7 +393,8 @@ if KEYWARDEN_SOCIAL_AUTH_ENABLED:
|
||||
ACCOUNT_UNIQUE_EMAIL = True
|
||||
ACCOUNT_USERNAME_REQUIRED = False
|
||||
ACCOUNT_AUTHENTICATION_METHOD = "email"
|
||||
SOCIALACCOUNT_AUTO_SIGNUP = True
|
||||
KEYWARDEN_SOCIAL_ALLOW_AUTO_ONBOARDING = env_bool("KEYWARDEN_SOCIAL_ALLOW_AUTO_ONBOARDING", default=False)
|
||||
SOCIALACCOUNT_AUTO_SIGNUP = KEYWARDEN_SOCIAL_ALLOW_AUTO_ONBOARDING
|
||||
SOCIALACCOUNT_EMAIL_AUTHENTICATION = False
|
||||
SOCIALACCOUNT_ADAPTER = "apps.accounts.social_auth.KeywardenSocialAccountAdapter"
|
||||
SOCIALACCOUNT_LOGIN_ON_GET = True
|
||||
@@ -426,28 +442,37 @@ KEYWARDEN_SOCIAL_REQUIRE_VERIFIED_EMAIL = env_bool(
|
||||
)
|
||||
|
||||
# Auth mode: native | oidc | hybrid
|
||||
AUTH_MODE = os.getenv("KEYWARDEN_AUTH_MODE", "hybrid").lower()
|
||||
if AUTH_MODE not in {"native", "oidc", "hybrid"}:
|
||||
AUTH_MODE = "hybrid"
|
||||
AUTH_MODE = KEYWARDEN_AUTH_MODE_REQUESTED
|
||||
if AUTH_MODE == "oidc" and not KEYWARDEN_OIDC_ENABLED:
|
||||
AUTH_MODE = "native"
|
||||
KEYWARDEN_AUTH_MODE = AUTH_MODE
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
if AUTH_MODE == "oidc":
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"apps.accounts.oidc.KeywardenOIDCAuthenticationBackend",
|
||||
"guardian.backends.ObjectPermissionBackend",
|
||||
]
|
||||
elif AUTH_MODE == "hybrid":
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"guardian.backends.ObjectPermissionBackend",
|
||||
]
|
||||
if KEYWARDEN_SOCIAL_AUTH_ENABLED:
|
||||
]
|
||||
if KEYWARDEN_SOCIAL_AUTH_ENABLED:
|
||||
AUTHENTICATION_BACKENDS.append("allauth.account.auth_backends.AuthenticationBackend")
|
||||
if KEYWARDEN_OIDC_ENABLED:
|
||||
if KEYWARDEN_OIDC_ENABLED:
|
||||
AUTHENTICATION_BACKENDS.append("apps.accounts.oidc.KeywardenOIDCAuthenticationBackend")
|
||||
else:
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"guardian.backends.ObjectPermissionBackend",
|
||||
]
|
||||
|
||||
if AUTH_MODE == "oidc":
|
||||
# OIDC-only: enforce identity provider logins.
|
||||
LOGIN_URL = "/oidc/authenticate/"
|
||||
else:
|
||||
LOGIN_URL = "/accounts/login/"
|
||||
LOGOUT_URL = "/oidc/logout/" if KEYWARDEN_OIDC_ENABLED else "/accounts/logout/"
|
||||
LOGOUT_URL = "/oidc/logout/" if AUTH_MODE in {"oidc", "hybrid"} and KEYWARDEN_OIDC_ENABLED else "/accounts/logout/"
|
||||
LOGIN_REDIRECT_URL = "/servers/"
|
||||
LOGOUT_REDIRECT_URL = "/"
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@ from keywarden.api import api as ninja_api, api_v1 as ninja_api_v1
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("oidc/", include("mozilla_django_oidc.urls")),
|
||||
]
|
||||
|
||||
if getattr(settings, "KEYWARDEN_OIDC_LOGIN_ENABLED", False):
|
||||
urlpatterns.append(path("oidc/", include("mozilla_django_oidc.urls")))
|
||||
|
||||
if getattr(settings, "KEYWARDEN_SOCIAL_AUTH_ENABLED", False):
|
||||
urlpatterns.append(path("accounts/sso/", include("allauth.urls")))
|
||||
|
||||
|
||||
Reference in new issue
Block a user