This commit is contained in:
@ -2,6 +2,7 @@
|
||||
|
||||
require_once __DIR__ . '/../lib/settings.php';
|
||||
require_once __DIR__ . '/../lib/login_rate_limit.php';
|
||||
require_once __DIR__ . '/../lib/keycloak_auth.php';
|
||||
|
||||
switch ($route) {
|
||||
|
||||
@ -186,6 +187,100 @@ case 'auth/change-password':
|
||||
}
|
||||
break;
|
||||
|
||||
case 'auth/keycloak-config':
|
||||
if ($method !== 'GET') {
|
||||
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
|
||||
}
|
||||
$kcConfig = qdb_keycloak_config();
|
||||
json_success([
|
||||
'enabled' => qdb_keycloak_is_configured($kcConfig),
|
||||
'displayName' => $kcConfig['display_name'],
|
||||
'loginUrl' => qdb_keycloak_is_configured($kcConfig) ? 'auth/keycloak-login' : '',
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'auth/keycloak-login':
|
||||
if ($method !== 'GET') {
|
||||
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
|
||||
}
|
||||
$kcConfig = qdb_keycloak_config();
|
||||
if (!qdb_keycloak_is_configured($kcConfig)) {
|
||||
json_error('KEYCLOAK_NOT_CONFIGURED', 'University login is not configured', 503);
|
||||
}
|
||||
try {
|
||||
$discovery = qdb_keycloak_discovery($kcConfig);
|
||||
$nonce = bin2hex(random_bytes(16));
|
||||
$params = [
|
||||
'client_id' => $kcConfig['client_id'],
|
||||
'redirect_uri' => $kcConfig['redirect_uri'],
|
||||
'response_type' => 'code',
|
||||
'scope' => 'openid profile email',
|
||||
'state' => qdb_keycloak_create_state($nonce),
|
||||
'nonce' => $nonce,
|
||||
];
|
||||
qdb_keycloak_redirect($discovery['authorization_endpoint'] . '?' . http_build_query($params));
|
||||
} catch (Throwable $e) {
|
||||
qdb_handler_fail($e, 'keycloak-login', null, null, null);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'auth/keycloak-callback':
|
||||
if ($method !== 'GET') {
|
||||
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
|
||||
}
|
||||
if (isset($_GET['error'])) {
|
||||
json_error('KEYCLOAK_DENIED', 'University login was cancelled or denied', 401);
|
||||
}
|
||||
$code = trim((string)($_GET['code'] ?? ''));
|
||||
$state = trim((string)($_GET['state'] ?? ''));
|
||||
if ($code === '' || $state === '') {
|
||||
json_error('MISSING_FIELDS', 'Keycloak callback requires code and state', 400);
|
||||
}
|
||||
$kcConfig = qdb_keycloak_config();
|
||||
if (!qdb_keycloak_is_configured($kcConfig)) {
|
||||
json_error('KEYCLOAK_NOT_CONFIGURED', 'University login is not configured', 503);
|
||||
}
|
||||
try {
|
||||
qdb_keycloak_verify_state($state);
|
||||
$discovery = qdb_keycloak_discovery($kcConfig);
|
||||
$tokenResponse = qdb_keycloak_exchange_code($kcConfig, $discovery, $code);
|
||||
$accessToken = (string)($tokenResponse['access_token'] ?? '');
|
||||
if ($accessToken === '') {
|
||||
json_error('KEYCLOAK_TOKEN_FAILED', 'University login did not return an access token', 401);
|
||||
}
|
||||
$claims = qdb_keycloak_userinfo($discovery, $accessToken);
|
||||
$username = qdb_keycloak_claim_username($kcConfig, $claims);
|
||||
|
||||
[$pdo, $tmpDb, $lockFp] = qdb_open_read_or_fail();
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT userID, role, entityID, mustChangePassword
|
||||
FROM users WHERE username = :u"
|
||||
);
|
||||
$stmt->execute([':u' => $username]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
qdb_discard($tmpDb, $lockFp);
|
||||
|
||||
if (!$user) {
|
||||
json_error('KEYCLOAK_USER_NOT_ALLOWED', 'University account is not registered for this application', 403);
|
||||
}
|
||||
qdb_reject_coach_web_login((string)($user['role'] ?? ''));
|
||||
if ((int)($user['mustChangePassword'] ?? 0) === 1) {
|
||||
json_error('PASSWORD_CHANGE_REQUIRED', 'Please sign in locally once to change your temporary password before using University login', 403);
|
||||
}
|
||||
|
||||
$securitySettings = qdb_settings_get();
|
||||
$token = bin2hex(random_bytes(32));
|
||||
token_add($token, qdb_session_ttl_seconds($securitySettings, false), [
|
||||
'role' => $user['role'],
|
||||
'entityID' => $user['entityID'],
|
||||
'userID' => $user['userID'],
|
||||
]);
|
||||
qdb_keycloak_finish_html($token, $username, (string)$user['role']);
|
||||
} catch (Throwable $e) {
|
||||
qdb_handler_fail($e, 'keycloak-callback', null, $tmpDb ?? null, $lockFp ?? null);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
json_error('NOT_FOUND', 'Unknown auth route', 404);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user