PHP Classes

File: tests/src/services/UserCredentialSmsTokenLoginServiceTest.php

Recommend this page to a friend!
  Classes of Cyril Ogana   PHP User Credentials   tests/src/services/UserCredentialSmsTokenLoginServiceTest.php   Download  
File: tests/src/services/UserCredentialSmsTokenLoginServiceTest.php
Role: Unit test script
Content type: text/plain
Description: Add MultiOTP wrapper and SMS Token 2 factor authentication service
Class: PHP User Credentials
Implement password authentication policies
Author: By
Last change: Update to Multiotp 5.6+, PHPass 8+, PHP 7.3+ and PHPUnit 9+
Date: 4 years ago
Size: 18,218 bytes
 

Contents

Class file image Download
<?php namespace cymapgt\core\application\authentication\UserCredential\services; /** * Generated by PHPUnit_SkeletonGenerator on 2015-07-25 at 00:31:37. */ class UserCredentialSmsTokenLoginServiceTest extends \PHPUnit\Framework\TestCase { /** * @var UserCredentialSmsTotpService */ protected $object; /** * Sets up the fixture, for example, opens a network connection. * This method is called before a test is executed. */ protected function setUp() : void { $this->object = new UserCredentialSmsTokenLoginService; /** * This is the password that is stored in DB hashed with \password_hash function. * PHP 5.4 will be supported because of ircmaxell/password-compat package */ $this->password = \password_hash('123456', \PASSWORD_DEFAULT); } /** * Tears down the fixture, for example, closes a network connection. * This method is called after a test is executed. */ protected function tearDown() : void { } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::initialize */ public function testInitializeStage1Exception() { $this->expectException('\cymapgt\Exception\UserCredentialException'); $this->expectExceptionMessage('The multi factor stages register is initialized with an an unknown state'); /** * When you call setMultiFactor() to true, we require more info than just username, hashed password and password logged * * This additional parameters have to be set during Stage 1 are * - The Multi Factor stages array, which indicates the current stage and has an additional state info. The structure of the * array is : * * array ( * 'current' => $loginStage, * 1 => array ( * * ) * ); * * Where 'curent' is the login stage of the multifactor auth transaction and array with key 1 contains stage info. Note * that the state 1 indicates we are in stage 1. Each subsequent stage gets its index (see below) * * - The EncKey Length which is the length of the hash that will change with each login transaction. Default length is 16 * and it is generated using \openssl_pseudo_random_bytes. The client must return this hash together with login * info for subsequent login stages e.g during stage 2 otherwise authentication will not occur even with correct token * */ $this->object->setMultiFactor(true); $this->object->initialize(); } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::initialize */ public function testInitializeStage1() { $this->object->setMultiFactor(true); //Set multifactor stages array as explained above $this->object->setMultiFactorStages(array('current' => 1, 1 => array())); //set EncKey Length as explained above $this->object->setEncKeyLength(16); //Username and password as required by UserCredential service $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword(\password_hash('123456', \PASSWORD_DEFAULT)); //initialize for multi-factor should now work l now work $this->object->initialize(); //because we are in Stage 1 the statuss of multi-factor array should have been set false by the class because we are yet to authenticate $mFactorStages = $this->object->getMultiFactorStages(); $this->assertIsBool($mFactorStages[1]['statuss']); $this->assertEquals(false, $mFactorStages[1]['statuss']); } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::authenticate */ public function testAuthenticateStage1() { //initialize and authenticate password (stage 1) $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 1, 1 => array())); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $this->object->setPassword('1234567'); $this->object->initialize(); $authResult = $this->object->authenticate(); /** * Note that the auth result should be an array for stages prior to the final stage * * The structure of a successful authResult is * * $authResult = array ( * 1 => array ( * 'statuss' => true * ), * 2 => array ( * 'enc_key' => \openssl_random_pseudo_bytes($this->getEncKeyLength()), * 'statuss' => false * ) * ); * * The structure of an unsuccessful authResult is * * $authResult = array ( * 1 => array ( * 'statuss' => false * ) * ); * */ //as per above structure, assert the unsuccessful login $this->assertIsArray($authResult); $this->assertEquals(false, $authResult[1]['statuss']); $this->object->setPassword('123456'); $this->object->initialize(); $authResult2 = $this->object->authenticate(); //as per the above structure, assert successful login for Stage 1 $this->assertIsArray($authResult2); $this->assertEquals(true, $authResult2[1]['statuss']); $this->assertIsArray($authResult2[2]); $this->assertArrayHasKey('enc_key', $authResult2[2]); $this->assertArrayHasKey('statuss', $authResult2[2]); } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::initialize */ public function testInitializeStage2Exception() { $this->expectException('\cymapgt\Exception\UserCredentialException'); $this->expectExceptionMessage('The user TOTP profile is not initialized properly'); /** * When you call setMultiFactor() to true, we require more info than just username, hashed password and password logged * * This additional parameters have to be set during Stage 2 are * * - The TOTP Profile array, which contains some information about the generated token. The structure of the TOTP array is: * * array ( * 'enc_key' => $encKey, //enc key generated in Stage 1 * 'totp_timestamp" => $totpTimestamp, //timestamp when the login transaction opened * 'totp_timelimit' => $totpTimelimit //time limit for user to complete stage 2 (180 seconds by default) * ); * * - The Verification Hash. This is a hash computed with \crypt, and salted with the unique EncKey generated in stage 1 * * - One Time Token: This is the one time token input by the user in the Login Screen / API * * - Current One Time Token: This is the one time token generated using MultiOTP and which user is expected to input. * Note that this is Hashed using \password_hash */ $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 2, 1 => array())); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $this->object->setPassword('1234567'); //This should generate Exception as we have not initialized with TOTP profile, verification hash, one time token and Current One Time Token $this->object->initialize(); } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::initialize */ public function testInitializeStage2() { $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 1, 1 => array())); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $this->object->setPassword('123456'); $this->object->initialize(); $authResult = $this->object->authenticate(); $encKey = $authResult[2]['enc_key']; $verificationHash = \crypt($this->object->getPassword(), $authResult[2]['enc_key']); $nowObj = new \DateTime(); $nowObj->setTimestamp(($nowObj->getTimestamp() - 140)); $totpTimeLimit = 180; $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 2, 1 => array('statuss' => true))); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $totpProfile = array ( 'enc_key' => $encKey, 'totp_timestamp' => $nowObj, 'totp_timelimit' => $totpTimeLimit ); $this->object->setUserTotpProfile($totpProfile); $this->object->setVerificationHash($verificationHash); $oneTimeToken = \password_hash('123456', \PASSWORD_DEFAULT); $this->object->setOneTimeToken($oneTimeToken); $this->object->setCurrentOneTimeToken($oneTimeToken); //This should go through $this->object->initialize(); $this->assertEquals(1, true); } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::authenticate */ public function testAuthenticateStage2() { //Stage 1 Authentication $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 1, 1 => array())); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $this->object->setPassword('123456'); $this->object->initialize(); $authResult = $this->object->authenticate(); $encKey = $authResult[2]['enc_key']; $verificationHash = \crypt($this->object->getCurrentPassword(), $authResult[2]['enc_key']); $nowObj = new \DateTime(); $nowObj->setTimestamp(($nowObj->getTimestamp() - 140)); $totpTimeLimit = 180; //Stage 2 Authentication $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 2, 1 => array('statuss' => true))); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $totpProfile = array ( 'enc_key' => $encKey, 'totp_timestamp' => $nowObj, 'totp_timelimit' => $totpTimeLimit ); /** * For This stage of authentication, three things must be Validated * * 1) Token Input by user in Login Screen / API must match the Expected Token Generated * * 2) Token must have not been input later than totp_timelimit seconds * * 3) The VerificationHash recalculated by \crypt using the EncKey must match the one generated * in Stage 1 */ $this->object->setUserTotpProfile($totpProfile); $this->object->setVerificationHash($verificationHash); $oneTimeToken = \password_hash('827110', \PASSWORD_DEFAULT); $this->object->setCurrentOneTimeToken($oneTimeToken); $this->object->setOneTimeToken('827110'); $this->object->initialize(); $this->assertEquals(true, $this->object->authenticate()); } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::authenticate */ public function testAuthenticateStageTimelimit() { //Correct detials, but Token was input 181 seconds later. Should fail $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 1, 1 => array())); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $this->object->setPassword('123456'); $this->object->initialize(); $authResult = $this->object->authenticate(); $encKey = $authResult[2]['enc_key']; $verificationHash = \crypt($this->object->getCurrentPassword(), $authResult[2]['enc_key']); //simulate delaying submission by 1 second $nowObj = new \DateTime(); $nowObj->setTimestamp(($nowObj->getTimestamp() - 181)); $totpTimeLimit = 180; $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 2, 1 => array('statuss' => true))); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $totpProfile = array ( 'enc_key' => $encKey, 'totp_timestamp' => $nowObj, 'totp_timelimit' => $totpTimeLimit ); $this->object->setUserTotpProfile($totpProfile); $this->object->setVerificationHash($verificationHash); $oneTimeToken = \password_hash('827110', \PASSWORD_DEFAULT); $this->object->setCurrentOneTimeToken($oneTimeToken); $this->object->setOneTimeToken('827110'); $this->object->initialize(); $this->assertEquals(false, $this->object->authenticate()); } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::authenticate */ public function testAuthenticateStageTokenWrong() { //Wrong Token has been keyed in. Should fail $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 1, 1 => array())); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $this->object->setPassword('123456'); $this->object->initialize(); $authResult = $this->object->authenticate(); $encKey = $authResult[2]['enc_key']; $verificationHash = \crypt($this->object->getCurrentPassword(), $authResult[2]['enc_key']); $nowObj = new \DateTime(); $nowObj->setTimestamp(($nowObj->getTimestamp() - 140)); $totpTimeLimit = 180; $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 2, 1 => array('statuss' => true))); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $totpProfile = array ( 'enc_key' => $encKey, 'totp_timestamp' => $nowObj, 'totp_timelimit' => $totpTimeLimit ); $this->object->setUserTotpProfile($totpProfile); $this->object->setVerificationHash($verificationHash); $oneTimeToken = \password_hash('827110', \PASSWORD_DEFAULT); $this->object->setCurrentOneTimeToken($oneTimeToken); $this->object->setOneTimeToken('827118'); $this->object->initialize(); $this->assertEquals(false, $this->object->authenticate()); } /** * @covers cymapgt\core\application\authentication\UserCredential\services\UserCredentialSmsTotpService::authenticate */ public function testAuthenticateStageEncKeyWrong() { //Wrong EncKey provided, thus VerificationHash check fails. TODO: Does it mitigate Replay? $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 1, 1 => array())); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $this->object->setPassword('123456'); $this->object->initialize(); $authResult = $this->object->authenticate(); $verificationHash = \crypt($this->object->getCurrentPassword(), $authResult[2]['enc_key']); $nowObj = new \DateTime(); $nowObj->setTimestamp(($nowObj->getTimestamp() - 140)); $totpTimeLimit = 180; $this->object->setMultiFactor(true); $this->object->setMultiFactorStages(array('current' => 2, 1 => array('statuss' => true))); $this->object->setEncKeyLength(16); $this->object->setCurrentUserName('rhossis'); $this->object->setCurrentPassword($this->password); $totpProfile = array ( 'enc_key' => 'hElLoThErEiAmAwRoNgEnCkEy', 'totp_timestamp' => $nowObj, 'totp_timelimit' => $totpTimeLimit ); $this->object->setUserTotpProfile($totpProfile); $this->object->setVerificationHash($verificationHash); $oneTimeToken = \password_hash('827110', \PASSWORD_DEFAULT); $this->object->setCurrentOneTimeToken($oneTimeToken); $this->object->setOneTimeToken('827110'); $this->object->initialize(); $this->assertEquals(false, $this->object->authenticate()); } }