Skip to content

Perbaikan tampilan durasi lockout akun login (menit dan detik) serta koreksi perhitungan selisih waktu Carbon#1099

Open
habibie11 wants to merge 1 commit into
rilis-devfrom
dev-1098
Open

Perbaikan tampilan durasi lockout akun login (menit dan detik) serta koreksi perhitungan selisih waktu Carbon#1099
habibie11 wants to merge 1 commit into
rilis-devfrom
dev-1098

Conversation

@habibie11

Copy link
Copy Markdown
Contributor

issue # #1098

🎯 Deskripsi

Pull request ini memperbaiki bug pada sistem autentikasi (web dan API) di mana informasi waktu tunggu saat akun terkunci (isLocked()) menampilkan durasi 0 menit (contoh pesan: AKUN TERKUNCI. Terlalu banyak gagal login. Coba lagi dalam 0 menit.).

Penyebab utamanya adalah kesalahan perhitungan pada method getLockoutRemainingSeconds() di model User. Pemanggilan $this->lockout_expires_at->diffInSeconds(now()) pada Carbon v3 secara default mengembalikan nilai negatif (-900 detik) untuk waktu di masa depan, sehingga fungsi max(0, -900) mengubah hasilnya menjadi 0.

Dengan mengganti perhitungan tersebut menggunakan now()->diffInSeconds($this->lockout_expires_at, false), selisih hitung mundur sekarang menghasilkan durasi detik yang positif dan akurat. Furthermore, PR ini menambahkan logika pemformatan waktu dinamis pada AuthController dan LoginController: jika sisa waktu di bawah 60 detik, pesan error akan otomatis menampilkan satuan detik (... Coba lagi dalam 45 detik.), dan jika di atas atau sama dengan 60 detik akan dibulatkan ke atas dalam satuan menit (... Coba lagi dalam 15 menit.).


🛠️ Perubahan yang Dilakukan

1. app/Models/User.php

Fix — Koreksi perhitungan selisih waktu diffInSeconds pada method getLockoutRemainingSeconds():

  • Mengganti $this->lockout_expires_at->diffInSeconds(now()) dengan now()->diffInSeconds($this->lockout_expires_at, false) agar mengembalikan detik sisa hitung mundur yang akurat dan positif.
  public function getLockoutRemainingSeconds(): int
  {
      if (!$this->isLocked()) {
          return 0;
      }

-     return max(0, $this->lockout_expires_at->diffInSeconds(now()));
+     return (int) max(0, ceil(now()->diffInSeconds($this->lockout_expires_at, false)));
  }

2. app/Http/Controllers/Api/Auth/AuthController.php

Feat & Fix — Format satuan waktu dinamis (detik vs menit) dan konsistensi pesan peringatan:

  • Menerapkan penentuan $timeText dinamis saat pengecekan akun terkunci (login()), rate limiter, maupun pada saat recordFailedLogin() pertama kali memicu penguncian akun.
  // Check if account is locked
  if ($user && $user->isLocked()) {
      $remainingSeconds = $user->getLockoutRemainingSeconds();
-     $minutes = ceil($remainingSeconds / 60);
+     $timeText = $remainingSeconds < 60 ? "{$remainingSeconds} detik" : ceil($remainingSeconds / 60) . " menit";

      return response()->json([
-         'message' => "AKUN TERKUNCI. Terlalu banyak gagal login. Coba lagi dalam {$minutes} menit.",
+         'message' => "AKUN TERKUNCI. Terlalu banyak gagal login. Coba lagi dalam {$timeText}.",
          'locked' => true,
          'retry_after' => $remainingSeconds,
      ], Response::HTTP_FORBIDDEN);
  }

3. app/Http/Controllers/Auth/LoginController.php

