Initial commit
This commit is contained in:
commit
037ee17766
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016-2017 meta-tech.academy
|
||||
|
||||
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.
|
99
README.md
Normal file
99
README.md
Normal file
|
@ -0,0 +1,99 @@
|
|||
# PwsAuth
|
||||
|
||||
PwsAuth is an authentication protocol throught http header designed to web services
|
||||
|
||||
## Request Headers
|
||||
|
||||
request headers must be define as follow :
|
||||
|
||||
Pws-Authorization : $type $token
|
||||
Pws-Ident : $userkey
|
||||
|
||||
the **$token** can be either a `loginToken` or a `sessionToken`
|
||||
|
||||
the **$token** is divided in four part
|
||||
|
||||
* a datetime formatted with the `Authenticator::DATE_FORMAT` format
|
||||
* an obfuscate part 's token builded by date, common salt & the third token 's part
|
||||
* a loginToken representing a user signed token for a specific login at given date
|
||||
OR
|
||||
a session token representing the session id
|
||||
* noise data to be removed
|
||||
|
||||
the complete token is valid only if obfuscate part can be rebuild
|
||||
this simple mecanism ensure that **sessionId** is valid and can be safety load
|
||||
|
||||
Authenticator 's configuration comes with a `hash.session.index` and `hash.noise.length` values
|
||||
wich can be redefined to move the session token part into the complete token
|
||||
|
||||
<< hash.session.index >> << hash.noise.length >>
|
||||
|-----------------------------------------------------------<<-^->>---------------------------------------------<<-^->>--------|
|
||||
|- type -|-- date ---|------------ obfuscate token ---------<<-^->>-------------- session token ----------------<<-^->> noise -|
|
||||
| | 1 | 2 | 3 | 4 |
|
||||
PwsAuth2 242003031711e1a6104135f04c6c01e6cd5952ecafbb53c928603b0gb64tqo609qse6ovd7lhdvk4fnaqk7cdl26e4d4qh7jb41eu5f1zb5y79m8pgu3
|
||||
|
||||
|
||||
### ClientSide
|
||||
|
||||
a request header can be generated via the `generateHeader($login, $key, $sessid=null)` method
|
||||
the third parameter determine wich kind of token will be generated
|
||||
|
||||
### ServerSide
|
||||
|
||||
the Token can be retriew via the `getToken` method
|
||||
|
||||
`loginToken` is validate by the `check(Token $token, $login)` method
|
||||
`loginToken` must match a public url with method `POST` and a couple of login/password
|
||||
on successfull login, the session id must be transmit to the client.
|
||||
|
||||
`sessionToken` is valid only if the session can effectively be loaded, and the
|
||||
user key match the given `Pws-Ident` value
|
||||
|
||||
### Configuration
|
||||
|
||||
configuration must be the same on server and client sides
|
||||
hash definition is a convenient way to obfuscate your tokens
|
||||
|
||||
```yaml
|
||||
pwsauth :
|
||||
|
||||
type : PwsAuth2
|
||||
|
||||
header :
|
||||
auth : Pws-Authorization
|
||||
ident : Pws-Ident
|
||||
|
||||
salt :
|
||||
common : jK5#p9Mh5.Zv}
|
||||
# used for generating user specific salt
|
||||
user.index : 10
|
||||
user.length : 12
|
||||
|
||||
hash :
|
||||
sep : /
|
||||
algo : sha256
|
||||
# effective token length size. out of bound data is simply noise
|
||||
length : 52
|
||||
# session index (or obfuscate length)
|
||||
session.index : 58
|
||||
# ending noise data length)
|
||||
noise.length : 12
|
||||
```
|
||||
|
||||
### Authenticator instanciation
|
||||
|
||||
```php
|
||||
<?php
|
||||
require_once(__dir__ . '/vendor/autoload.php');
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use MetaTech\PwsAuth\Authenticator;
|
||||
|
||||
$config = Yaml::parse(file_get_contents(__dir__ . '/config/pwsauth.yml'));
|
||||
$authenticator = new Authenticator($config['pwsauth']);
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
a valid `$userkey` alone is useless
|
||||
a valid `$sessionId` alone is useless
|
257
src/MetaTech/PwsAuth/Authenticator.php
Normal file
257
src/MetaTech/PwsAuth/Authenticator.php
Normal file
|
@ -0,0 +1,257 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of the PwsAuth package.
|
||||
*
|
||||
* (c) meta-tech.academy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace MetaTech\PwsAuth;
|
||||
|
||||
use MetaTech\Util\Tool;
|
||||
use MetaTech\PwsAuth\Token;
|
||||
use MetaTech\PwsAuth\AuthenticateException;
|
||||
|
||||
/*!
|
||||
* a simple class to authenticate access throught webservices using Pluie\Auth\Token
|
||||
* and PwsAuth Protocol
|
||||
*
|
||||
* @package MetaTech\PwsAuth
|
||||
* @class Authenticator
|
||||
* @author a-Sansara
|
||||
* @date 2016-05-02 13:08:01 CET
|
||||
*
|
||||
*/
|
||||
class Authenticator
|
||||
{
|
||||
/*! @constant DATE_FORMAT */
|
||||
const DATE_FORMAT = 'smHdiy';
|
||||
/*! @constant DATE_LENGTH */
|
||||
const DATE_LENGTH = 12;
|
||||
/*! @constant DATE_LENGTH */
|
||||
const DEFAULT_ALGO = 'sha256';
|
||||
|
||||
/*! @protected @var [assoc] $config */
|
||||
protected $config;
|
||||
|
||||
/*!
|
||||
* @constructor
|
||||
* @public
|
||||
*/
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/*!
|
||||
* check if specified Token is a valid token
|
||||
*
|
||||
* @method isValid
|
||||
* @public
|
||||
* @param Pluie\Auth\Token $token
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(Token $token)
|
||||
{
|
||||
return $token->getType() == $this->config['type'] && $this->checkObfuscatePart($token);
|
||||
}
|
||||
|
||||
/*!
|
||||
* generate a unique signature at given time for specifyed user
|
||||
*
|
||||
* @method sign
|
||||
* @public
|
||||
* @param str $dtime given time in sqldatetime format
|
||||
* @param str $login the user login
|
||||
* @param str $key the user key
|
||||
* @return str
|
||||
*/
|
||||
public function sign($dtime, $login, $key, $length=null)
|
||||
{
|
||||
$str = Tool::concat($this->config['hash']['sep'], [$dtime, $login, $this->getUserSalt($login), $key]);
|
||||
return substr(hash($this->config['hash']['algo'], $str), is_null($length) ? - $this->config['hash']['length'] : - $length);
|
||||
}
|
||||
|
||||
/*!
|
||||
* generate the salt for a specific user
|
||||
*
|
||||
* @method getUserSalt
|
||||
* @public
|
||||
* @param str $login the user login
|
||||
* @return str
|
||||
*/
|
||||
public function getUserSalt($login)
|
||||
{
|
||||
return substr(
|
||||
hash(self::DEFAULT_ALGO, $login . $this->config['salt']['common']),
|
||||
$this->config['salt']['user.index'],
|
||||
$this->config['salt']['user.length']
|
||||
);
|
||||
}
|
||||
|
||||
/*!
|
||||
* generate noise to obfuscate token
|
||||
*
|
||||
* @method obfuscate
|
||||
* @orivate
|
||||
* @param str $data
|
||||
* @return str
|
||||
*/
|
||||
private function obfuscate($data, $date)
|
||||
{
|
||||
return substr(
|
||||
hash(self::DEFAULT_ALGO, $date . $data . $this->config['salt']['common']),
|
||||
- $this->config['hash']['session.index']
|
||||
);
|
||||
}
|
||||
|
||||
/*!
|
||||
* check valid noise obfuscation
|
||||
*
|
||||
* @method checkObfuscatePart
|
||||
* @public
|
||||
* @param Pluie\Auth\Token $token
|
||||
* @return bool
|
||||
*/
|
||||
public function checkObfuscatePart(Token $token)
|
||||
{
|
||||
$tokenValue = $token->getValue();
|
||||
return substr($tokenValue, 0, $this->config['hash']['session.index']) == $this->obfuscate($this->deobfuscate($tokenValue), $token->getDate());
|
||||
}
|
||||
|
||||
/*!
|
||||
* deoffuscate token
|
||||
*
|
||||
* @method deobfuscate
|
||||
* @orivate
|
||||
* @param str $data
|
||||
* @return str
|
||||
*/
|
||||
private function deobfuscate($data)
|
||||
{
|
||||
return substr($data, $this->config['hash']['session.index']);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @method getSessionId
|
||||
* @orivate
|
||||
* @param Pluie\Auth\Token $token
|
||||
* @return str
|
||||
*/
|
||||
public function getSessionId(Token $token)
|
||||
{
|
||||
return $this->deobfuscate($token->getValue());
|
||||
}
|
||||
|
||||
/*!
|
||||
* check validity of Token
|
||||
*
|
||||
* @mehtod check
|
||||
* @public
|
||||
* @param Pluie\Auth\Token $token
|
||||
* @param str $login
|
||||
* @return bool
|
||||
*/
|
||||
public function check(Token $token, $login)
|
||||
{
|
||||
return !is_null($token) && $this->deobfuscate($token->getValue()) == $this->sign($token->getDate(), $login, $token->getIdent());
|
||||
}
|
||||
|
||||
/*!
|
||||
* @method generateNoise
|
||||
* @public
|
||||
* @param str $data
|
||||
* @return str
|
||||
*/
|
||||
public function generateNoise($data)
|
||||
{
|
||||
return substr(hash(self::DEFAULT_ALGO, str_shuffle($data)), - $this->config['hash']['noise.length']);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @method generateToken
|
||||
* @public
|
||||
* @param str $login
|
||||
* @param str $key
|
||||
* @param str $sessid|null
|
||||
* @return Pluie\Auth\Token
|
||||
*/
|
||||
public function generateToken($login, $key, $sessid=null)
|
||||
{
|
||||
$date = Tool::now();
|
||||
$sessid = is_null($sessid) ? $this->sign($date, $login, $key) : $sessid;
|
||||
$dt = Tool::formatDate($date, Tool::TIMESTAMP_SQLDATETIME, self::DATE_FORMAT);
|
||||
$tokenValue = $dt . $this->obfuscate($sessid, $date) . $sessid;
|
||||
$noise = $this->generateNoise($tokenValue);
|
||||
return new Token($this->config['type'], $key, $date, $tokenValue, $noise);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @method generateHeader
|
||||
* @public
|
||||
* @param str $login
|
||||
* @param str $key
|
||||
* @param str $sessid
|
||||
* @return []
|
||||
*/
|
||||
public function generateHeader($login, $key, $sessid=null)
|
||||
{
|
||||
$token = $this->generateToken($login, $key, $sessid);
|
||||
return array(
|
||||
$this->config['header']['auth'] .': ' . $token->getType() . ' ' . $token->getValue() . $token->getNoise(),
|
||||
$this->config['header']['ident'].': ' . $token->getIdent()
|
||||
);
|
||||
}
|
||||
|
||||
/*!
|
||||
* get token from specified $header or request headers.
|
||||
*
|
||||
* @method getToken
|
||||
* @public
|
||||
* @param [assoc] $headers
|
||||
* @throw Pluie\Auth\AuthenticateException
|
||||
* @return Pluie\Auth\Token
|
||||
*/
|
||||
public function getToken($headers = null)
|
||||
{
|
||||
$token = null;
|
||||
try {
|
||||
if (is_null($headers)) {
|
||||
$headers = apache_request_headers();
|
||||
}
|
||||
$tokenValue = $headers[$this->config['header']['auth']] ?: '';
|
||||
$ident = $headers[$this->config['header']['ident']] ?: '';
|
||||
if (preg_match('/(?P<type>[a-z\d]+) (?P<date>\d{'.self::DATE_LENGTH.'})(?P<id>[a-z\d]+)/i', $tokenValue, $rs)) {
|
||||
$date = Tool::formatDate($rs['date'], self::DATE_FORMAT, Tool::TIMESTAMP_SQLDATETIME);
|
||||
$tokenValue = substr($rs['id'], 0, -$this->config['hash']['noise.length']);
|
||||
$noise = substr($rs['id'], -$this->config['hash']['noise.length']);
|
||||
$token = new Token($rs['type'], $ident, $date, $tokenValue, $noise);
|
||||
}
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
throw new AuthenticateException("invalid authentication protocol : ".$e->getMessage());
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
/*!
|
||||
* read header generate by generateHeader
|
||||
*
|
||||
* @method readHeader
|
||||
* @public
|
||||
* @param [str] $arrHeaders
|
||||
* @return [assoc]
|
||||
*/
|
||||
public function readHeader($arrHeaders)
|
||||
{
|
||||
$headers = [];
|
||||
foreach($arrHeaders as $h) {
|
||||
$rs = preg_split('/:/', $h);
|
||||
if (count($rs)==2) {
|
||||
$headers[$rs[0]] = trim($rs[1]);
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
111
src/MetaTech/PwsAuth/Token.php
Normal file
111
src/MetaTech/PwsAuth/Token.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of the PwsAuth package.
|
||||
*
|
||||
* (c) meta-tech.academy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace MetaTech\PwsAuth;
|
||||
|
||||
/*!
|
||||
* PwsAuth token
|
||||
*
|
||||
* @package MetaTech\PwsAuth
|
||||
* @class Token
|
||||
* @author a-Sansara
|
||||
* @date 2016-05-02 13:16:01 CET
|
||||
*/
|
||||
class Token
|
||||
{
|
||||
/*! @protected @var $type */
|
||||
protected $type = null;
|
||||
/*! @protected @var $ident */
|
||||
protected $ident = null;
|
||||
/*! @protected @var $date */
|
||||
protected $date = null;
|
||||
/*! @protected @var $token */
|
||||
protected $value = null;
|
||||
/*! @protected @var $noise */
|
||||
protected $noise = null;
|
||||
|
||||
/*!
|
||||
* @constructor
|
||||
* @param str $type
|
||||
* @param str $ident
|
||||
* @param str $date
|
||||
* @param str $value
|
||||
* @param str $noise
|
||||
* @public
|
||||
*/
|
||||
public function __construct($type, $ident, $date, $value, $noise)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->ident = $ident;
|
||||
$this->date = $date;
|
||||
$this->value = $value;
|
||||
$this->noise = $noise;
|
||||
}
|
||||
|
||||
/*!
|
||||
* desc
|
||||
*
|
||||
* @method getType
|
||||
* @public
|
||||
* @return str
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/*!
|
||||
* desc
|
||||
*
|
||||
* @method getIdent
|
||||
* @public
|
||||
* @return str
|
||||
*/
|
||||
public function getIdent()
|
||||
{
|
||||
return $this->ident;
|
||||
}
|
||||
|
||||
/*!
|
||||
* desc
|
||||
*
|
||||
* @method getDate
|
||||
* @public
|
||||
* @return str
|
||||
*/
|
||||
public function getDate()
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
/*!
|
||||
* desc
|
||||
*
|
||||
* @method getValue
|
||||
* @public
|
||||
* @return str
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/*!
|
||||
* desc
|
||||
*
|
||||
* @method getNoise
|
||||
* @public
|
||||
* @return str
|
||||
*/
|
||||
public function getNoise()
|
||||
{
|
||||
return $this->noise;
|
||||
}
|
||||
|
||||
}
|
81
src/MetaTech/Util/Tool.php
Normal file
81
src/MetaTech/Util/Tool.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of the PwsAuth package.
|
||||
*
|
||||
* (c) meta-tech.academy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace MetaTech\Util;
|
||||
|
||||
/*!
|
||||
* @package MetaTech\Util
|
||||
* @class Tool
|
||||
* @static
|
||||
* @author a-Sansara
|
||||
* @date 2014-12-11 17:46:29 CET
|
||||
*/
|
||||
class Tool
|
||||
{
|
||||
/*! @var TIMESTAMP_SQLDATETIME default sqldatetime timestamp */
|
||||
const TIMESTAMP_SQLDATETIME = 'Y-m-d H:i:s';
|
||||
/*! @var TIMESTAMP_SQLDATE default sqldate timestamp */
|
||||
const TIMESTAMP_SQLDATE = 'Y-m-d';
|
||||
|
||||
/*!
|
||||
* @constructor
|
||||
* @protected
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* @method now
|
||||
* @public
|
||||
* @static
|
||||
* @param bool $full full format
|
||||
* @return str
|
||||
*/
|
||||
public static function now($full = true)
|
||||
{
|
||||
return date($full ? self::TIMESTAMP_SQLDATETIME : self::TIMESTAMP_SQLDATE);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @method formatDate
|
||||
* @public
|
||||
* @static
|
||||
* @param str $date
|
||||
* @param str $fromFormat
|
||||
* @param str $toFormat
|
||||
* @return str
|
||||
*/
|
||||
public static function formatDate($date, $fromFormat='d-m-Y', $toFormat='Y-m-d')
|
||||
{
|
||||
$dt = \DateTime::createFromFormat($fromFormat, $date);
|
||||
return !$dt ? null : $dt->format($toFormat);
|
||||
}
|
||||
|
||||
/*!
|
||||
* concatenate various items in $list separate with specifyed separator $sep
|
||||
*
|
||||
* @method concat
|
||||
* @public
|
||||
* @param str $sep the used separator to concatenate items in $list
|
||||
* @param [str] $list the list of items to concatenate
|
||||
* @return str
|
||||
*/
|
||||
public static function concat($sep, $list)
|
||||
{
|
||||
$value = array_shift($list);
|
||||
foreach ($list as $item) {
|
||||
$value .= $sep . $item;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user