commit c1d457911fd8dbf5d1b7f6b9d3d394afed213fa4
parent 05e6df939cb50575a11a4e5a7d9e8db801c16742
Author: Jan Dankert <develop@jandankert.de>
Date: Thu, 28 Apr 2022 00:28:24 +0200
New: Login with Json webtoken (JWT)
Diffstat:
4 files changed, 79 insertions(+), 16 deletions(-)
diff --git a/modules/cms/Dispatcher.class.php b/modules/cms/Dispatcher.class.php
@@ -173,13 +173,63 @@ class Dispatcher
private function checkLogin() {
if ( $this->request->withAuthorization ) {
- $userAuth = new InternalAuth();
- $status = $userAuth->login( $this->request->authUser,$this->request->authPassword,'' );
- if ( ! ($status & Auth::STATUS_SUCCESS) )
- throw new SecurityException('user cannot be authenticated');
+ $securityConfig = Configuration::subset('security');
+ $authConfig = $securityConfig->subset('authorization');
- $user = User::loadWithName( $this->request->authUser,User::AUTH_TYPE_INTERNAL );
- Request::setUser( $user );
+ if ( $this->request->authUser ) {
+ if ( ! $authConfig->is('basic'))
+ throw new SecurityException('Basic Authorization is disabled');
+
+ $userAuth = new InternalAuth();
+ $status = $userAuth->login( $this->request->authUser,$this->request->authPassword,'' );
+ if ( ! ($status & Auth::STATUS_SUCCESS) )
+ throw new SecurityException('user cannot be authenticated');
+
+ $user = User::loadWithName( $this->request->authUser,User::AUTH_TYPE_INTERNAL );
+ Request::setUser( $user );
+ }
+ elseif ( $authToken = $this->request->authToken ) {
+ if ( ! $authConfig->is('bearer'))
+ throw new SecurityException('Bearer Authorization is disabled');
+
+ // We are expecting a JSON webtoken here
+ function base64url_decode( $data ){
+ return base64_decode( strtr( $data, '-_', '+/') . str_repeat('=', 3 - ( 3 + strlen( $data )) % 4 ));
+ }
+
+ list( $b64Header,$b64Payload,$signature ) = array_pad(explode('.',$authToken),3,'');
+ $header = JSON::decode( base64url_decode($b64Header) );
+ $supportedAlgos = [
+ 'HS256' => 'sha256',
+ 'HS384' => 'sha384',
+ 'HS512' => 'sha512',
+ ];
+ if ( ! $algo = @$supportedAlgos[ @$header['alg'] ] )
+ throw new SecurityException('Unknown algorithm in JWT, only supporting: '.implode(',',array_keys($supportedAlgos) ));
+ elseif( ! in_array( $algo,hash_algos() ) )
+ throw new SecurityException('Unsupported algorithm');
+ elseif( hash_hmac( $algo,$b64Header.'.'.$b64Payload,$securityConfig->get('key'),true) != base64url_decode($signature) )
+ throw new SecurityException('Signature does not match');
+
+ $payload = JSON::decode( base64url_decode($b64Payload));
+
+ if ( $notBefore = @$payload['nbf'] )
+ if ( $notBefore < Startup::getStartTime() )
+ throw new SecurityException('token is not valid');
+
+ if ( $expires = @$payload['exp'] )
+ if ( $expires > Startup::getStartTime() )
+ throw new SecurityException('token is not valid');
+
+ if ( $subject = @$payload['sub'] ) {
+ if ( $user = User::loadWithName( $subject,User::AUTH_TYPE_INTERNAL ) )
+ Request::setUser( $user );
+ else
+ throw new SecurityException('User not found');
+ }
+ else
+ throw new SecurityException('User not found');
+ }
}
}
diff --git a/modules/cms/action/RequestParams.class.php b/modules/cms/action/RequestParams.class.php
@@ -35,6 +35,7 @@ class RequestParams
public $headers;
public $authUser;
public $authPassword;
+ public $authToken;
private $parameter;
@@ -397,12 +398,19 @@ class RequestParams
if ( $auth = @$this->headers['authorization'] ) {
$this->withAuthorization = true;
- if ( substr( $auth,0,6 ) == 'Basic ' )
- list($this->authUser,$this->authPassword) = explode(':',base64_decode( substr( $this->headers['authorization'],6) ) );
- else
- // Only supporting Basic Auth
- // Maybe in the future we will support JWT Bearer tokens...
- error_log('Only supporting basic authorization. Authorization header will be ignored.');
+ list( $type,$value ) = array_pad( explode(' ',$auth),2,'');
+ switch( $type ) {
+ case 'Basic':
+ list($this->authUser,$this->authPassword) = array_pad(explode(':',base64_decode( $value )),2,'' );
+ break;
+ case 'Bearer':
+ $this->authToken = $value;
+ break;
+ default:
+ // Only supporting Basic Auth and Bearer Auth
+ error_log('Only supporting Basic and Bearer authorization. Authorization header will be ignored.');
+ }
+
}
}
}
\ No newline at end of file
diff --git a/modules/cms/base/DefaultConfig.class.php b/modules/cms/base/DefaultConfig.class.php
@@ -348,7 +348,12 @@ class DefaultConfig {
],
],
'security' =>
- [
+ [
+ 'key' => 'OAp5M3GYvm2U7r1FEdvVhqDBxJjMTWgKvN8VfJrHLa7D9pVdrYkMyA',
+ 'authorization' => [
+ 'basic' => true,
+ 'bearer' => false,
+ ],
'cookie' =>
[
'secure' => false,
diff --git a/modules/util/json/JSON.class.php b/modules/util/json/JSON.class.php
@@ -32,10 +32,10 @@ class JSON
}
public static function decode($jsonText) {
- if (!function_exists('json_decode')) {
- return json_decode( $jsonText );
+ if (function_exists('json_decode')) {
+ return json_decode( $jsonText,true );
} else {
- $json = new \JSON();
+ $json = new JSON();
return $json->decode($jsonText);
}
}