Feat & Fix — Format satuan waktu dinamis pada otentikasi Web:

  • Menerapkan penentuan format $timeText yang sama pada method login(), recordFailedLoginAttempt(), dan checkUserLockoutById().
  if ($user && $user->isLocked()) {
      $remainingSeconds = $user->getLockoutRemainingSeconds();
-     $minutes = ceil($remainingSeconds / 60);
+     $timeText = $remainingSeconds < 60 ? "{$remainingSeconds} detik" : ceil($remainingSeconds / 60) . " menit";

      throw ValidationException::withMessages([
-         $this->username() => "AKUN TERKUNCI. Terlalu banyak gagal login. Coba lagi dalam {$minutes} menit.",
+         $this->username() => "AKUN TERKUNCI. Terlalu banyak gagal login. Coba lagi dalam {$timeText}.",
      ]);
  }

✅ Test Cases yang Diimplementasikan

  • Saat akun baru saja terkunci akibat 5 kali gagal login (durasi 15 menit / 900 detik), pesan error menginformasikan: AKUN TERKUNCI. Terlalu banyak gagal login. Coba lagi dalam 15 menit. (bukan 0 menit).
  • Saat mencoba login kembali ketika sisa waktu lockout di bawah 1 menit (misal: tinggal 45 detik), pesan error otomatis berganti satuan menjadi detik: AKUN TERKUNCI. Terlalu banyak gagal login. Coba lagi dalam 45 detik.
  • Nilai numerik retry_after dan lockout_expires_in pada JSON response API mengembalikan durasi detik yang valid (bukan 0), kompatibel dengan timer hitung mundur pada frontend/client.
  • Saat masa penguncian akun telah berakhir (lockout_expires_at di masa lalu), fungsi isLocked() kembali bernilai false dan getLockoutRemainingSeconds() mengembalikan 0.

📸 Cara Menjalankan Uji Coba Manual

  1. Buka halaman login web (/login) atau gunakan API client (Postman/cURL) ke endpoint /api/v1/auth/login.
  2. Masukkan email/username sembarang yang terdaftar di database dengan kata sandi yang salah secara berulang sebanyak 5 kali.
  3. Pada percobaan gagal ke-5, amati pesan error yang muncul:
    • Seharusnya: AKUN TERKUNCI. Terlalu banyak gagal login (5 kali). Coba lagi dalam 15 menit.
  4. Tunggu hingga sisa waktu penguncian tinggal di bawah 1 menit (atau modifikasi nilai lockout_expires_at user di database/tinker menjadi now()->addSeconds(30)), kemudian coba login kembali.
    • Seharusnya: Pesan error secara dinamis menampilkan satuan detik: AKUN TERKUNCI. Terlalu banyak gagal login. Coba lagi dalam 30 detik.

🤖 Cara Menjalankan Uji Coba Otomatis (Automated Test)

Untuk memvalidasi perhitungan sisa detik dan format teks waktu melalui simulasi skrip pengujian atau automated testing, jalankan perintah berikut di terminal:

# Menjalankan simulasi verifikasi langsung menggunakan skrip scratch yang disediakan
php C:\Users\habib\.gemini\antigravity-ide\brain\104ace05-2020-4d9f-953a-f33a520925ad\scratch\verify_all.php

Atau melalui artisan test (jika environment testing telah disiapkan):

php artisan test --filter=LoginControllerTest

📸 Screenshot atau Video

image

⚠️ Catatan Penting

Perubahan pada struktur pesan ini tetap mempertahankan kompatibilitas penuh dengan struktur respons JSON eksisting (locked, retry_after, dan lockout_expires_in). Frontend UI web maupun aplikasi mobile yang menggunakan retry_after sebagai acuan timer hitung mundur kini akan mendapatkan durasi detik yang presisi tanpa ada gangguan pada antarmuka.

@github-actions

Copy link
Copy Markdown

🔄 AI PR Review sedang antri di server...

Proses review akan segera dimulai di background — hasil akan muncul sebagai komentar setelah selesai.
Powered by CrewAI · PR #1099

@habibie11
habibie11 requested a review from vickyrolanda July 20, 2026 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant