Merge 193cc5358f into 9e17c85f1b
This commit is contained in:
commit
b75c183c2c
|
|
@ -131,9 +131,28 @@ class LoginController extends BaseController
|
||||||
|
|
||||||
//2FA
|
//2FA
|
||||||
if ($user->google_2fa_secret && $request->has('one_time_password')) {
|
if ($user->google_2fa_secret && $request->has('one_time_password')) {
|
||||||
$google2fa = new Google2FA();
|
$otp = $request->input('one_time_password');
|
||||||
|
$secret = decrypt($user->google_2fa_secret);
|
||||||
|
$timestamp = false;
|
||||||
|
|
||||||
if (strlen($request->input('one_time_password')) == 0 || !$google2fa->verifyKey(decrypt($user->google_2fa_secret), $request->input('one_time_password'))) {
|
if (strlen($otp) > 0) {
|
||||||
|
// Try SHA512 first (new algorithm) with timestamp to prevent OTP reuse
|
||||||
|
$google2fa = new Google2FA();
|
||||||
|
$google2fa->setAlgorithm(\PragmaRX\Google2FA\Support\Constants::SHA512);
|
||||||
|
$timestamp = $google2fa->verifyKeyNewer($secret, $otp, $user->google_2fa_ts ?? 0);
|
||||||
|
|
||||||
|
// Fall back to SHA1 for existing users (backward compatibility)
|
||||||
|
if ($timestamp === false) {
|
||||||
|
$google2fa = new Google2FA();
|
||||||
|
$timestamp = $google2fa->verifyKeyNewer($secret, $otp, $user->google_2fa_ts ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($timestamp !== false) {
|
||||||
|
// Update timestamp to prevent OTP reuse
|
||||||
|
$user->google_2fa_ts = $timestamp;
|
||||||
|
$user->save();
|
||||||
|
} else {
|
||||||
return response()
|
return response()
|
||||||
->json(['message' => ctrans('texts.invalid_one_time_password')], 422)
|
->json(['message' => ctrans('texts.invalid_one_time_password')], 422)
|
||||||
->header('X-App-Version', config('ninja.app_version'))
|
->header('X-App-Version', config('ninja.app_version'))
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@ class TwoFactorController extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
$google2fa = new Google2FA();
|
$google2fa = new Google2FA();
|
||||||
$secret = $google2fa->generateSecretKey();
|
$google2fa->setAlgorithm(\PragmaRX\Google2FA\Support\Constants::SHA512);
|
||||||
|
$secret = $google2fa->generateSecretKey(32);
|
||||||
|
|
||||||
$qr_code = $google2fa->getQRCodeUrl(
|
$qr_code = $google2fa->getQRCodeUrl(
|
||||||
config('ninja.app_name'),
|
config('ninja.app_name'),
|
||||||
|
|
@ -48,6 +49,9 @@ class TwoFactorController extends BaseController
|
||||||
$secret
|
$secret
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Append algorithm parameter for SHA512
|
||||||
|
$qr_code .= '&algorithm=SHA512';
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'secret' => $secret,
|
'secret' => $secret,
|
||||||
'qrCode' => $qr_code,
|
'qrCode' => $qr_code,
|
||||||
|
|
@ -58,20 +62,30 @@ class TwoFactorController extends BaseController
|
||||||
|
|
||||||
public function enableTwoFactor(EnableTwoFactorRequest $request)
|
public function enableTwoFactor(EnableTwoFactorRequest $request)
|
||||||
{
|
{
|
||||||
$google2fa = new Google2FA();
|
|
||||||
|
|
||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
$secret = $request->input('secret');
|
$secret = $request->input('secret');
|
||||||
$oneTimePassword = $request->input('one_time_password');
|
$oneTimePassword = $request->input('one_time_password');
|
||||||
|
|
||||||
if ($google2fa->verifyKey($secret, $oneTimePassword) && $user->phone && $user->email_verified_at) {
|
// Try SHA512 first (new algorithm)
|
||||||
|
$google2fa = new Google2FA();
|
||||||
|
$google2fa->setAlgorithm(\PragmaRX\Google2FA\Support\Constants::SHA512);
|
||||||
|
$timestamp = $google2fa->verifyKeyNewer($secret, $oneTimePassword, 0);
|
||||||
|
|
||||||
|
// Fall back to SHA1 for manual entry (authenticator apps default to SHA1)
|
||||||
|
if ($timestamp === false) {
|
||||||
|
$google2fa = new Google2FA();
|
||||||
|
$timestamp = $google2fa->verifyKeyNewer($secret, $oneTimePassword, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($timestamp !== false && $user->phone && $user->email_verified_at) {
|
||||||
$user->google_2fa_secret = encrypt($secret);
|
$user->google_2fa_secret = encrypt($secret);
|
||||||
|
$user->google_2fa_ts = $timestamp;
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200);
|
return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200);
|
||||||
} elseif (! $secret || ! $google2fa->verifyKey($secret, $oneTimePassword)) {
|
} elseif (! $secret || $timestamp === false) {
|
||||||
return response()->json(['message' => ctrans('texts.invalid_one_time_password')], 400);
|
return response()->json(['message' => ctrans('texts.invalid_one_time_password')], 400);
|
||||||
} elseif (! $user->phone) {
|
} elseif (! $user->phone) {
|
||||||
return response()->json(['message' => ctrans('texts.set_phone_for_two_factor')], 400);
|
return response()->json(['message' => ctrans('texts.set_phone_for_two_factor')], 400);
|
||||||
|
|
@ -94,6 +108,7 @@ class TwoFactorController extends BaseController
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
$user->google_2fa_secret = null;
|
$user->google_2fa_secret = null;
|
||||||
|
$user->google_2fa_ts = null;
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
return $this->itemResponse($user);
|
return $this->itemResponse($user);
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,7 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||||
'remember_token',
|
'remember_token',
|
||||||
'google_2fa_secret',
|
'google_2fa_secret',
|
||||||
'google_2fa_phone',
|
'google_2fa_phone',
|
||||||
|
'google_2fa_ts',
|
||||||
'remember_2fa_token',
|
'remember_2fa_token',
|
||||||
'slack_webhook_url',
|
'slack_webhook_url',
|
||||||
'referral_earnings',
|
'referral_earnings',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
|
||||||
|
* Run the migrations.
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
public function up(): void
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
|
||||||
|
|
||||||
|
$table->integer('google_2fa_ts')->nullable()->after('google_2fa_secret');
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
|
||||||
|
* Reverse the migrations.
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
|
||||||
|
|
||||||
|
$table->dropColumn('google_2fa_ts');
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue