Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
janicerar committed Oct 16, 2018
0 parents commit 12e74ef
Show file tree
Hide file tree
Showing 14 changed files with 519 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/.idea
/vendor
/node_modules
package-lock.json
composer.phar
composer.lock
phpunit.xml
.phpunit.result.cache
.DS_Store
Thumbs.db
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TODO
31 changes: 31 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "lifeonscreen/nova-google2fa",
"description": "This package provides Google2FA to Laravel Nova.",
"keywords": [
"laravel",
"nova"
],
"license": "MIT",
"require": {
"php": ">=7.1.0",
"bacon/bacon-qr-code": "^2.0",
"pragmarx/google2fa-laravel": "^0.2.0"
},
"autoload": {
"psr-4": {
"Lifeonscreen\\Google2fa\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Lifeonscreen\\Google2fa\\ToolServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}
10 changes: 10 additions & 0 deletions config/lifeonscreen2fa.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

return [
'models' => [
'user' => 'App\User',
],
'tables' => [
'user' => 'users',
],
];
41 changes: 41 additions & 0 deletions resources/database/2018_10_15_095425_create_user_2fa_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUser2faTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_2fa', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id');
$table->boolean('google2fa_enable')->default(false);
$table->string('google2fa_secret')->nullable();
$table->timestamp('created_at')
->default(DB::raw('CURRENT_TIMESTAMP'));
$table->timestamp('updated_at')
->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));

$table->foreign('user_id')
->references('id')
->on(config('lifeonscreen2fa.tables.user'));
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_2fa');
}
}
63 changes: 63 additions & 0 deletions resources/views/authenticate.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en" class="h-full font-sans">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="csrf-token" content="{{ csrf_token() }}">

<title>{{ Nova::name() }}</title>

<!-- Styles -->
<link rel="stylesheet" href="{{ mix('app.css', 'vendor/nova') }}">

<style>
body {
font-family: "Montserrat", sans-serif !important;
}
.btn,
.form-input,
.rounded-lg {
border-radius: 0 !important;
}
</style>
</head>
<body class="bg-40 text-black h-full">
<div class="h-full">
<div class="px-view py-view mx-auto">
<div class="mx-auto py-8 max-w-sm text-center text-90">
@include('nova::partials.logo')
</div>

<form class="bg-white shadow rounded-lg p-8 max-w-xl mx-auto" method="POST" action="/los/2fa/authenticate">
<h2 class="p-2">Two Factor Authentication</h2>

<p class="p-2">Two factor authentication (2FA) strengthens access security by requiring two methods (also
referred to as factors) to
verify your identity.
Two factor authentication protects against phishing, social engineering and password brute force attacks
and secures your logins from attackers
exploiting weak or stolen credentials.</p>
<p class="p-2"><strong>Enter the pin from Google Authenticator Enable 2FA</strong></p>

<div class="text-center pt-3">
<div class="mb-6 w-1/2" style="display:inline-block">
@if (isset($error))
<p class="text-center font-semibold text-danger my-3">
{{ $error }}
</p>
@endif
<label class="block font-bold mb-2" for="co">One Time Password</label>
<input class="form-control form-input form-input-bordered w-full" id="secret" type="number"
name="secret" value="" required="required" autofocus="">

</div>
<button class="w-1/2 btn btn-default btn-primary hover:bg-primary-dark" type="submit">
Authenticate
</button>
</div>
</form>
</div>
</div>
</body>
</html>
71 changes: 71 additions & 0 deletions resources/views/register.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en" class="h-full font-sans">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="csrf-token" content="{{ csrf_token() }}">

<title>{{ Nova::name() }}</title>

<!-- Styles -->
<link rel="stylesheet" href="{{ mix('app.css', 'vendor/nova') }}">

<style>
body {
font-family: "Montserrat", sans-serif !important;
}
.btn,
.form-input,
.rounded-lg {
border-radius: 0 !important;
}
</style>
</head>
<body class="bg-40 text-black h-full">
<div class="h-full">
<div class="px-view py-view mx-auto">
<div class="mx-auto py-8 max-w-sm text-center text-90">
@include('nova::partials.logo')
</div>

<form class="bg-white shadow rounded-lg p-8 max-w-xl mx-auto" method="POST" action="/los/2fa/confirm">
<h2 class="p-2">Two Factor Authentication</h2>

