feat: inisialisasi project kit v2

This commit is contained in:
2026-05-21 15:57:29 +07:00
commit d4fd478e1f
271 changed files with 35300 additions and 0 deletions
@@ -0,0 +1,177 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Inertia\Inertia;
use PragmaRX\Google2FA\Google2FA;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
class TwoFactorController extends Controller
{
protected Google2FA $google2fa;
public function __construct()
{
$this->google2fa = new Google2FA();
}
/**
* Show 2FA setup page (generate QR code data).
*/
public function show()
{
$user = auth()->user();
// If not yet set up, generate a new secret
if (!$user->two_factor_secret) {
$secret = $this->google2fa->generateSecretKey();
$user->update(['two_factor_secret' => $secret]);
}
$secret = $user->two_factor_secret;
$otpUrl = $this->google2fa->getQRCodeUrl(
config('app.name'),
$user->email,
$secret
);
// Generate QR code as SVG using BaconQrCode
$renderer = new \BaconQrCode\Renderer\ImageRenderer(
new \BaconQrCode\Renderer\RendererStyle\RendererStyle(200),
new \BaconQrCode\Renderer\Image\SvgImageBackEnd()
);
$qrCode = (new \BaconQrCode\Writer($renderer))->writeString($otpUrl);
$qrCodeBase64 = 'data:image/svg+xml;base64,' . base64_encode($qrCode);
return Inertia::render('TwoFactor/Setup', [
'enabled' => !is_null($user->two_factor_confirmed_at),
'qr_code' => $qrCodeBase64,
'secret' => $secret,
'recovery_codes' => $user->two_factor_recovery_codes
? json_decode($user->two_factor_recovery_codes, true)
: [],
]);
}
/**
* Confirm & enable 2FA.
*/
public function enable(Request $request)
{
$request->validate([
'code' => 'required|string',
]);
$user = auth()->user();
$secret = $user->two_factor_secret;
$valid = $this->google2fa->verifyKey($secret, $request->code);
if (!$valid) {
return back()->withErrors(['code' => 'Invalid authentication code. Please try again.']);
}
// Generate recovery codes
$recoveryCodes = Collection::times(8, fn() => Str::random(10) . '-' . Str::random(10));
$user->update([
'two_factor_confirmed_at' => now(),
'two_factor_recovery_codes' => json_encode($recoveryCodes->toArray()),
]);
return back()->with('success', 'Two-Factor Authentication has been enabled successfully.');
}
/**
* Disable 2FA.
*/
public function disable(Request $request)
{
$request->validate([
'password' => 'required|current_password',
]);
auth()->user()->update([
'two_factor_secret' => null,
'two_factor_recovery_codes' => null,
'two_factor_confirmed_at' => null,
]);
return back()->with('success', 'Two-Factor Authentication has been disabled.');
}
/**
* Regenerate recovery codes.
*/
public function regenerateCodes()
{
$recoveryCodes = Collection::times(8, fn() => Str::random(10) . '-' . Str::random(10));
auth()->user()->update([
'two_factor_recovery_codes' => json_encode($recoveryCodes->toArray()),
]);
return back()->with('success', 'Recovery codes have been regenerated.');
}
/**
* Show the 2FA challenge screen (after login).
*/
public function challenge(Request $request)
{
if (!$request->session()->has('two_factor_user_id')) {
return redirect()->route('login');
}
return Inertia::render('TwoFactor/Challenge');
}
/**
* Verify the 2FA challenge code after login.
*/
public function verify(Request $request)
{
$request->validate([
'code' => 'required|string',
]);
$userId = $request->session()->get('two_factor_user_id');
if (!$userId) {
return redirect()->route('login');
}
$user = \App\Models\User::find($userId);
if (!$user || !$user->two_factor_secret) {
$request->session()->forget('two_factor_user_id');
return redirect()->route('login');
}
$code = $request->code;
$valid = $this->google2fa->verifyKey($user->two_factor_secret, $code);
if (!$valid) {
$recoveryCodes = json_decode($user->two_factor_recovery_codes ?? '[]', true);
if (in_array($code, $recoveryCodes)) {
$remaining = array_filter($recoveryCodes, fn($c) => $c !== $code);
$user->update(['two_factor_recovery_codes' => json_encode(array_values($remaining))]);
$valid = true;
}
}
if (!$valid) {
return back()->withErrors(['code' => 'Invalid code. Please try again.']);
}
$request->session()->forget('two_factor_user_id');
\Illuminate\Support\Facades\Auth::login($user);
$request->session()->regenerate();
return redirect()->intended(route('dashboard'));
}
}