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.

  1. At the office, your ID is checked once. You get a printed pass with your name, class and a stamp.
  2. At each gate, the guard only checks the stamp, and reads your class from the pass.
  3. The pass says "valid until 6 PM". After that, it is useless.
  4. 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:

  1. How sessions and tokens differ, and when to use each.
  2. What is inside a JWT.
  3. How to issue a token at login and check it on every request.
  4. How expiry and refresh tokens work.
  5. How roles and ownership checks protect data.
  6. 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 Authorization header 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) and exp (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.
  • exp is iat plus 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:

  1. POST /api/login checks the password, then signs a token with the user's ID and role.
  2. requireAuth middleware reads Authorization: Bearer <token> on protected routes.
  3. It calls jwt.verify. A missing, changed or expired token gets 401.
  4. A valid token puts the claims on req.user, then calls next().
  • Log in, copy the token, open + Request headers and type Authorization: Bearer <token>. Then send GET /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/refresh answers 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 staff may 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/41 to /orders/42 shows 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

MistakeWrong ideaCorrect ideaWhat goes wrong
Secrets in the JWT payloadJWTs are encryptedAnyone can read the payloadPrivate data leaks
jwt.decode instead of jwt.verifyDecoding checks itOnly verify checks the signatureForged tokens are accepted
Long-lived access tokensFewer logins is friendlierShort access, refresh for long sessionsA stolen token works for weeks
No ownership checkLogged-in users only read their own dataCheck order.studentId === req.user.subAnyone reads anyone's orders
Trusting a user ID from the bodyThe client sends its own IDUse req.user.sub from the tokenUsers act as other users
Weak or hard-coded secretAny text worksA long random secret from the environmentAnyone can sign valid tokens
No algorithms option in verifyThe default is safe enoughPin ["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.verify on 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.