TOTP integration

This commit is contained in:
Bubka 2019-05-26 16:42:09 +02:00
parent 482c01ca2c
commit 6a76a493a2
6 changed files with 374 additions and 5 deletions

View File

@ -3,7 +3,10 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\TwoFAccount; use App\TwoFAccount;
use OTPHP\TOTP;
use OTPHP\Factory;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use ParagonIE\ConstantTime\Base32;
class TwoFAccountController extends Controller class TwoFAccountController extends Controller
{ {
@ -47,6 +50,29 @@ public function show(TwoFAccount $twofaccount)
} }
/**
* Generate a TOTP
*
* @param \App\TwoFAccount $twofaccount
* @return \Illuminate\Http\Response
*/
public function generateTOTP(TwoFAccount $twofaccount)
{
try {
$otp = Factory::loadFromProvisioningUri($twofaccount->secret);
} catch (InvalidArgumentException $exception) {
return response()->json([
'message' => 'Error generating TOTP',
], 500);
}
return response()->json([
'totp' => $otp->now(),
], 200);
}
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *

View File

@ -12,7 +12,8 @@
"fideloper/proxy": "^4.0", "fideloper/proxy": "^4.0",
"laravel/framework": "5.8.*", "laravel/framework": "5.8.*",
"laravel/passport": "^7.2", "laravel/passport": "^7.2",
"laravel/tinker": "^1.0" "laravel/tinker": "^1.0",
"spomky-labs/otphp": "^10.0"
}, },
"require-dev": { "require-dev": {
"beyondcode/laravel-dump-server": "^1.0", "beyondcode/laravel-dump-server": "^1.0",

322
composer.lock generated
View File

@ -4,8 +4,63 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "dea87dd5fa06feeb9038c216bb7cca08", "content-hash": "e2586083343555a07abbaad764ae5305",
"packages": [ "packages": [
{
"name": "beberlei/assert",
"version": "v3.2.0",
"source": {
"type": "git",
"url": "https://github.com/beberlei/assert.git",
"reference": "fd82f4c8592c8128dd74481034c31da71ebafc56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/beberlei/assert/zipball/fd82f4c8592c8128dd74481034c31da71ebafc56",
"reference": "fd82f4c8592c8128dd74481034c31da71ebafc56",
"shasum": ""
},
"require": {
"php": "^7"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "*",
"phpstan/phpstan-shim": "*",
"phpunit/phpunit": "*"
},
"type": "library",
"autoload": {
"psr-4": {
"Assert\\": "lib/Assert"
},
"files": [
"lib/Assert/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de",
"role": "Lead Developer"
},
{
"name": "Richard Quadling",
"email": "rquadling@gmail.com",
"role": "Collaborator"
}
],
"description": "Thin assertion library for input validation in business models.",
"keywords": [
"assert",
"assertion",
"validation"
],
"time": "2018-12-24T15:25:25+00:00"
},
{ {
"name": "defuse/php-encryption", "name": "defuse/php-encryption",
"version": "v2.2.1", "version": "v2.2.1",
@ -1550,6 +1605,68 @@
], ],
"time": "2019-05-05T12:50:25+00:00" "time": "2019-05-05T12:50:25+00:00"
}, },
{
"name": "paragonie/constant_time_encoding",
"version": "v2.2.3",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "55af0dc01992b4d0da7f6372e2eac097bbbaffdb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/55af0dc01992b4d0da7f6372e2eac097bbbaffdb",
"reference": "55af0dc01992b4d0da7f6372e2eac097bbbaffdb",
"shasum": ""
},
"require": {
"php": "^7"
},
"require-dev": {
"phpunit/phpunit": "^6|^7",
"vimeo/psalm": "^1|^2"
},
"type": "library",
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base16",
"base32",
"base32_decode",
"base32_encode",
"base64",
"base64_decode",
"base64_encode",
"bin2hex",
"encoding",
"hex",
"hex2bin",
"rfc4648"
],
"time": "2019-01-03T20:26:31+00:00"
},
{ {
"name": "paragonie/random_compat", "name": "paragonie/random_compat",
"version": "v9.99.99", "version": "v9.99.99",
@ -2179,6 +2296,77 @@
], ],
"time": "2018-07-19T23:38:55+00:00" "time": "2018-07-19T23:38:55+00:00"
}, },
{
"name": "spomky-labs/otphp",
"version": "v10.0",
"source": {
"type": "git",
"url": "https://github.com/Spomky-Labs/otphp.git",
"reference": "3ff28fc30efdedacf1bc99e66232225a9354dbba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/3ff28fc30efdedacf1bc99e66232225a9354dbba",
"reference": "3ff28fc30efdedacf1bc99e66232225a9354dbba",
"shasum": ""
},
"require": {
"beberlei/assert": "^3.0",
"ext-mbstring": "*",
"paragonie/constant_time_encoding": "^2.0",
"php": "^7.2|^8.0",
"thecodingmachine/safe": "^0.1.14"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.0",
"phpstan/phpstan": "^0.11",
"phpstan/phpstan-beberlei-assert": "^0.11.0",
"phpstan/phpstan-deprecation-rules": "^0.11",
"phpstan/phpstan-phpunit": "^0.11",
"phpstan/phpstan-strict-rules": "^0.11",
"phpunit/phpunit": "^8.0",
"thecodingmachine/phpstan-safe-rule": "^0.1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"v10.0": "10.0.x-dev",
"v9.0": "9.0.x-dev",
"v8.3": "8.3.x-dev"
}
},
"autoload": {
"psr-4": {
"OTPHP\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Florent Morselli",
"homepage": "https://github.com/Spomky"
},
{
"name": "All contributors",
"homepage": "https://github.com/Spomky-Labs/otphp/contributors"
}
],
"description": "A PHP library for generating one time passwords according to RFC 4226 (HOTP Algorithm) and the RFC 6238 (TOTP Algorithm) and compatible with Google Authenticator",
"homepage": "https://github.com/Spomky-Labs/otphp",
"keywords": [
"FreeOTP",
"RFC 4226",
"RFC 6238",
"google authenticator",
"hotp",
"otp",
"totp"
],
"time": "2019-05-07T21:08:23+00:00"
},
{ {
"name": "swiftmailer/swiftmailer", "name": "swiftmailer/swiftmailer",
"version": "v6.2.1", "version": "v6.2.1",
@ -3383,6 +3571,138 @@
], ],
"time": "2019-05-01T12:55:36+00:00" "time": "2019-05-01T12:55:36+00:00"
}, },
{
"name": "thecodingmachine/safe",
"version": "v0.1.15",
"source": {
"type": "git",
"url": "https://github.com/thecodingmachine/safe.git",
"reference": "9a4dbc54e397e0bfb152f4b38f8a03040a1e9e3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thecodingmachine/safe/zipball/9a4dbc54e397e0bfb152f4b38f8a03040a1e9e3e",
"reference": "9a4dbc54e397e0bfb152f4b38f8a03040a1e9e3e",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpstan/phpstan": "^0.10.3",
"squizlabs/php_codesniffer": "^3.2",
"thecodingmachine/phpstan-strict-rules": "^0.10.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.1-dev"
}
},
"autoload": {
"psr-4": {
"Safe\\": [
"lib/",
"generated/"
]
},
"files": [
"generated/apache.php",
"generated/apc.php",
"generated/apcu.php",
"generated/array.php",
"generated/bzip2.php",
"generated/classobj.php",
"generated/com.php",
"generated/cubrid.php",
"generated/curl.php",
"generated/datetime.php",
"generated/dir.php",
"generated/eio.php",
"generated/errorfunc.php",
"generated/exec.php",
"generated/fileinfo.php",
"generated/filesystem.php",
"generated/filter.php",
"generated/fpm.php",
"generated/ftp.php",
"generated/funchand.php",
"generated/gmp.php",
"generated/gnupg.php",
"generated/hash.php",
"generated/ibase.php",
"generated/ibmDb2.php",
"generated/iconv.php",
"generated/image.php",
"generated/imap.php",
"generated/info.php",
"generated/ingres-ii.php",
"generated/inotify.php",
"generated/json.php",
"generated/ldap.php",
"generated/libevent.php",
"generated/libxml.php",
"generated/lzf.php",
"generated/mailparse.php",
"generated/mbstring.php",
"generated/misc.php",
"generated/msql.php",
"generated/mssql.php",
"generated/mysql.php",
"generated/mysqli.php",
"generated/mysqlndMs.php",
"generated/mysqlndQc.php",
"generated/network.php",
"generated/oci8.php",
"generated/opcache.php",
"generated/openssl.php",
"generated/outcontrol.php",
"generated/password.php",
"generated/pcntl.php",
"generated/pcre.php",
"generated/pdf.php",
"generated/pgsql.php",
"generated/posix.php",
"generated/ps.php",
"generated/pspell.php",
"generated/readline.php",
"generated/rrd.php",
"generated/sem.php",
"generated/session.php",
"generated/shmop.php",
"generated/simplexml.php",
"generated/sockets.php",
"generated/sodium.php",
"generated/solr.php",
"generated/spl.php",
"generated/sqlsrv.php",
"generated/ssdeep.php",
"generated/ssh2.php",
"generated/stats.php",
"generated/stream.php",
"generated/strings.php",
"generated/swoole.php",
"generated/uodbc.php",
"generated/uopz.php",
"generated/url.php",
"generated/var.php",
"generated/xdiff.php",
"generated/xml.php",
"generated/xmlrpc.php",
"generated/yaml.php",
"generated/yaz.php",
"generated/zip.php",
"generated/zlib.php",
"lib/special_cases.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHP core functions that throw exceptions instead of returning FALSE on error",
"time": "2019-04-17T16:09:50+00:00"
},
{ {
"name": "tijsverkoyen/css-to-inline-styles", "name": "tijsverkoyen/css-to-inline-styles",
"version": "2.2.1", "version": "2.2.1",

View File

@ -17,7 +17,7 @@ public function run()
TwoFAccount::create([ TwoFAccount::create([
'name' => $faker->unique()->domainName, 'name' => $faker->unique()->domainName,
'secret' => $faker->password, 'secret' => 'otpauth://totp/test@test.com?secret=A4GRFHVVRBGY7UIW&issuer=test',
]); ]);
$deletedResource = TwoFAccount::create([ $deletedResource = TwoFAccount::create([

View File

@ -22,5 +22,6 @@
Route::group(['middleware' => 'auth:api'], function(){ Route::group(['middleware' => 'auth:api'], function(){
Route::apiResource('twofaccounts', 'TwoFAccountController'); Route::apiResource('twofaccounts', 'TwoFAccountController');
Route::get('twofaccounts/{twofaccount}/totp', 'TwoFAccountController@generateTOTP')->name('twofaccounts.generateTOTP');
Route::delete('twofaccounts/force/{id}', 'TwoFAccountController@forceDestroy')->name('twofaccounts.forceDestroy'); Route::delete('twofaccounts/force/{id}', 'TwoFAccountController@forceDestroy')->name('twofaccounts.forceDestroy');
}); });

View File

@ -36,8 +36,6 @@ public function setUp(): void
*/ */
public function testTwoFAccountCreation() public function testTwoFAccountCreation()
{ {
//$this->withoutMiddleware();
$user = \App\User::find(1); $user = \App\User::find(1);
$response = $this->actingAs($user, 'api') $response = $this->actingAs($user, 'api')
@ -53,6 +51,29 @@ public function testTwoFAccountCreation()
} }
/**
* test TOTP generation via API
*
* @return void
*/
public function testTOTPgeneration()
{
$user = \App\User::find(1);
$twofaccount = TwoFAccount::create([
'name' => 'testTOTP',
'secret' => 'otpauth://totp/test@test.com?secret=A4GRFHVVRBGY7UIW&issuer=test'
]);
$response = $this->actingAs($user, 'api')
->json('POST', '/api/twofaccounts/' . $twofaccount->id . '/totp')
->assertStatus(200)
->assertJsonStructure([
'totp',
]);
}
/** /**
* test TwoFAccount update via API * test TwoFAccount update via API
* *