<p class="p-2">Two factor authentication (2FA) strengthens access security by requiring two methods (also
referred
to as factors) to verify your identity. Two factor authentication protects against phishing, social
engineering and password brute force attacks and secures your logins from attackers exploiting weak
or stolen credentials.</p>
<p class="p-2">To Enable Two Factor Authentication on your Account, you need to do following steps</p>
<strong>
<ol>
<li>Verify the OTP from Google Authenticator Mobile App</li>
</ol>
</strong>
<div class="text-center">
<img src="{{ $google2fa_url }}" alt="">
</div>

<div class="text-center">
<div class="mb-6 w-1/2" style="display:inline-block">
@if (isset($error))
<p class="text-center font-semibold text-danger my-3">
{{ $error }}
</p>
@endif
<label class="block font-bold mb-2" for="co">Secret</label>
<input class="form-control form-input form-input-bordered w-full" id="secret" type="number"
name="secret" value="" required="required" autofocus="">
</div>


<button class="w-1/2 btn btn-default btn-primary hover:bg-primary-dark" type="submit">
Confirm
</button>
</div>
</form>
</div>
</div>
</body>
</html>
13 changes: 13 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

use Illuminate\Support\Facades\Route;

/**
* This route is called when user must first time confirm secret
*/
Route::post('confirm', 'Lifeonscreen\Google2fa\Google2fa@confirm');

/**
* This route is called to verify users secret
*/
Route::post('authenticate', 'Lifeonscreen\Google2fa\Google2fa@authenticate');
35 changes: 35 additions & 0 deletions src/Google2FAAuthenticator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Lifeonscreen\Google2fa;

use App\Exceptions\ValidationException;
use PragmaRX\Google2FALaravel\Support\Authenticator;

/**
* Class Google2FAAuthenticator
* @package Lifeonscreen\Google2fa
*/
class Google2FAAuthenticator extends Authenticator
{
protected function canPassWithoutCheckingOTP()
{
return
!$this->isEnabled() ||
$this->noUserIsAuthenticated() ||
$this->twoFactorAuthStillValid();
}

/**
* @return mixed
* @throws ValidationException
*/
protected function getGoogle2FASecretKey()
{
$secret = $this->getUser()->user2fa->{$this->config('otp_secret_column')};
if (is_null($secret) || empty($secret)) {
throw new ValidationException('Secret key cannot be empty.');
}

return $secret;
}
}
80 changes: 80 additions & 0 deletions src/Google2fa.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace Lifeonscreen\Google2fa;

use Laravel\Nova\Tool;
use PragmaRX\Google2FA\Google2FA as G2fa;
use Request;

class Google2fa extends Tool
{
/**
* Perform any tasks that need to happen when the tool is booted.
*
* @return void
*/
public function boot()
{
}

/**
* Build the view that renders the navigation links for the tool.
*
* @return \Illuminate\View\View
*/
public function renderNavigation()
{
return view('google2fa::navigation');
}

protected function is2FAValid()
{
$secret = Request::get('secret');

$google2fa = new G2fa();
$google2fa->setAllowInsecureCallToGoogleApis(true);

return $google2fa->verifyKey(auth()->user()->user2fa->google2fa_secret, $secret);
}

public function confirm()
{
if ($this->is2FAValid()) {
auth()->user()->user2fa->google2fa_enable = 1;
auth()->user()->user2fa->save();
$authenticator = app(Google2FAAuthenticator::class);
$authenticator->login();

return response()->redirectTo('/nova');
}

$google2fa = new G2fa();
$secretKey = $google2fa->generateSecretKey();
$google2fa->setAllowInsecureCallToGoogleApis(true);

$google2fa_url = $google2fa->getQRCodeGoogleUrl(
config('app.name'),
auth()->user()->email,
$secretKey
);

$data['google2fa_url'] = $google2fa_url;
$data['error'] = 'Secret is invalid.';

return view('google2fa::register', $data);

}

public function authenticate()
{
if ($this->is2FAValid()) {
$authenticator = app(Google2FAAuthenticator::class);
$authenticator->login();

return response()->redirectTo('/nova');
}
$data['error'] = 'One time password is invalid.';

return view('google2fa::authenticate', $data);
}
}
20 changes: 20 additions & 0 deletions src/Http/Middleware/Authorize.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Lifeonscreen\Google2fa\Http\Middleware;

use Lifeonscreen\Google2fa\Google2fa;

class Authorize
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response
*/
public function handle($request, $next)
{
return resolve(Google2fa::class)->authorize($request) ? $next($request) : abort(403);
}
}
Loading

0 comments on commit 12e74ef

Please sign in to comment.