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:
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
80
controllers/OIDCLogin.php
Normal file
80
controllers/OIDCLogin.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ class Router
|
||||
'edituser' => 'EditUser',
|
||||
'login' => 'Login',
|
||||
'logout' => 'Logout',
|
||||
'oidclogin' => 'OIDCLogin',
|
||||
'managealbums' => 'ManageAlbums',
|
||||
'manageassets' => 'ManageAssets',
|
||||
'manageerrors' => 'ManageErrors',
|
||||
|
||||
@@ -63,5 +63,18 @@ class LogInForm extends SubTemplate
|
||||
</div>
|
||||
</div>
|
||||
</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>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user