\section{JWT} \begin{frame}[shrink=15]{Mi az a JWT?} \begin{block}{JSON Web Token (RFC 7519)} Kompakt, önálló token információ biztonságos továbbításához. \end{block} \begin{itemize} \item Digitálisan aláírt, Base64URL kódolt \item Használat: authentikáció, stateless session \item \textbf{NEM} titkosított - payload olvasható! \end{itemize} \end{frame} \begin{frame}[fragile,shrink=15]{JWT formátum} \texttt{\textcolor{red}{HEADER}.\textcolor{purple}{PAYLOAD}.\textcolor{blue}{SIGNATURE}} \begin{tiny} \begin{verbatim} eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZ SI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36P \end{verbatim} \end{tiny} \begin{itemize} \item \textcolor{red}{Header}: Algoritmus + token típus \item \textcolor{purple}{Payload}: Claims (user adatok) \item \textcolor{blue}{Signature}: Integritás védelem \end{itemize} \end{frame} \begin{frame}[fragile,shrink=15]{JWT Header \& Payload} \begin{columns} \begin{column}{0.48\textwidth} \textbf{Header:} \begin{verbatim} { "alg": "HS256", "typ": "JWT" } \end{verbatim} \end{column} \begin{column}{0.48\textwidth} \textbf{Payload:} \begin{verbatim} { "sub": "1234", "name": "John", "iat": 1516239022, "exp": 1516242622 } \end{verbatim} \end{column} \end{columns} \begin{alertblock}{Fontos} Ne tároljunk jelszót payload-ban! \end{alertblock} \end{frame} \begin{frame}[fragile,shrink=15]{JWT Signature} \begin{verbatim} HMACSHA256( base64(header) + "." + base64(payload), secret ) \end{verbatim} \begin{columns} \begin{column}{0.48\textwidth} \textbf{HMAC:} Egy kulcs, gyors (HS256) \end{column} \begin{column}{0.48\textwidth} \textbf{RSA:} Privát+publikus, biztonságosabb (RS256) \end{column} \end{columns} \end{frame} \begin{frame}[fragile,shrink=20]{JWT generálás Node.js} \begin{verbatim} const jwt = require('jsonwebtoken'); const payload = { userId: user.id, role: user.role }; const token = jwt.sign( payload, process.env.JWT_SECRET, { expiresIn: '1h' } ); res.json({ token }); \end{verbatim} \end{frame} \begin{frame}[fragile,shrink=20]{JWT validálás Node.js} \begin{verbatim} const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token' }); try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (err) { res.status(403).json({ error: 'Invalid' }); } \end{verbatim} \end{frame} \begin{frame}[fragile,shrink=20]{JWT middleware} \begin{verbatim} const authJWT = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).send('Token required'); jwt.verify(token, process.env.JWT_SECRET, (err, user) => { if (err) return res.status(403).send('Invalid'); req.user = user; next(); }); }; app.get('/protected', authJWT, (req, res) => { res.json({ data: 'Secret', user: req.user }); }); \end{verbatim} \end{frame} \begin{frame}[shrink=15]{JWT vs Session} \begin{columns} \begin{column}{0.48\textwidth} \textbf{JWT:} \begin{itemize} \item Stateless \item Skálázható \item Cross-domain \item Nehéz visszavonni \item Token méret \end{itemize} \end{column} \begin{column}{0.48\textwidth} \textbf{Session:} \begin{itemize} \item Stateful \item Szerver tárolja \item Könnyű visszavonás \item Nehezebb skálázás \item Kis cookie \end{itemize} \end{column} \end{columns} \end{frame} \begin{frame}[shrink=15]{Access \& Refresh Token} \begin{block}{Két token stratégia} \textbf{Access token}: Rövid élet (15 perc), API hozzáférés\\ \textbf{Refresh token}: Hosszú élet (7 nap), új access token generálás \end{block} \begin{enumerate} \item Login $\to$ Access + Refresh token \item API kérés Access token-nel \item Access lejár $\to$ Refresh-el új Access-t kér \item Refresh lejár $\to$ Újra login \end{enumerate} \end{frame} \begin{frame}[fragile,shrink=20]{Refresh Token példa} \begin{verbatim} app.post('/refresh', (req, res) => { const { refreshToken } = req.body; if (!refreshToken) return res.sendStatus(401); jwt.verify(refreshToken, REFRESH_SECRET, (err, user) => { if (err) return res.sendStatus(403); const accessToken = jwt.sign( { userId: user.userId }, ACCESS_SECRET, { expiresIn: '15m' } ); res.json({ accessToken }); }); }); \end{verbatim} \end{frame} \begin{frame}[shrink=15]{JWT Security Best Practices} \begin{enumerate} \item Rövid lejárat (15-60 perc) \item Erős secret kulcs (min 256 bit) \item HTTPS kötelező \item Ne tároljunk érzékeny adatot payload-ban \item Validáld mindig az \texttt{exp} és \texttt{iat} claim-eket \item Használj RS256-ot HS256 helyett production-ben \item Refresh token rotation \item Token blacklist megfontolása \end{enumerate} \end{frame}