Admidio is a free open source user management system for websites of organizations and groups. The system has a flexible role model so that it’s possible to reflect the structure and permissions of your organization.
<?php
/**
***********************************************************************************************
* @copyright 2004-2016 The Admidio Team
* @see https://www.admidio.org/
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2.0 only
***********************************************************************************************
*/
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\ValidationData;
use Lcobucci\JWT\Signer\Hmac\Sha256;
/**
* @class JWTManager
* @brief Create and validate a JTW.
*/
class JWTManager
{
/**
* @param int $orgId
* @param int $userId
* @return string
*/
private static function getSignKey($orgId, $userId)
{
global $gPrivateKey, $gDb;
$orgKey = '';//self::getOrgSignKey($gDb, $orgId);
$userKey = '';//self::getUserSignKey($gDb, $userId);
return $gPrivateKey . $orgKey . $userKey;
}
/**
* @param \Database $database
* @param int $orgId
* @return string
*/
private static function getOrgSignKey(\Database $database, $orgId)
{
$sql = 'SELECT org_sign_key
FROM ' . TBL_ORGANIZATIONS . '
WHERE org_id = ' . $orgId;
$statement = $database->query($sql);
return $statement->fetchColumn();
}
/**
* @param \Database $database
* @param int $userId
* @return string
*/
private static function getUserSignKey(\Database $database, $userId)
{
$sql = 'SELECT usr_sign_key
FROM ' . TBL_USERS . '
WHERE usr_id = ' . $userId;
$statement = $database->query($sql);
return $statement->fetchColumn();
}
/**
* @param string $signKey
* @param string $id
* @param int $lifetime
* @param string $audience
* @param string $subject
* @param array $properties
* @return string
*/
private static function build($signKey, $id, $lifetime, $audience, $subject, array $properties = array())
{
$tokenBuilder = new Builder();
foreach ($properties as $key => $value)
{
$tokenBuilder->set($key, $value);
}
$now = time();
$tokenBuilder
->setId($id)
->setIssuer('admidio-jwt-plugin')
->setIssuedAt($now)
->setExpiration($now + $lifetime)
->setAudience($audience)
->setSubject($subject)
;
$tokenBuilder->sign(new Sha256(), $signKey);
return (string) $tokenBuilder->getToken();
}
/**
* @param string $tokenString
* @return Lcobucci\JWT\Token
*/
private static function parse($tokenString)
{
$tokenParser = new Parser();
return $tokenParser->parse((string) $tokenString);
}
/**
* @param Token $token
* @param string $audience
* @param string $subject
* @return bool
*/
private static function validate(Token $token, $audience, $subject)
{
$tokenValData = new ValidationData();
// $tokenValData->setAudience($audience);
// $tokenValData->setSubject($subject);
return $token->validate($tokenValData);
}
/**
* @param Token $token
* @param int $orgId
* @param int $userId
* @return bool
*/
private static function verify(Token $token, $orgId, $userId)
{
$signKey = self::getSignKey($orgId, $userId);
return $token->verify(new Sha256(), $signKey);
}
/**
* @param int $orgId
* @param int $userId
* @param array $properties
* @return string
*/
public static function create($orgId, $userId, array $properties)
{
$signKey = self::getSignKey($orgId, $userId);
$id = '123456'; // TODO: Generate unique ID
$lifetime = 3600;
return self::build($signKey, $id, $lifetime, (string) $orgId, (string) $userId, $properties);
}
/**
* @param string $tokenString
* @return array[]
*/
public static function properties($tokenString)
{
$token = self::parse($tokenString);
$claims = $token->getClaims();
$properties = array();
/**
* @var Lcobucci\JWT\Claim\Basic $claim
*/
foreach ($claims as $key => $claim)
{
$properties[$key] = $claim->getValue();
}
return $properties;
}
/**
* @param string $tokenString
* @return bool
*/
public static function check($tokenString)
{
$token = self::parse($tokenString);
$orgId = (int) $token->getClaim('aud');
$userId = (int) $token->getClaim('sub');
return self::validate($token, (string) $orgId, (string) $userId) && self::verify($token, $orgId, $userId);
}
}
Token Endpoint
<?php
/**
***********************************************************************************************
* @copyright 2004-2016 The Admidio Team
* @see https://www.admidio.org/
* @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License v2.0 only
***********************************************************************************************
*/
$gPrivateKey = 'fra0950v2nc30';
$properties = array('admin' => true, 'roles' => array(1, 3, 6));
require_once('../../src/libs/server/autoload.php');
require_once('common.php');
require_once('jwtmanager.php');
$mode = $_GET['mode'];
if ($mode === 'create')
{
// Initialize parameters
$bAutoLogin = false;
$loginname = '';
$password = '';
$organizationId = (int) $gCurrentOrganization->getValue('org_id');
// Filter parameters
// parameters could be from login dialog or login plugin !!!
/**
* @param string $prefix
*/
function initLoginParams($prefix = '')
{
global $bAutoLogin, $loginname, $password, $organizationId, $gPreferences;
$loginname = $_POST[$prefix.'usr_login_name'];
$password = $_POST[$prefix.'usr_password'];
if($gPreferences['enable_auto_login'] == 1 && array_key_exists($prefix.'auto_login', $_POST) && $_POST[$prefix.'auto_login'] == 1)
{
$bAutoLogin = true;
}
// if user can choose organization then save the selection
if(array_key_exists($prefix.'org_id', $_POST) && is_numeric($_POST[$prefix.'org_id']) && $_POST[$prefix.'org_id'] > 0)
{
$organizationId = (int) $_POST[$prefix.'org_id'];
}
}
if(array_key_exists('usr_login_name', $_POST) && $_POST['usr_login_name'] !== '')
{
initLoginParams('');
}
if(array_key_exists('plg_usr_login_name', $_POST) && $_POST['plg_usr_login_name'] !== '')
{
initLoginParams('plg_');
}
if($loginname === '' || $password === '')
{
$json = array('error' => $gL10n->get('SYS_FIELD_EMPTY', $gL10n->get('SYS_USERNAME') . '/' . $gL10n->get('SYS_PASSWORD')));
echo json_encode($json);
exit();
}
// Search for username
$sql = 'SELECT usr_id
FROM '.TBL_USERS.'
WHERE UPPER(usr_login_name) = UPPER(\''.$loginname.'\')';
$userStatement = $gDb->query($sql);
if ($userStatement->rowCount() === 0)
{
$json = array('error' => $gL10n->get('SYS_LOGIN_USERNAME_PASSWORD_INCORRECT'));
echo json_encode($json);
exit();
}
// if login organization is different to organization of config file then create new session variables
if($organizationId !== (int) $gCurrentOrganization->getValue('org_id'))
{
// read organization of config file with their preferences
$gCurrentOrganization->readDataById($organizationId);
$gPreferences = $gCurrentOrganization->getPreferences();
// read new profile field structure for this organization
$gProfileFields->readProfileFields($organizationId);
// save new organization id to session
$gCurrentSession->setValue('ses_org_id', $organizationId);
$gCurrentSession->save();
}
$userId = (int) $userStatement->fetchColumn();
// create user object
$gCurrentUser = new User($gDb, $gProfileFields, $userId);
$checkLoginReturn = $gCurrentUser->checkLogin($password, $bAutoLogin);
if (is_string($checkLoginReturn))
{
$json = array('error' => $checkLoginReturn);
echo json_encode($json);
exit();
}
$tokenString = JWTManager::create($organizationId, $userId, $properties);
$json = array('token' => $tokenString);
echo json_encode($json);
exit();
}
if ($mode === 'check')
{
$tokenString = $_POST['token'];
$valid = JWTManager::check($tokenString);
$json = array('valid' => $valid);
echo json_encode($json);
exit();
}
if ($mode === 'properties')
{
$tokenString = $_POST['token'];
$properties = JWTManager::properties($tokenString);
$json = array('properties' => $properties);
echo json_encode($json);
exit();
}
// php -S localhost:8000
// curl -X POST --data "usr_login_name=Admin&usr_password=Admidio" http://localhost:8000/adm_program/system/token.php?mode=create
// token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhZG1pbiI6dHJ1ZSwicm9sZXMiOlsxLDMsNl0sImp0aSI6IjEyMzQ1NiIsImlzcyI6ImFkbWlkaW8tand0LXBsdWdpbiIsImlhdCI6MTQ3ODczNDI3MCwiZXhwIjoxNDc4NzM3ODcwLCJhdWQiOiIxIiwic3ViIjoiMSJ9.SFPA82Awde3OP2UkDfEW0WC2gnA2DP7D-AzEIOzeZk8
// curl -X POST --data "token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJSwicm9sZXMiOlsxLDMsNl0sImp0aSI6IjEyMzQ1NiIsImlzcyI6ImFkbWlkaW8tand0LXBsdWdpbiIsImlhdCI6MTQ3ODczNDI3MCwiZXhwIjoxNDc4NzM3ODcwLCJhdWQiOiIxIiwic3ViIjoiMSJ9.SFPA82Awde3OP2UkDfEW0WC2gnA2DP7D-AzEIOzeZk8" http://localhost:8000/adm_program/system/token.php?mode=check
// curl -X POST --data "token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJSwicm9sZXMiOlsxLDMsNl0sImp0aSI6IjEyMzQ1NiIsImlzcyI6ImFkbWlkaW8tand0LXBsdWdpbiIsImlhdCI6MTQ3ODczNDI3MCwiZXhwIjoxNDc4NzM3ODcwLCJhdWQiOiIxIiwic3ViIjoiMSJ9.SFPA82Awde3OP2UkDfEW0WC2gnA2DP7D-AzEIOzeZk8" http://localhost:8000/adm_program/system/token.php?mode=properties
---
Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/39088531-jwt-to-authenticate?utm_campaign=plugin&utm_content=tracker%2F10474012&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F10474012&utm_medium=issues&utm_source=github).
Add JWT (JsonWebToken) as option to authenticate.
Example Code:
Class JWTManager
Token Endpoint