瀏覽代碼

TOTP integration

Bubka 6 年之前
父節點
當前提交
6a76a493a2

+ 26 - 0
app/Http/Controllers/TwoFAccountController.php

@@ -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 @@ class TwoFAccountController extends Controller
     }
     }
 
 
 
 
+    /**
+     * 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.
      *
      *

+ 2 - 1
composer.json

@@ -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",

+ 321 - 1
composer.lock

@@ -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",

+ 1 - 1
database/seeds/TwoFAccountsTableSeeder.php

@@ -17,7 +17,7 @@ class TwoFAccountsTableSeeder extends Seeder
 
 
         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([

+ 1 - 0
routes/api.php

@@ -22,5 +22,6 @@ Route::middleware('auth:api')->get('/user', function (Request $request) {
 
 
 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');
 });
 });

+ 23 - 2
tests/Unit/TwoFAccountTest.php

@@ -36,8 +36,6 @@ class TwoFAccountTest extends TestCase
      */
      */
     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 @@ class TwoFAccountTest extends TestCase
     }
     }
 
 
 
 
+    /**
+     * 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
      *
      *