Backend Authentication
JWT dual-token auth, middleware, NIK encryption, and password reset.
Dual-token JWT
Secrets from env: JWT_ACCESS_SECRET, JWT_REFRESH_SECRET.
| Token | Algorithm | Claims | Expiry | Storage |
|---|---|---|---|---|
| Access | HS256 | {user_id, exp, iat} | 60 minutes | client localStorage |
| Refresh | HS256 | {user_id, exp, iat} | 60 days | users.refresh_token column |
| Super-admin | HS256 | {username, role:"superadmin", exp, iat} | 1 hour | client localStorage |
Refresh validates both the signed token and that it matches the value stored on the user row — so a revoked/rotated refresh token is rejected even if the signature is valid.
Middleware (middleware/auth.go)
Handler identity extraction
Every handler that needs the caller identity repeats:
claims := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)
userID := uint(claims["user_id"].(float64))Password hashing
utils/hash.go — bcrypt (GenerateFromPassword / CompareHashAndPassword).
NIK encryption (PII)
The 16-digit NIK (national ID) is AES-GCM encrypted before storage (utils/AESencrypt.go, key from AES_ENCRYPTION_KEY — 32 bytes):
- Encrypted at registration and admin-create.
- Decrypted on read in
GetProfile,GetUserByID,GetAllAdmins. - There are (commented-out) admin endpoints
EncryptAllNIK/EncryptNIKByIdto migrate legacy plaintext NIKs.
Password reset flow
Registration
POST /api/register accepts {username, email?, password, nik(16), nama_ktp, nama_samaran, nomor_hp, role?, preferences?}:
- NIK encrypted with AES-GCM
- Phone normalized to
62-prefix - Returns
{access_token, refresh_token}
POST /api/auth/check-username checks availability before submission.
Required env vars
config.GetEnv(key) fatals on missing variables — the app refuses to start without all required secrets (JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, AES_ENCRYPTION_KEY, etc.).