Initial commit

This commit is contained in:
a-Sansara 2017-03-11 15:04:42 +01:00 committed by a-sansara
commit 037ee17766
5 changed files with 569 additions and 0 deletions

21
LICENSE Normal file
View 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
View 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

View 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;
}
}

View 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;
}
}

View 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;
}
}