Episode ini membahas keamanan dan autentikasi: pola autentikasi dengan JWT, melindungi rute dengan auth guards, penyimpanan token dan HTTP calls yang aman, serta role-based authorization dan permission checks.

Sebagian besar aplikasi menyimpan data pengguna — dan data itu harus dilindungi. Autentikasi memverifikasi siapa penggunanya, sementara otorisasi menentukan apa yang boleh mereka akses.
Episode 12 membahas pola autentikasi dengan JWT, melindungi rute dengan auth guards, penyimpanan token dan HTTP calls yang aman, serta role-based authorization dan permission checks. Keamanan bukan fitur tambahan — ia bagian dari arsitektur.
JWT (JSON Web Token) adalah token yang berisi klaim terverifikasi. Alur umumnya: pengguna login, server mengembalikan access token (dan biasanya refresh token), aplikasi menyimpannya, lalu mengirimkannya di header tiap request.
import { Injectable, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class AuthService {
private readonly http = inject(HttpClient);
readonly pengguna = signal<Pengguna | null>(null);
login(email: string, password: string): void {
this.http.post<LoginResponse>('/api/login', { email, password })
.subscribe({
next: (res) => {
this.simpanToken(res.accessToken);
this.pengguna.set(res.pengguna);
},
error: (err) => console.error('Login gagal', err),
});
}
}login mengirim kredensial, lalu menyimpan access token dan state pengguna. Setelah ini, aplikasi tahu pengguna sudah terautentikasi dan bisa menampilkan UI yang sesuai.
Penyimpanan token menentukan tingkat keamanan:
Praktik terbaik modern: simpan access token di memory, refresh token di httpOnly cookie. Ini menghindari XSS sekaligus mempertahankan sesi.
Guard dari episode 9 dipakai untuk melindungi rute yang butuh login:
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.pengguna() !== null) {
return true;
}
return router.createUrlTree(['/login'], { queryParams: { kembali: router.url } });
};Pasang guard di route yang dilindungi: { path: 'profil', component: ProfilComponent, canActivate: [authGuard] }. Ketika pengguna belum login dan mencoba membuka rute tersebut, dia diarahkan ke halaman login dengan catatan halaman asal.
Kadang token sudah ada, tetapi data pengguna belum dimuat. authGuard bisa menunggu selesainya pembaruan profil:
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
const status = auth.muatProfil();
return status ? true : router.createUrlTree(['/login']);
};muatProfil di sini mengecek token lokal dan memuat data pengguna jika perlu. Guard mengembalikan true hanya setelah state autentikasi benar-benar pasti.
Setiap request API harus membawa token. Tempat terpusatnya adalah HTTP interceptor:
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const auth = inject(AuthService);
const token = auth.getToken();
if (!token) {
return next(req);
}
const reqBaru = req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
});
return next(reqBaru);
};authInterceptor menambahkan header Authorization: Bearer <token> ke setiap request. Interceptor didaftarkan di app.config.ts lewat provideHttpClient(withInterceptors([authInterceptor])). Semua request otomatis terautentikasi tanpa mengubah kode di setiap service.
Saat access token kedaluwarsa, server mengembalikan status 401. Tangani secara terpusat: refresh token lewat endpoint, ulangi request yang gagal, atau arahkan pengguna ke halaman login. Pola ini membuat sesi bertahan tanpa membuat pengguna login berulang kali.
Otorisasi berbasis peran (RBAC) mengecek role pengguna sebelum memberi akses:
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const roleGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.pengguna()?.role === 'admin') {
return true;
}
return router.createUrlTree(['/forbidden']);
};roleGuard hanya mengizinkan pengguna dengan role admin. Halaman forbidden menampilkan pesan akses ditolak. Kombinasikan guard auth dan role untuk area yang membutuhkan keduanya.
Jangan hanya menyembunyikan tombol admin di UI — permission juga harus diverifikasi di backend. UI menyembunyikan atau menonaktifkan aksi yang tidak boleh dijalankan pengguna, sementara backend tetap memvalidasi setiap request. Dua lapis ini melindungi aplikasi dari manipulasi langsung lewat API.
Inti yang harus dibawa pulang:
Di episode 13 selanjutnya kita akan membahas secure web practices — mencegah XSS, CSRF, dan injection, menerapkan content security policy dan secure headers, men-sanitize HTML dan menangani URL aman, serta praktik terbaik keamanan sisi klien.