diff --git a/Web-Application/Manually/app/Http/Controllers/Auth/LogoutController.php b/Web-Application/Manually/app/Http/Controllers/Auth/LogoutController.php index f7b12d0..b90d6ac 100644 --- a/Web-Application/Manually/app/Http/Controllers/Auth/LogoutController.php +++ b/Web-Application/Manually/app/Http/Controllers/Auth/LogoutController.php @@ -9,8 +9,18 @@ use Illuminate\Support\Facades\Auth; class LogoutController extends Controller { + public $user; + + public function __construct() + { + $this->user = Auth::user(); + } public function logout(Request $request) { + $this->user->update([ + 'verify2fa' => 0, + ]); + Auth::logout(); $request->session()->invalidate(); @@ -21,13 +31,12 @@ class LogoutController extends Controller public function deleteAccount(Request $request) { - $user = Auth::user(); Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); - $user->delete(); + $this->user->delete(); return redirect()->route('login')->with('success', 'Your account has been deleted successfully.'); } diff --git a/Web-Application/Manually/app/Http/Controllers/Auth/TwoFactorAuthenticationController.php b/Web-Application/Manually/app/Http/Controllers/Auth/TwoFactorAuthenticationController.php new file mode 100644 index 0000000..133de81 --- /dev/null +++ b/Web-Application/Manually/app/Http/Controllers/Auth/TwoFactorAuthenticationController.php @@ -0,0 +1,77 @@ +google2fa = app('pragmarx.google2fa'); + $this->user = Auth::user(); + } + + public function enable2FAShow() + { + $secret = $this->google2fa->generateSecretKey(); + + $urlQRCode = $this->google2fa->getQRCodeInline( + config('app.name'), + $this->user->email, + $secret + ); + + return view("auth.enable-2fa", compact(['secret', 'urlQRCode'])); + } + + public function enable2FA(Enable2FARequest $request) + { + $inputs = $request->only('otp', 'secret'); + $valid = $this->google2fa->verifyKey($inputs['secret'], $inputs['otp']); + + if ($valid) { + $this->user->update([ + 'google2fa_secret' => $inputs['secret'], + ]); + } + + return redirect()->route('dashboard')->with('success', '2fa is verified'); + } + + public function disable2FA() + { + $this->user->update([ + 'google2fa_secret' => null, + 'verify2fa' => 0, + ]); + return redirect()->route('dashboard'); + } + + public function secretCodeShow() + { + return view('auth.secret-code'); + } + + public function secretCode(SecretCode2FARequest $request) + { + $secret = $this->user->google2fa_secret; + $otp = $request->only('code'); + $valid = $this->google2fa->verifyKey($secret, $otp['code']); + + if ($valid) { + $this->user->update([ + 'verify2fa' => 1, + ]); + return redirect()->route('dashboard')->with('google2fa', '2fa is verify'); + } + return redirect()->back()->withErrors(['code' => 'secret code is wrong']); + } +} diff --git a/Web-Application/Manually/app/Http/Middleware/TwoFactorAuthMiddleware.php b/Web-Application/Manually/app/Http/Middleware/TwoFactorAuthMiddleware.php new file mode 100644 index 0000000..779bba3 --- /dev/null +++ b/Web-Application/Manually/app/Http/Middleware/TwoFactorAuthMiddleware.php @@ -0,0 +1,26 @@ +google2fa_secret) { + if (!Auth::user()->verify2fa) { + return redirect()->route('secret.code.show'); + } + } + return $next($request); + } +} diff --git a/Web-Application/Manually/app/Http/Requests/Enable2FARequest.php b/Web-Application/Manually/app/Http/Requests/Enable2FARequest.php new file mode 100644 index 0000000..1610952 --- /dev/null +++ b/Web-Application/Manually/app/Http/Requests/Enable2FARequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'otp' => 'required', + 'secret' => 'required', + ]; + } +} diff --git a/Web-Application/Manually/app/Http/Requests/SecretCode2FARequest.php b/Web-Application/Manually/app/Http/Requests/SecretCode2FARequest.php new file mode 100644 index 0000000..867ac24 --- /dev/null +++ b/Web-Application/Manually/app/Http/Requests/SecretCode2FARequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + 'code' => 'required' + ]; + } +} diff --git a/Web-Application/Manually/app/Models/User.php b/Web-Application/Manually/app/Models/User.php index c24f280..3ccce93 100644 --- a/Web-Application/Manually/app/Models/User.php +++ b/Web-Application/Manually/app/Models/User.php @@ -23,7 +23,9 @@ class User extends Authenticatable implements MustVerifyEmail 'password', 'github_id', 'github_token', - 'github_refresh_token' + 'github_refresh_token', + 'google2fa_secret', + 'verify2fa' ]; /** diff --git a/Web-Application/Manually/bootstrap/app.php b/Web-Application/Manually/bootstrap/app.php index 7b162da..3e077e8 100644 --- a/Web-Application/Manually/bootstrap/app.php +++ b/Web-Application/Manually/bootstrap/app.php @@ -1,17 +1,20 @@ withRouting( - web: __DIR__.'/../routes/web.php', - commands: __DIR__.'/../routes/console.php', + web: __DIR__ . '/../routes/web.php', + commands: __DIR__ . '/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { - // + $middleware->alias([ + '2fa' => TwoFactorAuthMiddleware::class, + ]); }) ->withExceptions(function (Exceptions $exceptions) { // diff --git a/Web-Application/Manually/composer.json b/Web-Application/Manually/composer.json index b488129..bf8ed06 100644 --- a/Web-Application/Manually/composer.json +++ b/Web-Application/Manually/composer.json @@ -10,10 +10,12 @@ "license": "MIT", "require": { "php": "^8.2", + "bacon/bacon-qr-code": "^3.0", "laravel/framework": "^12.0", "laravel/socialite": "^5.20", "laravel/tinker": "^2.10.1", - "mailersend/laravel-driver": "^2.9" + "mailersend/laravel-driver": "^2.9", + "pragmarx/google2fa-laravel": "^2.3" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/Web-Application/Manually/composer.lock b/Web-Application/Manually/composer.lock index 56db7be..ca830f9 100644 --- a/Web-Application/Manually/composer.lock +++ b/Web-Application/Manually/composer.lock @@ -4,8 +4,62 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cc95798219be94103b39757ba957bfb5", + "content-hash": "7ec9c18ae33503f4d06a9ce3cef2e3cf", "packages": [ + { + "name": "bacon/bacon-qr-code", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/Bacon/BaconQrCode.git", + "reference": "f9cc1f52b5a463062251d666761178dbdb6b544f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/f9cc1f52b5a463062251d666761178dbdb6b544f", + "reference": "f9cc1f52b5a463062251d666761178dbdb6b544f", + "shasum": "" + }, + "require": { + "dasprid/enum": "^1.0.3", + "ext-iconv": "*", + "php": "^8.1" + }, + "require-dev": { + "phly/keep-a-changelog": "^2.12", + "phpunit/phpunit": "^10.5.11 || 11.0.4", + "spatie/phpunit-snapshot-assertions": "^5.1.5", + "squizlabs/php_codesniffer": "^3.9" + }, + "suggest": { + "ext-imagick": "to generate QR code images" + }, + "type": "library", + "autoload": { + "psr-4": { + "BaconQrCode\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "BaconQrCode is a QR code generator for PHP.", + "homepage": "https://github.com/Bacon/BaconQrCode", + "support": { + "issues": "https://github.com/Bacon/BaconQrCode/issues", + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.1" + }, + "time": "2024-10-01T13:55:55+00:00" + }, { "name": "beberlei/assert", "version": "v3.3.3", @@ -268,6 +322,56 @@ ], "time": "2023-12-20T15:40:13+00:00" }, + { + "name": "dasprid/enum", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "shasum": "" + }, + "require": { + "php": ">=7.1 <9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "support": { + "issues": "https://github.com/DASPRiD/Enum/issues", + "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" + }, + "time": "2024-08-09T14:30:48+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.3", @@ -3753,6 +3857,201 @@ ], "time": "2024-12-14T21:12:59+00:00" }, + { + "name": "pragmarx/google2fa", + "version": "v8.0.3", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa.git", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.9", + "phpunit/phpunit": "^7.5.15|^8.5|^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PragmaRX\\Google2FA\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa/issues", + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" + }, + "time": "2024-09-05T11:56:40+00:00" + }, + { + "name": "pragmarx/google2fa-laravel", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa-laravel.git", + "reference": "60f363c16db1e94263e0560efebe9fc2e302b7ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-laravel/zipball/60f363c16db1e94263e0560efebe9fc2e302b7ef", + "reference": "60f363c16db1e94263e0560efebe9fc2e302b7ef", + "shasum": "" + }, + "require": { + "laravel/framework": "^5.4.36|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "php": ">=7.0", + "pragmarx/google2fa-qrcode": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "bacon/bacon-qr-code": "^2.0", + "orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*|7.*|8.*|9.*|10.*", + "phpunit/phpunit": "~5|~6|~7|~8|~9|~10|~11" + }, + "suggest": { + "bacon/bacon-qr-code": "Required to generate inline QR Codes.", + "pragmarx/recovery": "Generate recovery codes." + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Google2FA": "PragmaRX\\Google2FALaravel\\Facade" + }, + "providers": [ + "PragmaRX\\Google2FALaravel\\ServiceProvider" + ] + }, + "component": "package", + "frameworks": [ + "Laravel" + ], + "branch-alias": { + "dev-master": "0.2-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FALaravel\\": "src/", + "PragmaRX\\Google2FALaravel\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "A One Time Password Authentication package, compatible with Google Authenticator.", + "keywords": [ + "Authentication", + "Two Factor Authentication", + "google2fa", + "laravel" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa-laravel/issues", + "source": "https://github.com/antonioribeiro/google2fa-laravel/tree/v2.3.0" + }, + "time": "2025-02-26T19:39:35+00:00" + }, + { + "name": "pragmarx/google2fa-qrcode", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/antonioribeiro/google2fa-qrcode.git", + "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/antonioribeiro/google2fa-qrcode/zipball/ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "reference": "ce4d8a729b6c93741c607cfb2217acfffb5bf76b", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "pragmarx/google2fa": ">=4.0" + }, + "require-dev": { + "bacon/bacon-qr-code": "^2.0", + "chillerlan/php-qrcode": "^1.0|^2.0|^3.0|^4.0", + "khanamiryan/qrcode-detector-decoder": "^1.0", + "phpunit/phpunit": "~4|~5|~6|~7|~8|~9" + }, + "suggest": { + "bacon/bacon-qr-code": "For QR Code generation, requires imagick", + "chillerlan/php-qrcode": "For QR Code generation" + }, + "type": "library", + "extra": { + "component": "package", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "PragmaRX\\Google2FAQRCode\\": "src/", + "PragmaRX\\Google2FAQRCode\\Tests\\": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Antonio Carlos Ribeiro", + "email": "acr@antoniocarlosribeiro.com", + "role": "Creator & Designer" + } + ], + "description": "QR Code package for Google2FA", + "keywords": [ + "2fa", + "Authentication", + "Two Factor Authentication", + "google2fa", + "qr code", + "qrcode" + ], + "support": { + "issues": "https://github.com/antonioribeiro/google2fa-qrcode/issues", + "source": "https://github.com/antonioribeiro/google2fa-qrcode/tree/v3.0.0" + }, + "time": "2021-08-15T12:53:48+00:00" + }, { "name": "psr/clock", "version": "1.0.0", diff --git a/Web-Application/Manually/database/migrations/2025_04_23_095813_add_google2fa_to_users_table.php b/Web-Application/Manually/database/migrations/2025_04_23_095813_add_google2fa_to_users_table.php new file mode 100644 index 0000000..10dfd70 --- /dev/null +++ b/Web-Application/Manually/database/migrations/2025_04_23_095813_add_google2fa_to_users_table.php @@ -0,0 +1,28 @@ +text('google2fa_secret')->nullable()->after('github_refresh_token'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + // + }); + } +}; diff --git a/Web-Application/Manually/database/migrations/2025_04_23_100135_add_verify2fa_to_users_table.php b/Web-Application/Manually/database/migrations/2025_04_23_100135_add_verify2fa_to_users_table.php new file mode 100644 index 0000000..ab90217 --- /dev/null +++ b/Web-Application/Manually/database/migrations/2025_04_23_100135_add_verify2fa_to_users_table.php @@ -0,0 +1,28 @@ +boolean('verify2fa')->default(false)->after('google2fa_secret'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + // + }); + } +}; diff --git a/Web-Application/Manually/resources/views/auth/enable-2fa.blade.php b/Web-Application/Manually/resources/views/auth/enable-2fa.blade.php new file mode 100644 index 0000000..699a3ed --- /dev/null +++ b/Web-Application/Manually/resources/views/auth/enable-2fa.blade.php @@ -0,0 +1,35 @@ +@extends('layouts.app') + +@section('content') +
{{ $secret }}
++ Please enter your secret code to login +
+ +