Dobrodošli u osmi deo serijala na ITNetwork.rs! Do sada smo prošli kroz monolit i mikroservise, frontend arhitekturu, API-je, baze, mikroservise, serverless, i performanse. Danas ulazimo u najosetljiviju tačku svake aplikacije – bezbednost korisnika. Naučićemo kako pravilno implementirati autentifikaciju (AuthN) i autorizaciju (AuthZ), koristeći moderne standarde poput JWT, OAuth2, OpenID Connect, Passkeys i RBAC/ABAC.
Šta je AuthN, a šta AuthZ?
| Pojam | Pitanje | Primer |
|---|---|---|
| AuthN (Autentifikacija) | „Ko si ti?“ | Login sa emailom/lozinkom |
| AuthZ (Autorizacija) | „Šta smeš da radiš?“ | Admin vidi /admin, korisnik ne |
Loša bezbednost = kraj projekta.
1. Tradicionalni pristupi – lozinke i sesije
Problemi:
- Lozinke se čuvaju u bazi → rizik od breach-a
- Sesije zahtevaju server state → teško skaliranje
- Phishing napadi
Rešenje: Hash-ovanje lozinki
import bcrypt from 'bcrypt';
const saltRounds = 12;
const hash = await bcrypt.hash(password, saltRounds);
const match = await bcrypt.compare(password, storedHash);
Nikad ne čuvaj plain-text lozinke!
2. JWT (JSON Web Tokens) – stateless autentifikacija
Kako radi?

Struktura JWT-a
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAwMzYwMH0.
signature
| + | Objašnjenje |
|---|---|
| Stateless | Nema sesije u bazi |
| Skalabilan | Bilo koji server može proveriti |
| Frontend-friendly | Čuva se u localStorage ili HttpOnly cookie |
| – | Objašnjenje |
|---|---|
| Ne može se opozvati | Samo čekanjem na exp |
| Veliki token | Ako ima mnogo claim-ova |
| localStorage rizik | XSS napad |
Kada koristiti?
- „Login sa Google/Facebook/GitHub“
- Mobilne aplikacije
- B2B integracije
Flow (Authorization Code + PKCE)

Alati
| Alat | Namena |
|---|---|
| Auth0 | Kompletan IdP |
| Keycloak | Open-source, self-hosted |
| NextAuth.js | Next.js integracija |
| Supabase Auth | PostgreSQL + GoTrue |
1. Magic Link
Pošalji email: https://app.rs/login?token=abc123
→ Token važi 10 min, jednokratan
- Biometrika (FaceID, otisak)
- Hardverski ključevi (YubiKey)
- Nema lozinki!
Primer: NextAuth.js + Passkeys
// pages/api/auth/[...nextauth].ts
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { verifyPasskey } from 'webauthn';
export default NextAuth({
providers: [
CredentialsProvider({
// role: admin, editor, viewer
if (user.role === 'admin') allow('/admin');
// middleware.ts
import { NextResponse } from 'next/server';
export function middleware(req) {
const token = req.cookies.get('auth');
const user = verifyJWT(token);
if (req.nextUrl.pathname.startsWith('/admin') && user.role !== 'admin') {
return NextResponse.redirect(new URL('/login', req.url));
}
return NextResponse.next();
}
// Pravila:
if (user.department === 'HR' && resource.type === 'salary') allow;
Fleksibilnije, ali kompleksnije.
6. Bezbednost u mikroservisima
Izazovi:
- JWT mora biti proveren u svakom servisu
- Nema centralne sesije
Rešenja:
| Rešenje | Kako |
|---|---|
| API Gateway | Proveri JWT pre rutiranja |
| Service Mesh (Istio) | mTLS + JWT validacija |
| Shared Secret | HS256 potpis |
| RSA/ES256 | Javni ključ u svim servisima |
app/
├── login/page.tsx
├── dashboard/page.tsx
├── admin/
│ └── page.tsx
├── api/
│ └── auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import GoogleProvider from 'next-auth/providers/google';
import { PrismaAdapter } from '@next-auth/prisma-adapter';
import { prisma } from '@/lib/prisma';
import bcrypt from 'bcrypt';
const handler = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
name: 'Email',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' }
},
async authorize(credentials) {
const user = await prisma.user.findUnique({ where: { email: credentials.email } });
if (!user) return null;
const match = await bcrypt.compare(credentials.password, user.passwordHash);
if (match) return user;
return null;
}
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET
})
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.role = user.role;
}
return token;
},
async session({ session, token }) {
session.user.role = token.role;
return session;
}
},
pages: {
signIn: '/login'
}
});
export { handler as GET, handler as POST };
Zaštita rute (Server Component)
// app/admin/page.tsx
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
export default async function AdminPage() {
const session = await getServerSession(authOptions);
if (!session || session.user.role !== 'admin') {
return <div>403 - Zabranjeno</div>;
}
return <h1>Admin panel – dobrodošao, {session.user.name}!</h1>;
}
| Pravilo | Objašnjenje |
|---|---|
| HttpOnly + Secure cookie | Za JWT – ne localStorage |
| Short-lived access token | 15 min |
| Refresh token (HttpOnly) | Dugovečan, rotira se |
| CORS ograničenja | allowedOrigins: [‘https://app.rs’] |
| Rate limit na login | 5 pokušaja / 10 min |
| 2FA / MFA | Za admin panele |
Poređenje: Auth metode
| Metoda | Sigurnost | UX | Skalabilnost | Kada koristiti |
|---|---|---|---|---|
| Lozinka + Sesija | ★★ | ★★★ | ★★ | Legacy |
| JWT + Cookie | ★★★★ | ★★★★ | ★★★★★ | Web app |
| OAuth2 / OIDC | ★★★★★ | ★★★★ | ★★★★★ | Socijalni login |
| Passkeys | ★★★★★ | ★★★★★ | ★★★★ | Budućnost |
Frontend (Next.js)
↓ (HttpOnly cookie)
API Gateway (JWT validacija)
↓
Mikroservisi (RBAC po claim-ovima)
↓
Baza (nikad plain lozinke)
Zlatno pravilo: „Nikad ne veruj klijentu. Uvek proveravaj na serveru.“
Šta dalje?
U devetom delu ulazimo u DevOps, CI/CD i observability:
- GitOps
- GitHub Actions
- Prometheus + Grafana
- OpenTelemetry
Tvoj izazov
- Implementiraj NextAuth.js sa:
- Email/lozinka
- Google login
- RBAC middleware
- Dodaj Passkey podršku (koristi simplewebauthn)
- Uradi penetration test sa OWASP ZAP
- Komentariši ispod: „Koju auth metodu koristiš trenutno?“
Autor: Dušan Antonijević – saradnik ITNetwork.rs
Hvala na čitanju! Podeli tekst sa kolegama.



