From a361df26686c5df2378019a2332d2cea17fb73c8 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Sat, 14 Feb 2026 16:08:45 +0100 Subject: [PATCH] Add OIDC login support for external identity providers Adds "Login with " as an alternative login method using the jumbojett/openid-connect-php library. OIDC users must already exist in the database (matched by email). Configurable via OIDC_PROVIDER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, and OIDC_PROVIDER_NAME constants. Co-Authored-By: Claude Opus 4.6 --- composer.json | 3 +- config.php.dist | 6 +++ controllers/OIDCLogin.php | 80 +++++++++++++++++++++++++++++++++++++++ models/Router.php | 1 + templates/LogInForm.php | 13 +++++++ 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 controllers/OIDCLogin.php diff --git a/composer.json b/composer.json index a4b52e4..4294764 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "ext-imagick": "*", "ext-mysqli": "*", "twbs/bootstrap": "^5.3", - "twbs/bootstrap-icons": "^1.10" + "twbs/bootstrap-icons": "^1.10", + "jumbojett/openid-connect-php": "^1.0" } } diff --git a/config.php.dist b/config.php.dist index 0c86137..587c36d 100644 --- a/config.php.dist +++ b/config.php.dist @@ -34,3 +34,9 @@ const DB_LOG_QUERIES = false; const SITE_TITLE = 'HashRU Pics'; const SITE_SLOGAN = 'Nijmeegs Nerdclubje'; + +// OIDC authentication (e.g. Kanidm). OIDC is enabled when OIDC_PROVIDER_URL is non-empty. +const OIDC_PROVIDER_URL = ''; // e.g. 'https://kanidm.example.com/oauth2/openid/pics' +const OIDC_CLIENT_ID = ''; +const OIDC_CLIENT_SECRET = ''; +const OIDC_PROVIDER_NAME = ''; // e.g. 'Kanidm' — used as button label diff --git a/controllers/OIDCLogin.php b/controllers/OIDCLogin.php new file mode 100644 index 0000000..5216661 --- /dev/null +++ b/controllers/OIDCLogin.php @@ -0,0 +1,80 @@ +isLoggedIn()) + { + header('Location: ' . BASEURL . '/'); + exit; + } + + // Store redirect URL in session before OIDC flow. + if (isset($_GET['redirect'])) + $_SESSION['oidc_redirect_url'] = base64_decode($_GET['redirect']); + + $oidc = new OpenIDConnectClient(OIDC_PROVIDER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET); + $oidc->setRedirectURL(BASEURL . '/oidclogin/'); + $oidc->addScope(['openid', 'email']); + + try + { + $oidc->authenticate(); + } + catch (\Exception $e) + { + $_SESSION['login_msg'] = ['', 'OIDC authentication failed: ' . $e->getMessage(), 'danger']; + header('Location: ' . BASEURL . '/login/'); + exit; + } + + $email = $oidc->requestUserInfo('email'); + if (empty($email)) + { + $_SESSION['login_msg'] = ['', 'No email address received from OIDC provider.', 'danger']; + header('Location: ' . BASEURL . '/login/'); + exit; + } + + $user = Member::fromEmailAddress($email); + if ($user === null || $user === false) + { + $_SESSION['login_msg'] = ['', 'No account found for this email address. Please contact an administrator.', 'danger']; + header('Location: ' . BASEURL . '/login/'); + exit; + } + + $_SESSION['user_id'] = $user->getUserId(); + + if (!empty($_SESSION['oidc_redirect_url'])) + { + $redirect = $_SESSION['oidc_redirect_url']; + unset($_SESSION['oidc_redirect_url']); + header('Location: ' . $redirect); + } + elseif (!empty($_SESSION['login_url'])) + { + $redirect = $_SESSION['login_url']; + unset($_SESSION['login_url']); + header('Location: ' . $redirect); + } + else + header('Location: ' . BASEURL . '/'); + + exit; + } +} diff --git a/models/Router.php b/models/Router.php index ac0e2ad..189bfd0 100644 --- a/models/Router.php +++ b/models/Router.php @@ -20,6 +20,7 @@ class Router 'edituser' => 'EditUser', 'login' => 'Login', 'logout' => 'Logout', + 'oidclogin' => 'OIDCLogin', 'managealbums' => 'ManageAlbums', 'manageassets' => 'ManageAssets', 'manageerrors' => 'ManageErrors', diff --git a/templates/LogInForm.php b/templates/LogInForm.php index 0e0c96a..5f04b6e 100644 --- a/templates/LogInForm.php +++ b/templates/LogInForm.php @@ -63,5 +63,18 @@ class LogInForm extends SubTemplate '; + + if (!empty(OIDC_PROVIDER_URL)) + { + $oidc_url = BASEURL . '/oidclogin/'; + if (!empty($this->redirect_url)) + $oidc_url .= '?redirect=' . base64_encode($this->redirect_url); + + echo ' + '; + } } }