Token Authentication and Authorization
By Syncfusion®, Inc. · Last updated
Sessions versus tokens, how a JWT is built, login that signs tokens, Bearer middleware, expiry and refresh tokens, role and ownership checks, token storage, Passport and Google sign-in.
What you will learn
- Explain authentication versus authorization
- Sign and verify JWTs
- Protect routes with Bearer middleware
- Use short access tokens and refresh tokens
- Check roles and ownership on every route
Token Authentication Introduction
Sessions work well for websites. Mobile apps and other servers usually log in a different way.
- Authentication = proving who you are.
- Authorization = deciding what you may do.
- Token = a signed piece of text the client sends with every request.
- JWT (JSON Web Token) = the most common token format. It carries a few facts, called claims.
- The client sends it in a header:
Authorization: Bearer <token>. - The server checks the signature, so nobody can change the claims without the secret.
Simple lesson: A token is a signed ticket. The server checks the signature, then trusts the claims.
A Real-World Example
Think about the school's annual day.
- At the office, your ID is checked once. You get a printed pass with your name, class and a stamp.
- At each gate, the guard only checks the stamp, and reads your class from the pass.
- The pass says "valid until 6 PM". After that, it is useless.
- A pass for "Class 8" does not open the staff room.
The office is your login route. The pass is a JWT. The stamp is the signature. The staff room rule is authorization.
In this lesson, you will learn:
- How sessions and tokens differ, and when to use each.
- What is inside a JWT.
- How to issue a token at login and check it on every request.
- How expiry and refresh tokens work.
- How roles and ownership checks protect data.
- Where tokens should be stored, and where Passport and Google sign-in fit.
Sessions vs Tokens
Sessions
- The server stores the login. The cookie only holds an ID. See Cookies and Sessions.
- Easy to log someone out: delete the session.
- The browser sends the cookie by itself.
- Best for: classic websites and apps on the same domain.
Tokens
- The token itself holds the login facts. The server stores nothing per user.
- Hard to cancel before it expires, so keep tokens short-lived.
- The client must attach the
Authorizationheader itself. - Best for: mobile apps, single-page apps on another domain, and server-to-server calls.
Both are fine. Many websites use sessions, many mobile apps use tokens, and some apps use both: a session for the website, tokens for the app.
Simple lesson: Sessions keep the login on the server. Tokens carry it with every request.
Inside a JWT
A JWT is three base64url parts joined by dots: header.payload.signature.
- Header: the algorithm, such as
HS256. - Payload: the claims, such as
sub(user ID),role,iat(issued at) andexp(expires). - Signature: an HMAC of the first two parts, made with your secret.
- Anyone can read the payload. Only the secret can sign it.
- So never put passwords or private details in a JWT.
expisiatplus 900 seconds: 15 minutes.- Install the package on your computer with
npm install jsonwebtoken.
A JWT payload is not secret. Paste any token into a decoder and you can read it. Keep only IDs and roles in it.
Simple lesson: Anyone can read a JWT. Only the server can make a valid one.
Logging In and Checking Tokens
The flow has two parts:
- POST /api/login checks the password, then signs a token with the user's ID and role.
- requireAuth middleware reads
Authorization: Bearer <token>on protected routes. - It calls
jwt.verify. A missing, changed or expired token gets 401. - A valid token puts the claims on
req.user, then callsnext().
- Log in, copy the
token, open + Request headers and typeAuthorization: Bearer <token>. Then sendGET /api/me. - The forged token was signed with another secret. The expired one is past its
exp. - This lab compares plain passwords to stay short. Real apps compare hashes. See Cookies and Sessions.
Simple lesson: Sign at login. Verify on every protected request. 401 for anything wrong.
Expiry and Refresh Tokens
Short tokens are safer, but logging in every 15 minutes is annoying.
- Access token = short-lived (5 to 15 minutes). Sent on every API call.
- Refresh token = long-lived (days). Used only to get a new access token.
- Keep the refresh token in an httpOnly, secure, sameSite cookie, so scripts cannot read it.
- Store refresh tokens on the server too, so logout can cancel them.
- Rotate them: each refresh gives a new refresh token and cancels the old one.
- After logout,
/api/refreshanswers 401. The refresh token was cancelled on the server.
Simple lesson: Short access tokens, a cancellable refresh token in a safe cookie.
Roles and Ownership
Being logged in is not the same as being allowed.
- Role check = only
staffmay change prices. Wrong role gets 403. - Ownership check = a student may read their own orders, not other students' orders.
- Missing ownership checks are one of the most common API bugs: changing
/orders/41to/orders/42shows someone else's data. - Check ownership in the route, using the ID from the token, never an ID from the request body.
- Some APIs answer 404 instead of 403 for other people's data, so attackers cannot even see it exists.
Simple lesson: Authentication says who. Roles and ownership checks say what. Check both on every route.
Storing Tokens and Other Login Options
In the Browser
- httpOnly cookies cannot be read by scripts, so XSS cannot steal them. Add CSRF protection. See Express Security.
- localStorage is easy, but any injected script can read it.
- Memory (a JavaScript variable) is lost on refresh, but is safest for short access tokens.
- Mobile apps use the phone's secure storage.
- The browser sends the cookie by itself, like the lab's client does here.
Passport and Google Sign-In
- Passport = a popular Express library with "strategies" for many login methods.
- OAuth 2.0 / OpenID Connect = "Sign in with Google" and similar. Users log in on Google's page.
- Your server receives a code, checks it with Google, then creates its own session or token.
- You never see or store the user's Google password.
import passport from "passport";
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
passport.use(new GoogleStrategy(
{ clientID: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET, callbackURL: "/auth/google/callback" },
(accessToken, refreshToken, profile, done) => done(null, { id: profile.id, name: profile.displayName }),
));
app.get("/auth/google", passport.authenticate("google", { scope: ["profile", "email"] }));
app.get("/auth/google/callback", passport.authenticate("google", { session: false }), (req, res) => {
res.json({ token: jwt.sign({ sub: req.user.id }, process.env.JWT_SECRET, { expiresIn: "15m" }) });
});
Easy way to remember: who logs you in can change. What you do after login stays the same: create your own session or token, then check roles and ownership.
Simple lesson: Store tokens where scripts cannot reach them, and let trusted providers handle passwords when you can.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Secrets in the JWT payload | JWTs are encrypted | Anyone can read the payload | Private data leaks |
jwt.decode instead of jwt.verify | Decoding checks it | Only verify checks the signature | Forged tokens are accepted |
| Long-lived access tokens | Fewer logins is friendlier | Short access, refresh for long sessions | A stolen token works for weeks |
| No ownership check | Logged-in users only read their own data | Check order.studentId === req.user.sub | Anyone reads anyone's orders |
| Trusting a user ID from the body | The client sends its own ID | Use req.user.sub from the token | Users act as other users |
| Weak or hard-coded secret | Any text works | A long random secret from the environment | Anyone can sign valid tokens |
No algorithms option in verify | The default is safe enough | Pin ["HS256"] | Algorithm-confusion attacks |
Check Yourself
Exercises
What to Remember
- Authentication proves who. Authorization decides what.
- A JWT is header.payload.signature. Anyone can read it, only your secret can sign it.
- Sign at login,
jwt.verifyon every protected request, 401 for anything wrong. - Short access tokens, plus a cancellable refresh token in an httpOnly cookie.
- Check roles and ownership on every route, using the ID from the token.
- Passport and Google sign-in handle the login. You still create your own session or token.
Next: Redirects, Content Negotiation and Proxies. You will send people to the right place, in the right format, behind a proxy.