Add OIDC login support for external identity providers

Adds "Login with <provider>" 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 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 16:08:45 +01:00
parent c862d14e45
commit a361df2668
5 changed files with 102 additions and 1 deletions

View File

@@ -22,6 +22,7 @@
"ext-imagick": "*", "ext-imagick": "*",
"ext-mysqli": "*", "ext-mysqli": "*",
"twbs/bootstrap": "^5.3", "twbs/bootstrap": "^5.3",
"twbs/bootstrap-icons": "^1.10" "twbs/bootstrap-icons": "^1.10",
"jumbojett/openid-connect-php": "^1.0"
} }
} }

View File

@@ -34,3 +34,9 @@ const DB_LOG_QUERIES = false;
const SITE_TITLE = 'HashRU Pics'; const SITE_TITLE = 'HashRU Pics';
const SITE_SLOGAN = 'Nijmeegs Nerdclubje'; 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

80
controllers/OIDCLogin.php Normal file
View File

@@ -0,0 +1,80 @@
<?php
/*****************************************************************************
* OIDCLogin.php
* Handles OIDC authentication via Kanidm (or other OIDC providers).
*****************************************************************************/
use Jumbojett\OpenIDConnectClient;
class OIDCLogin
{
public function __construct()
{
if (empty(OIDC_PROVIDER_URL))
{
header('Location: ' . BASEURL . '/login/');
exit;
}
// Already logged in? Redirect home.
if (Registry::get('user')->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;
}
}

View File

@@ -20,6 +20,7 @@ class Router
'edituser' => 'EditUser', 'edituser' => 'EditUser',
'login' => 'Login', 'login' => 'Login',
'logout' => 'Logout', 'logout' => 'Logout',
'oidclogin' => 'OIDCLogin',
'managealbums' => 'ManageAlbums', 'managealbums' => 'ManageAlbums',
'manageassets' => 'ManageAssets', 'manageassets' => 'ManageAssets',
'manageerrors' => 'ManageErrors', 'manageerrors' => 'ManageErrors',

View File

@@ -63,5 +63,18 @@ class LogInForm extends SubTemplate
</div> </div>
</div> </div>
</form>'; </form>';
if (!empty(OIDC_PROVIDER_URL))
{
$oidc_url = BASEURL . '/oidclogin/';
if (!empty($this->redirect_url))
$oidc_url .= '?redirect=' . base64_encode($this->redirect_url);
echo '
<div class="mt-4 text-center">
<hr>
<a class="btn btn-secondary" href="', htmlspecialchars($oidc_url), '">Login with ', htmlspecialchars(OIDC_PROVIDER_NAME), '</a>
</div>';
}
} }
} }