feat: add OIDC admin login for WHMCS

Introduces a new OIDC (OpenID Connect) based authentication mechanism for WHMCS admins, allowing secure logins using an external OIDC provider. This commit adds necessary configuration files, updates the .gitignore to exclude vendor-specific files and the config.php, adds a LICENSE file, and outlines the installation and usage in the README.md. The key functionalities include setting up an OIDC client with the required scopes and handling user authentication and session management within WHMCS.

This enhancement leverages openid-connect-php library for OIDC interactions and streamlines admin login processes, aiming to improve security and ease of use by integrating with modern identity providers.

Note: This module is a proof of concept and should be used cautiously in production environments.
This commit is contained in:
Kumi 2024-04-26 19:00:54 +02:00
commit 28ae0b2761
Signed by: kumi
GPG key ID: ECBCC9082395383F
7 changed files with 420 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
vendor/
config.php

19
LICENSE Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2024 Kumi Systems e.U. <support@kumi.systems>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

45
README.md Normal file
View file

@ -0,0 +1,45 @@
# Simple WHMCS OIDC Admin Login
This is a simple WHMCS OIDC Admin Login module that allows WHMCS administrators
to login to the WHMCS admin area using an OIDC provider.
It is not a full-fledged WHMCS module (yet), but so far only a script that
allows WHMCS administrators to login to the WHMCS admin area using an OIDC
provider.
## Disclaimer
The module was developed as a proof of concept and is not intended for
production use. It may have security vulnerabilities and may not work as
expected.
This module is provided as-is, not supported, and not endorsed by WHMCS. Use it
at your own risk.
## Installation
1. Install the module by uploading it to your WHMCS installation directory.
```bash
cd /path/to/whmcs/modules/security
git clone https://git.private.coffee/kumisystems/whmcs-oidc_admin_login.git oidc_admin_login
```
2. Configure the module by editing the `oidc_admin_login/config.php` file.
## Usage
You can access the OIDC Admin Login page by visiting the following URL:
```
https://your-whmcs-domain.com/modules/security/oidc_admin_login.php
```
If you are not logged in, you will be redirected to the OIDC provider login
page. After you have logged in, you will be redirected back to the WHMCS admin
area.
## License
This module is licensed under the MIT License. See the [LICENSE](LICENSE) file
for details.

5
composer.json Normal file
View file

@ -0,0 +1,5 @@
{
"require": {
"jumbojett/openid-connect-php": "^1.0"
}
}

287
composer.lock generated Normal file
View file

@ -0,0 +1,287 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c6deed93995bf2c39b35a421c16edbdd",
"packages": [
{
"name": "jumbojett/openid-connect-php",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/jumbojett/OpenID-Connect-PHP.git",
"reference": "4af1d11497ec765dccddf928c14c04535ca96017"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jumbojett/OpenID-Connect-PHP/zipball/4af1d11497ec765dccddf928c14c04535ca96017",
"reference": "4af1d11497ec765dccddf928c14c04535ca96017",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"php": ">=7.0",
"phpseclib/phpseclib": "~3.0"
},
"require-dev": {
"roave/security-advisories": "dev-latest",
"yoast/phpunit-polyfills": "^1.0"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"description": "Bare-bones OpenID Connect client",
"support": {
"issues": "https://github.com/jumbojett/OpenID-Connect-PHP/issues",
"source": "https://github.com/jumbojett/OpenID-Connect-PHP/tree/v1.0.0"
},
"time": "2023-12-13T09:52:12+00:00"
},
{
"name": "paragonie/constant_time_encoding",
"version": "v2.6.3",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "58c3f47f650c94ec05a151692652a868995d2938"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938",
"reference": "58c3f47f650c94ec05a151692652a868995d2938",
"shasum": ""
},
"require": {
"php": "^7|^8"
},
"require-dev": {
"phpunit/phpunit": "^6|^7|^8|^9",
"vimeo/psalm": "^1|^2|^3|^4"
},
"type": "library",
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base16",
"base32",
"base32_decode",
"base32_encode",
"base64",
"base64_decode",
"base64_encode",
"bin2hex",
"encoding",
"hex",
"hex2bin",
"rfc4648"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"time": "2022-06-14T06:56:20+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.100",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
"shasum": ""
},
"require": {
"php": ">= 7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/random_compat/issues",
"source": "https://github.com/paragonie/random_compat"
},
"time": "2020-10-15T08:29:30+00:00"
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.37",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "cfa2013d0f68c062055180dd4328cc8b9d1f30b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/cfa2013d0f68c062055180dd4328cc8b9d1f30b8",
"reference": "cfa2013d0f68c062055180dd4328cc8b9d1f30b8",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1|^2",
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
"php": ">=5.6.1"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"suggest": {
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
"type": "library",
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib3\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"homepage": "http://phpseclib.sourceforge.net",
"keywords": [
"BigInteger",
"aes",
"asn.1",
"asn1",
"blowfish",
"crypto",
"cryptography",
"encryption",
"rsa",
"security",
"sftp",
"signature",
"signing",
"ssh",
"twofish",
"x.509",
"x509"
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.37"
},
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"time": "2024-03-03T02:14:58+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.2.0"
}

11
config.dist.php Normal file
View file

@ -0,0 +1,11 @@
<?php
if (!defined("WHMCS")) {
die("This file cannot be accessed directly");
}
// OIDC Provider details
$oidcProviderUrl = 'https://kumidc.local'; // The URL of your OIDC Provider
$clientID = '123456'; // Your OIDC Client ID
$clientSecret = 'thisisaverylongsecretstring'; // Your OIDC Client Secret

51
oidc_admin_login.php Normal file
View file

@ -0,0 +1,51 @@
<?php
require_once 'vendor/autoload.php';
require_once "../../../init.php";
require_once "config.php";
use Jumbojett\OpenIDConnectClient;
// Initialize the OIDC client
$oidc = new OpenIDConnectClient($oidcProviderUrl, $clientID, $clientSecret);
$oidc->addScope(['openid', 'email', 'profile']);
try {
// Authenticate the user with the OIDC provider
$oidc->authenticate();
// Fetch the user's details
$userInfo = $oidc->requestUserInfo();
// The 'preferred_username' claim will be used as the WHMCS username
if (isset($userInfo->preferred_username)) {
$username = $userInfo->preferred_username;
// Initialize WHMCS authentication class
$auth = new WHMCS\Auth();
// Attempt to find and authenticate the user by username
if ($auth->getInfobyUsername($username)) {
// Set session variables for the logged-in user
$auth->setSessionVars();
$redirectUri = '/admin/';
header('Location: ' . $redirectUri);
exit;
} else {
// Handle the error case where the username doesn't exist in WHMCS
error_log("OIDC SSO login failed: Username not found in WHMCS");
// TODO: Redirect to a failure page
die("OIDC SSO login failed: Username not found in WHMCS");
}
} else {
// Handle missing username claim
error_log("OIDC SSO login failed: Username claim not found in user info");
exit;
}
} catch (Exception $e) {
// Handle errors, such as authentication failures
error_log("OIDC SSO login error: " . $e->getMessage());
die("OIDC SSO login error: " . $e->getMessage());
}