14 October 2026 web
Cara Menyimpan Auth Token yang Aman di Browser
Auth token / access token / bearer token adalah string yang didapatkan setelah login untuk mengakses data di API.
Auth token disertakan ketika request ke API dengan header Authorization . Contoh:
Authorization: Bearer <token>
Kalau auth token sampai tercuri maka pencuri bisa mengakses ke API sebagai user tersebut tanpa login.
Ada empat langkah menyimpan auth token yang aman dari tercuri di browser:
- Simpan auth token di memori.
- Simpan login state di local storage.
- Simpan refresh token di HTTP only cookie.
- Ketika refresh browser, lakukan refresh token untuk mengambil auth token baru.
Maka ada tiga data yang tempat penyimpanannya berbeda:
- Auth token, di memory. Sulit dicuri dan hilang ketika refresh.
- Login state, di local storage. Mudah dicuri tapi tidak ada manfaatnya.
- Refresh token, di HTTP only cookie. Tidak bisa dicuri dan tetap ada sampai waktu yang ditentukan.
Data di memory sulit untuk dicuri dengan XSS.
Sedangkan data di local storage mudah dicuri dengan XSS. Maka dari itu datanya harus yang tidak bermanfaat bagi pencuri, seperti login state.
Data di HTTP only cookie tidak bisa dicuri dengan XSS, sehingga aman untuk menyimpan refresh token.
Auth token tidak perlu disimpan di HTTP only cookie agar request API tetap stateless tanpa cookie.
Berikut contoh implementasinya.
Proses Login
Di sisi server, ketika login berhasil, kembalikan auth token di response dan kirim cookie refresh token dengan format HTTP only.
Auth token sebaiknya punya durasi kadaluarsa yang singkat, misalnya 15 menit. Sedangkan refresh token bisa lebih lama, misal 1 minggu.
Contoh implementasi login dengan Laravel:
<?php
namespace App\Http\Controllers\Api\AuthController;
use Illuminate\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Cookie;
class AuthController
{
public function login(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required', 'string', 'min:8']
]);
// Validasi User
abort_if(!Auth::attempt($credentials), 401, 'invalid credentials');
$user = Auth::user();
// Buat Refresh Token
$refreshToken = $user->refreshTokens()
->create(['token' => Str::random(32), 'expire_at' => now()->addWeek()]);
// Kirim refresh token di cookie
// Kirim auth token di response body
return response()
->cookie(
name: 'refresh_token',
value: $refreshToken->token,
minutes: now()->diffInMinutes($refreshToken->expire_at),
path: '/',
domain: null,
secure: true,
httpOnly: true,
raw: false,
sameSite: Cookie::SAMESITE_LAX,
)
->json([
'auth_token' => $user->createToken('api')->plainTextToken
]);
}
}
Di sisi browser, simpan auth token di memory dan login state (true) di local storage. Contoh di Vue.
import { ref } from 'vue'
export const loggedIn = ref(localStorage.getItem('logged_in') === 'true')
export const authToken = ref(null)
export async function login(credentials) {
const res = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
})
if (!res.ok) {
throw new Error('invalid credentials')
}
const data = await res.json()
loggedIn.value = true
authToken.value = data.auth_token
localStorage.setItem('logged_in', 'true')
}
Request API
Di server, setiap endpoint API yang membutuhkan autentikasi harus memiliki token di header Authorization dengan format Bearer <token>.
Token kemudian dicek apakah ada di database sekaligus mendapatkan user dari token tersebut.
Di laravel, bisa menggunakan sanctum. Cukup dengan menambahkan middleware auth:sanctum di routes.
<?php
use App\Http\Controllers\Api\AuthController\AuthController;
use Illuminate\Support\Facades\Route;
Route::get('/profile', [AuthController::class, 'getProfile'])
->middleware('auth:sanctum');
Di sisi client, setiap kali request ke API, sertakan auth token ke Authorization header jika user sudah login. Contoh.
import { loggedIn, authToken } from './auth.js'
async function request(url) {
const res = await fetch(url, {
headers: loggedIn.value
? { 'Authorization': `Bearer ${authToken.value}` }
: {}
})
// handle response
}
Refresh Token
Ketika halaman browser direfresh maka auth token di memory akan hilang.
Untuk mendapatkannya lagi perlu dilakukan refresh token ke server.
Refresh token hanya dilakukan jika ada login state di local storage.
import { loggedIn, authToken } from './auth.js'
async function refreshToken() {
const res = await fetch('/api/refresh-token', {
method: 'POST'
})
if (!res.ok) {
loggedIn.value = false
localStorage.removeItem('logged_in')
return;
}
const data = await res.json()
authToken.value = data.auth_token
}
Di server, buat endpoint refresh token yang mengambil token dari cookie lalu divalidasi ke database.
Jika valid maka buat auth token baru dan kirim ke client.
<?php
namespace App\Http\Controllers\Api\AuthController;
use App\Models\RefreshToken;
use Illuminate\Http\Request;
class AuthController
{
public function refreshToken(Request $request)
{
// Ambil refresh token dari cookie
$refreshTokenCookie = $request->cookie('refresh_token');
abort_if(!$refreshTokenCookie, 401);
// Ambil refresh token dari database
$refreshToken = RefreshToken::with('user')
->firstWhere('token', $refreshTokenCookie);
// validasi refresh token
abort_if(!$refreshToken, 401);
abort_if($refreshToken->expire_at->lessThan(now()), 401);
// Buat Auth Token baru
$authToken = $refreshToken->user->createToken('api')->plainTextToken;
return response()
->json([
'auth_token' => $authToken
]);
}
}
Package Pendukung
Saya sudah membuat package untuk memudahkan proses-proses di atas.
- Untuk autentikasi di server yang menggunakan Laravel dan Sanctum, bisa gunakan fineauth.
- Untuk mengelola autentikasi di Vue, bisa gunakan vue-auth-helper.
Kesimpulan
Cara di atas adalah versi sederhana secara konsep yang biasa saya lakukan, tentu ada tambahan detail implementasi yang belum dijelaskan seperti:
- Auto refresh token ketika request error.
- Hapus auth token sebelumnya ketika refresh token.
- Logout.