Home›ZX Parenting›ZX Parenting›Auth Anak (PIN)
AKTIFauthsecurity

Auth Anak (PIN)

Login anak via username + PIN, brute force protection

JSON

💡 Ide & Konsep

Belum ada ide. Klik Edit untuk menambahkan.

Ringkasan

Login anak via username + PIN (bukan email/password).

Endpoint: POST /api/auth/native-anak

Auth: JWT bearer (30 hari expiry)

Pemakai: App anak (zx-agent)

Keamanan:

  • Rate limit: 5 percobaan/menit per username (in-memory Map)
  • Lockout: 10 gagal → 15 menit lock
  • PIN blacklist: 66 PIN lemah (sequential + repeated 4/5/6 digit)
  • Username: regex /^[a-z0-9_]{3,20}$/, 16 reserved words
  • bcrypt hash PIN
  • P2002 unique constraint catch → 409

File:

  • src/lib/validasi.ts — USERNAME_REGEX, USERNAME_RESERVED, PIN_LEMAH, validasiUsername(), validasiPin(), duplikatUsername()
  • src/app/api/auth/native-anak/route.ts — login endpoint
  • src/app/api/anak/[id]/pin/route.ts — reset PIN
  • src/lib/umur.ts — hitungUmur() dari tanggalLahir

PIN Blacklist

66 PIN lemah di-blacklist:

Repeated (30):

  • 4-digit: 0000,1111,2222,...,9999
  • 5-digit: 00000,11111,...,99999
  • 6-digit: 000000,111111,...,999999

Sequential naik (18):

  • 4-digit: 0123,1234,2345,...,6789
  • 5-digit: 01234,...,56789
  • 6-digit: 012345,...,456789

Sequential turun (18):

  • 4-digit: 9876,8765,...,3210
  • 5-digit: 98765,...,54321
  • 6-digit: 987654,...,654321

Catatan:

  • 123456 = sequential naik → DITOLAK
  • 1234 = sequential naik → juga ada, tapi existing PIN tidak di-retroactive reject
  • User tidak bisa reset ke PIN yang ada di blacklist
  • Validasi: validasiPin() di src/lib/validasi.ts

Reset PIN Flow

3 iterasi fix bug reset PIN:

v2.9.5 — Iterasi 1:

  • Bug: dialog fire-and-forget, tutup instan sebelum API respond
  • Fix: async callback (sukses: Boolean) -> Unit + loading spinner + error display via state.error

v2.9.6 — Iterasi 2:

  • Bug: state.error global bisa ter-clear oleh operasi lain → user tidak lihat pesan error
  • Fix: callback (sukses: Boolean, pesan: String?) -> Unit — pesan langsung dari callback, tampilkan lokal di dialog sebagai pesanPin merah

v2.9.7 — Iterasi 3:

  • Bug: reset PIN inline di DialogEditAnak → ambiguitas dua tombol Simpan (Simpan Edit + Simpan PIN Baru). User tekan tombol Simpan (edit) bukan Simpan PIN Baru → reset PIN tidak ter-trigger
  • Fix: pisah ke DialogResetPin composable terpisah dengan field PIN + tombol "Simpan PIN Baru" yang jelas

File:

  • LayarAnak.kt — DialogResetPin composable, DialogEditAnak punya tombol "Reset PIN" yang buka dialog terpisah
  • ZxVm.kt — resetPinAnak(id, pin, onSelesai: (sukses, pesan) -> Unit)
  • MainActivity.kt — wiring onResetPin = { id, pin, onSelesai -> vm.resetPinAnak(id, pin, onSelesai) }
  • Api.kt — anakResetPin @PATCH("anak/{id}/pin"), AnakResetPinReq(val pin: String)
  • Backend: src/app/api/anak/[id]/pin/route.ts — PATCH reset PIN, validasiPin, bcrypt hash

Rate Limit & Lockout

In-memory Map (cukup single instance Coolify):

Map key = username (lowercase)
Value = { gagal: number, lockUntil: number, recent: number[] }

Map key = username (lowercase)

Value = { gagal: number, lockUntil: number, recent: number[] }


**Rules:**
- Max 5 percobaan per menit (sliding window)
- 10 gagal total → lockout 15 menit
- Sukses login → reset state
- Lockout response: `429 { error: "Terlalu banyak percobaan. Coba lagi dalam X menit." }`
- Rate limit response: `429 { error: "Terlalu banyak percobaan. Tunggu sebentar." }`

**Catatan:** In-memory = reset jika container restart. Tidak persistent. Cukup untuk single VPS.

Rules:

  • Max 5 percobaan per menit (sliding window)
  • 10 gagal total → lockout 15 menit
  • Sukses login → reset state
  • Lockout response: 429 { error: "Terlalu banyak percobaan. Coba lagi dalam X menit." }
  • Rate limit response: 429 { error: "Terlalu banyak percobaan. Tunggu sebentar." }

Catatan: In-memory = reset jika container restart. Tidak persistent. Cukup untuk single VPS.