23 Authentication & JWT Interview Questions (2026)

·24 min read
By ·Updated
authenticationjwtinterview-questionsnodejsoauthsecuritybackend

"How would you implement authentication?" is a common backend interview question. A strong answer starts with the threat model, client type, trust boundaries, recovery requirements and authorization model rather than defaulting to JWT or a particular Node.js package.

This guide covers the essential authentication questions you'll face in Node.js backend interviews, from basic concepts to advanced security patterns.

Table of Contents

  1. Authentication Fundamentals Questions
  2. Session-Based Authentication Questions
  3. JWT Authentication Questions
  4. OAuth 2.0 Questions
  5. Password Security Questions
  6. Multi-Factor Authentication Questions
  7. Security Best Practices Questions

Authentication Fundamentals Questions

These foundational questions test your understanding of core authentication concepts.

What is the difference between authentication and authorization?

Authentication establishes or verifies a principal. Authorization evaluates that subject, action, resource, tenant and relevant state. Public access can be authorized anonymously, so authentication does not literally precede every authorization decision. For protected HTTP operations, 401 means valid authentication credentials are missing or invalid; 403 means the server understood the request but refuses it, commonly because an authenticated principal lacks permission.

flowchart TB
    subgraph authn["Authentication: WHO are you?"]
        A1["Verify identity"]
        A2["Check credentials<br/>(password, token, biometric)"]
        A3["Result: This is user #123"]
    end
 
    subgraph authz["Authorization: WHAT can you do?"]
        B1["Check permissions"]
        B2["Evaluate roles/policies"]
        B3["Result: User #123 can<br/>edit this resource"]
    end
 
    authn --> authz
// Express middleware example
const authenticate = async (req, res, next) => {
  // WHO is this?
  const token = req.cookies.token;
  const user = await verifyToken(token);
  if (!user) return res.status(401).json({ error: 'Not authenticated' });
  req.user = user;
  next();
};
 
const authorize = (action, loadResource) => async (req, res, next) => {
  const resource = await loadResource(req.params.id);
  // Evaluate action, resource, tenant, ownership and current policy—not role alone.
  if (!await policy.allows(req.user, action, resource)) {
    return res.status(403).json({ error: 'Not authorized' });
  }
  req.resource = resource;
  next();
};
 
// Usage
app.delete('/users/:id',
  authenticate,           // Must be logged in
  authorize('delete', loadUserResource),
  deleteUser
);

How would you implement authentication in a Node.js application?

Start by deciding whether the application should own credentials at all. A maintained identity provider can supply passkeys/MFA, recovery, federation and security operations that are easy to implement badly. A same-origin browser app often benefits from an opaque, rotating session cookie or a backend-for-frontend (BFF). Native and third-party clients commonly use OAuth authorization code with PKCE; user login additionally needs OpenID Connect. A service-to-service API may accept an opaque access token or a tightly validated JWT.

A maintained Node.js framework integration can help, but no middleware automatically supplies safe recovery, account linking, protocol validation, session operations and authorization. Verify its security model, maintenance and configuration rather than treating a library or managed provider as proof of correctness.

When should you use sessions versus JWT?

Sessions normally send an opaque identifier while the server retains authoritative state. A JWT is a token format, not an authentication architecture: its claims may be signed (JWS), encrypted (JWE), both through nesting, or even unsecured where a profile permits it. Production security profiles require cryptographic protection.

Choose according to trust boundaries and lifecycle. Shared session storage can scale horizontally; a JWT deployment often still needs keys, user status, consent, revocation or replay state. Cookies and tokens can both cross domains when deliberately scoped and transported, so neither row is an architectural law.

RequirementSessionJWT
Server-rendered web appYesPossible
Native/mobile APIOAuth session or opaque tokenProfiled access token
Independently operated recipientsPossible, needs lookupUseful with strict validation
Immediate revocationCentral lookup is directIntrospection, denylist or short lifetime
Horizontal scalingShared/partitioned storeShared keys plus any lifecycle state

Session-Based Authentication Questions

Sessions are the traditional approach, still widely used for server-rendered web apps.

How does session-based authentication work?

After login, the server creates a high-entropy session identifier and stores authoritative state in a server-side store. The browser receives only the identifier in a cookie and sends it on matching requests. The application resolves it, checks expiry and revocation, then loads current authorization context.

Central state makes revocation direct, provided every node consults coherent state and no downstream credential outlives the session.

1. User submits credentials
2. Server validates, rotates the pre-login ID, creates server-side state
3. Server sends a high-entropy ID in a protected cookie
4. Browser sends cookie with every request
5. Server looks up session, attaches user to request

How do you implement session authentication in Express?

express-session can manage the cookie and lifecycle, but production readiness also depends on a supported shared store, TLS/proxy configuration, error handling, expiry alignment, regeneration, revocation and CSRF protection. HttpOnly blocks direct JavaScript reads and Secure limits transport to HTTPS. SameSite is useful defense in depth, not a universal replacement for CSRF tokens or strict Origin validation.

const express = require('express');
const session = require('express-session');
 
app.use(session({
  store: productionSessionStore, // Connected and monitored elsewhere
  name: '__Host-session',
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true,
    httpOnly: true,
    sameSite: 'lax',
    path: '/',
    maxAge: sessionLifetimeMs
  }
}));
 
// Add maintained CSRF protection for state-changing cookie-authenticated routes.
 
// Login
app.post('/login', async (req, res, next) => {
  const { email, password } = req.body;
 
  // Includes a precomputed dummy hash path so unknown accounts do comparable work.
  const user = await verifyPasswordWithoutEnumeration({ email, password });
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });
 
  // Regenerate session to prevent fixation
  req.session.regenerate((err) => {
    if (err) return next(err);
    req.session.userId = user.id;
    req.session.save((saveError) => {
      if (saveError) return next(saveError);
      res.json({ message: 'Logged in' });
    });
  });
});
 
// Logout
app.post('/logout', (req, res, next) => {
  req.session.destroy((err) => {
    if (err) return next(err);
    res.clearCookie('__Host-session', { secure: true, httpOnly: true, sameSite: 'lax', path: '/' });
    res.json({ message: 'Logged out' });
  });
});
 
// Auth middleware
const requireAuth = (req, res, next) => {
  if (!req.session.userId) {
    return res.status(401).json({ error: 'Not authenticated' });
  }
  next();
};

What are the pros and cons of session-based authentication?

Opaque sessions keep claims and lifecycle decisions server-side, make per-session revocation straightforward and send a relatively small credential. Their store must be available, protected, expired and scaled. Cookie transport is automatic, so state-changing requests need CSRF defenses. Cross-origin deployment is possible but adds deliberate cookie, CORS, CSRF and browser-policy design.

ProsCons
Immediate revocation (delete session)Requires server-side storage
Small opaque browser credentialStore availability and cleanup matter
Server controls authoritative stateCross-origin flows add browser constraints
Straightforward lifecycle modelCookie transport needs CSRF protection

JWT Authentication Questions

JWT is a standardized claims format used by some identity and authorization profiles. It is not automatically the right format for an API or microservice.

What is a JWT and what does its structure look like?

A JWT is a compact claims format defined by RFC 7519. The familiar three-part form is a JWS carrying a protected header, claim set and signature. Base64url encoding is not encryption: anyone holding such a token can normally read its claims. JWT can also be encrypted as JWE or nested, while an unsecured JWT exists in the base standard but should not be accepted by security profiles.

Signature verification proves integrity under a selected key; it does not by itself prove that the token is acceptable here. The recipient must pin allowed algorithms and trusted key sources, then validate issuer, audience, token type/use, lifetime and application claims. It may still consult state for subject status, authorization, consent or revocation.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

[Header].[Payload].[Signature]
// Header (algorithm + type)
{
  "alg": "HS256",
  "typ": "JWT"
}
 
// Payload (claims)
{
  "sub": "user123",        // Subject (user ID)
  "name": "John Doe",
  "role": "admin",
  "iat": 1516239022,       // Issued at
  "exp": 1516242622        // Expiration
}
 
// Conceptual JWS signature input (the concrete algorithm is profile-specific)
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

How do you implement JWT authentication with refresh tokens?

Access-token and refresh-token lifetimes are risk decisions, not universal 15-minute and 7-day constants. A refresh token is optional and represents a valuable grant. RFC 9700 requires public clients that receive one to use sender constraint or rotation with reuse detection; the server must retain the token-family relationship and revoke the active family when an invalidated token is replayed.

The example below deliberately uses an opaque refresh token. rotateRefreshTokenAtomically must hash tokens at rest, verify client/grant binding and expiry, consume the presented token exactly once, create its successor and revoke the family on reuse. Merely signing a fresh JWT with the same claims is not rotation.

const jwt = require('jsonwebtoken');
const crypto = require('node:crypto');
 
const issueAccessToken = (subject) => jwt.sign(
  { sub: subject, token_use: 'access' },
  accessTokenPrivateKey,
  {
    algorithm: 'RS256',
    issuer: 'https://identity.example.com',
    audience: 'https://api.example.com',
    expiresIn: accessTokenLifetime,
    jwtid: crypto.randomUUID(),
    header: { typ: 'at+jwt', kid: activeKeyId }
  }
);
 
app.post('/refresh', verifyCsrf, async (req, res, next) => {
  try {
    const presented = req.cookies['__Host-refresh'];
    if (!presented) return res.status(401).json({ error: 'Invalid grant' });
 
    // One transaction: consume once, detect replay, revoke family on reuse,
    // and return a random successor whose hash (not plaintext) is stored.
    const rotated = await rotateRefreshTokenAtomically(presented, {
      clientId: expectedClientId
    });
 
    res.cookie('__Host-refresh', rotated.plaintextToken, {
      httpOnly: true,
      secure: true,
      sameSite: 'lax',
      path: '/',
      maxAge: rotated.remainingLifetimeMs
    });
    res.json({ accessToken: issueAccessToken(rotated.subject) });
  } catch (error) {
    next(error); // Map expected grant failures to one generic 401 response.
  }
});
 
// Resource-server validation: the public key comes from trusted issuer metadata.
const authenticateJWT = (req, res, next) => {
  const match = req.headers.authorization?.match(/^Bearer ([^\s]+)$/);
  if (!match) return res.status(401).json({ error: 'Invalid token' });
 
  try {
    const verified = jwt.verify(match[1], trustedAccessTokenPublicKey, {
      algorithms: ['RS256'],
      issuer: 'https://identity.example.com',
      audience: 'https://api.example.com',
      complete: true
    });
    if (verified.header.typ !== 'at+jwt' || verified.payload.token_use !== 'access') {
      throw new Error('Wrong token type');
    }
    req.user = verified.payload;
    next();
  } catch {
    return res.status(401).json({ error: 'Invalid token' });
  }
};

How do you handle logout with JWT?

JWT does not prohibit revocation; offline validation simply cannot observe changed server state. On logout, revoke the current session or authorization grant and refresh-token family, then clear the browser credential with matching cookie attributes. Decide separately whether access tokens may remain valid for their bounded lifetime or whether sensitive APIs must use introspection, a denylist keyed by jti, sender-constrained tokens or another live-state check. “Logout everywhere” and “logout this device” need different identifiers.

app.post('/logout', verifyCsrf, async (req, res, next) => {
  await revokeCurrentGrant(req.cookies['__Host-refresh']);
  res.clearCookie('__Host-refresh', {
    secure: true, httpOnly: true, sameSite: 'lax', path: '/'
  });
  res.json({ message: 'Logged out' });
});

What are common JWT security pitfalls?

Common failures include accepting an attacker-selected algorithm or key, validating a signature but not iss, aud, time and token type, sharing one validation policy across access tokens and ID tokens, trusting arbitrary jku/jwk/kid values, leaking bearer tokens, putting sensitive data in readable claims and operating without key rotation. RFC 8725 requires mutually exclusive validation rules for different JWT kinds.

// BAD: Storing in localStorage (XSS vulnerable)
localStorage.setItem('token', accessToken);
 
// BAD: signature-only validation with a caller-selected key or algorithm
jwt.verify(token, keyFromTokenHeader);
 
// BETTER: separate, explicit policy for this access-token profile
const verified = jwt.verify(token, keyFromTrustedIssuerMetadata, {
  algorithms: ['RS256'],
  issuer: expectedIssuer,
  audience: expectedApiAudience,
  complete: true
});
if (verified.header.typ !== 'at+jwt' || verified.payload.token_use !== 'access') {
  throw new Error('Wrong token type');
}

What are the pros and cons of JWT authentication?

Signed JWTs let a recipient validate an issuer's protected claims without a per-request lookup and can interoperate across separately operated services. Costs include key distribution and rotation, larger credentials, claim staleness, strict profile validation and a deliberate replay/revocation model. Sensitive claims remain readable unless encryption is used, and encryption still does not make a stolen bearer token safe.

ProsCons
Offline integrity validationClaims can become stale
Standard claims and profilesValidation policy is easy to misconfigure
Useful across trust boundariesKey lifecycle and replay need design
Avoids some request-time lookupsUsually larger; payload is normally readable

What happens if a JWT secret is compromised?

A compromised signing key is an incident: stop issuance, identify affected token types and audiences, activate a prepared replacement, remove trust in the old key as quickly as the compatibility window permits, revoke grants/sessions where needed, investigate use and notify according to the response plan. Tokens signed with the removed key fail; not every user must necessarily reauthenticate if a separate session or refresh grant remains trustworthy.

Asymmetric signing lets resource servers receive only public verification keys, reducing signing authority spread, but the private key and publication pipeline still require hardened storage, access control, rotation, kid handling and monitoring. Separate keys and mutually exclusive validation rules can limit blast radius only when their operational trust boundaries are genuinely separate.


OAuth 2.0 Questions

OAuth 2.0 delegates access to protected resources. OpenID Connect (OIDC), not bare OAuth, defines federated user authentication.

How does OAuth 2.0 work?

In the authorization code flow, a client creates a transaction-bound state and PKCE verifier, redirects the browser to a trusted authorization endpoint and receives a one-time code at an exactly registered redirect URI. It validates the response and exchanges the code plus verifier at the token endpoint. The access token is for the resource server; an OIDC ID token is evidence for the client and must not be sent as an API access token.

RFC 9700 requires PKCE for public clients and recommends it for confidential clients. Implementations also need issuer/mix-up protection, exact redirect matching, least-privilege scopes, trusted metadata, TLS and secure token handling. Consent may be part of a flow but is not guaranteed on every request.

sequenceDiagram
    participant U as User Browser
    participant A as Your App (Backend)
    participant G as Google Auth Server
 
    U->>A: 1. Click "Login with Google"
    A->>G: 2. Authorization request + state + PKCE challenge
    G->>U: 3. Authenticate and, when needed, ask consent
    U->>A: 4. Exact redirect URI + code + state
    A->>A: 5. Validate transaction and issuer
    A->>G: 6. Code + PKCE verifier
    G->>A: 7. Access token + OIDC ID token
    A->>A: 8. Validate ID token and bind provider subject
    A->>U: 9. Rotate/create local session

How do you implement OAuth with Passport.js?

Passport.js strategies can integrate a provider, but package choice does not remove protocol verification duties. Confirm that the maintained strategy/library supports state, PKCE where required, exact redirect URIs, issuer and OIDC nonce/ID-token validation. Bind the account to the stable (issuer, subject) pair; do not merge accounts solely because an untrusted or unverified email string matches. The abbreviated example omits production error pages, account-linking confirmation and token lifecycle by design.

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
 
passport.use(new GoogleStrategy({
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: 'https://app.example.com/auth/google/callback',
    state: true
  },
  async (accessToken, refreshToken, profile, done) => {
    try {
      // Find or create user
      let user = await User.findOne({ googleId: profile.id });
 
      if (!user) {
        user = await User.create({
          googleId: profile.id, // Provider subject; store issuer alongside it.
          email: verifiedProviderEmail(profile),
          name: profile.displayName,
          avatar: profile.photos[0]?.value
        });
      }
 
      return done(null, user);
    } catch (error) {
      return done(error, null);
    }
  }
));
 
// Serialize user to session
passport.serializeUser((user, done) => {
  done(null, user.id);
});
 
passport.deserializeUser(async (id, done) => {
  const user = await User.findById(id);
  done(null, user);
});
 
// Routes
app.get('/auth/google',
  passport.authenticate('google', { scope: ['profile', 'email'] })
);
 
app.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/login' }),
  (req, res) => {
    res.redirect('/dashboard');
  }
);

What are the different OAuth 2.0 grant types?

Choose a grant by who acts, what credentials the client can protect and which current profile applies. Authorization Code is the interactive redirect flow; public clients must use PKCE, and confidential clients should use it too. Client Credentials is for a confidential client acting on its own behalf, not impersonating a user. Device Authorization Grant covers input-constrained devices when supported.

RFC 9700 says authorization servers must not use the implicit grant and must not use the Resource Owner Password Credentials grant. Do not describe these as merely lower-security options for new systems.

GrantAppropriate contextKey requirement
Authorization Code + PKCEBrowser, native and web clientsBind code to transaction and verifier
Client CredentialsConfidential service acting as itselfAuthenticate client; narrow audience/scope
Device AuthorizationInput-constrained deviceUser code verification and polling controls
ImplicitLegacy onlyDo not enable for new systems
Resource Owner PasswordLegacy onlyDo not enable; migrate away

Password Security Questions

Password handling is heavily tested in interviews.

How do you securely store passwords?

Store passwords with a salted, adaptive password-hashing function. OWASP currently prefers Argon2id; use scrypt when it is unavailable, bcrypt mainly for legacy compatibility, or the approved PBKDF2 profile where FIPS requirements apply. Calibrate memory, CPU and parallelism on production-class hardware under expected concurrency, record the algorithm and parameters with each hash, and rehash after successful login when policy changes. A salt prevents identical hashes and precomputed tables; it does not stop offline guessing. A separately protected pepper can add defense in depth.

Bcrypt has a 72-byte input limit in common implementations, so silent truncation must be prevented. There is no universal rounds value or response-time target: benchmark it, protect the service from resource exhaustion and revisit parameters.

const argon2 = require('argon2');
 
const hashPassword = (password) => argon2.hash(password, {
  type: argon2.argon2id,
  ...benchmarkedProductionParameters
});
 
const verifyPassword = (password, encodedHash) =>
  argon2.verify(encodedHash, password);

How do you implement password validation?

NIST SP 800-63B-4 requires at least 15 characters when a password is the only factor and permits a minimum of eight when it is used only within MFA. It says to allow a maximum of at least 64 characters, accept spaces and Unicode, reject known common/expected/compromised values, allow password managers and paste, and not impose character-composition rules or periodic rotation without evidence of compromise. Normalize Unicode consistently before hashing and document any sensible resource-exhaustion ceiling.

const validatePassword = async (password, { isSingleFactor }) => {
  const errors = [];
  const normalized = password.normalize('NFC');
  const minimum = isSingleFactor ? 15 : 8;
 
  if ([...normalized].length < minimum) errors.push(`Use at least ${minimum} characters`);
  if (Buffer.byteLength(normalized, 'utf8') > serviceSafetyLimitBytes) errors.push('Password is too long');
  if (await compromisedPasswordBlocklist.has(normalized)) errors.push('Choose a password not found in common or breached-password data');
 
  return errors;
};

How do you implement secure password reset?

Password reset is an authentication path and needs the same threat model as login. Return the same outward response and similar timing for existing and unknown accounts. Generate a high-entropy, single-use token, store only a keyed hash, bind it to the account and purpose, set a risk-based short expiry, rate-limit requests and build links from a configured trusted origin rather than the request Host header. Consume it atomically, invalidate competing reset tokens and decide which sessions/grants to revoke. Notify the user after a change without sending the new password.

const crypto = require('crypto');
 
const generateResetToken = async (user) => {
  const token = crypto.randomBytes(32).toString('hex');
  const hashedToken = keyedTokenHash(token);
 
  user.resetToken = hashedToken;
  user.resetTokenExpiry = Date.now() + resetLifetimeMs;
  await user.save();
 
  return token; // Send this to user, store hashed version
};
 
const verifyResetToken = async (token) => {
  // One transaction consumes the token once and clears every competing token.
  return consumeValidResetTokenAtomically(keyedTokenHash(token), Date.now());
};

Multi-Factor Authentication Questions

MFA adds critical security for sensitive applications.

How do you implement TOTP-based MFA?

TOTP is a shared-secret possession factor defined by RFC 6238. It is widely supported but remains phishable; for higher-risk systems, prefer phishing-resistant WebAuthn/passkeys when the threat model and recovery design support them. Require recent authentication before enrollment, create an expiring pending enrollment, present the secret once over a protected channel, encrypt it at rest, confirm a code before activation and issue separately protected recovery codes. Rate-limit verification, keep clock tolerance narrow and reject replay of an already accepted time step.

app.post('/mfa/setup', requireRecentAuthentication, async (req, res) => {
  const enrollment = await createPendingTotpEnrollment(req.user.id, {
    expiresInMs: enrollmentLifetimeMs,
    encryptSecretWith: mfaEncryptionKey
  });
 
  // The QR encodes the secret; show it only in this protected enrollment flow.
  res.json({ qrCode: await renderQr(enrollment.otpauthUri) });
});
 
app.post('/mfa/verify', requireRecentAuthentication, mfaRateLimit, async (req, res) => {
  const result = await confirmPendingTotpAtomically({
    userId: req.user.id,
    code: req.body.code,
    rejectPreviouslyAcceptedStep: true
  });
  if (!result.ok) return res.status(400).json({ error: 'Invalid or expired code' });
  res.json({ recoveryCodes: result.oneTimeRecoveryCodes });
});

How do you handle login with MFA?

After the first factor, create a short-lived, single-purpose pre-authentication transaction bound to the browser, user, requested action and attempt counter. Do not issue a normal session or access/refresh tokens until the required factors pass. Rate-limit by account and broader risk signals, prevent TOTP replay, offer controlled recovery, rotate the final session ID and log meaningful events. Passkeys may satisfy both possession and user verification without a separate password, depending on assurance requirements.

app.post('/login/password', loginRateLimit, async (req, res) => {
  const user = await verifyPasswordWithoutEnumeration(req.body);
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });
  const transaction = await createPreAuthTransaction(user.id, requiredFactors(user));
  res.json({ next: transaction.publicChallenge });
});
 
app.post('/login/totp', mfaRateLimit, async (req, res) => {
  const transaction = await consumeValidPreAuthStep(req.body.challenge, req.body.code);
  if (!transaction.complete) return res.status(401).json({ error: 'Invalid challenge' });
  await establishRotatedSession(req, transaction.userId);
  res.sendStatus(204);
});

Security Best Practices Questions

These questions test your overall security awareness.

How do you implement rate limiting for authentication?

Throttling must address targeted guessing, credential stuffing and distributed password spraying without giving attackers an easy account-lockout denial of service. Combine account-level controls with trustworthy network/device risk signals, progressive delay or risk-based challenges, breached-password detection, MFA and monitoring. Normalize identifiers, use a distributed atomic store, do not trust unsanitized forwarding headers, keep outward login responses generic and let legitimate users recover. Tune thresholds from traffic and threat data rather than copying a universal “five attempts” rule.

app.post('/login', async (req, res) => {
  const accountKey = keyedHash(normalizeIdentifier(req.body.email));
  const networkKey = trustedClientNetwork(req); // Proxy chain configured explicitly.
  const decision = await distributedAuthLimiter.checkAndIncrement({ accountKey, networkKey });
 
  if (!decision.allowed) {
    await constantShapeDelay();
    return res.status(429).json({ error: 'Unable to sign in' });
  }
 
  // Perform equal-shaped account/password verification and record outcome/risk.
  return completeLoginWithoutEnumeration(req, res);
});

How would you implement "Remember Me" functionality?

“Remember me” means choosing a persistent login lifecycle, not necessarily a JWT refresh token. Record the user's informed choice, use a rotating per-device session or grant with idle and absolute expiry, expose active devices, support individual/all-device revocation and require reauthentication for sensitive changes. Do not extend a rotated credential beyond its authorized maximum lifetime. Pick lifetimes from risk, regulation and recovery capability rather than fixed 7/30-day folklore.

How would you design authentication for a banking app?

Start from the bank's transactions, regulatory obligations, fraud model and assurance targets. Prefer phishing-resistant authenticators such as passkeys/security keys where deployable; provide hardened enrollment, recovery and support channels. Use risk-aware session idle/absolute limits, rotation, device/session management and step-up or transaction authorization that clearly binds payee and amount. Apply layered abuse controls, signed/auditable security events, anomaly detection and rehearsed key/session revocation.

IP addresses and device fingerprints are noisy, privacy-sensitive signals, not hard session bindings that strand mobile users or become authorization. Use TLS correctly; mobile certificate pinning is an operational trade-off, not a blanket requirement, because broken rotation and recovery can create availability and update risk. Independent review, threat modeling and tested incident/fraud response matter more than copied timeout constants.


Quick Reference

ConceptImplementationSecurity Note
Password storageCalibrated Argon2id; legacy/FIPS alternatives as requiredSalt; optional separate pepper; rehash plan
Browser sessionHigh-entropy opaque ID in protected cookieRotate on privilege change; CSRF defense
Access tokenProfiled opaque token or strictly validated JWTNarrow audience/scope/lifetime; replay model
Refresh tokenOptional, protected and grant-boundSender constrain or rotate with reuse detection
Password resetHigh-entropy, keyed-hash-at-rest, atomic single useGeneric response; short risk-based expiry
MFAPrefer phishing-resistant WebAuthn; TOTP as supported fallbackSecure enrollment and recovery

Frequently Asked Questions

What is the difference between authentication and authorization?

Authentication establishes or verifies an identity; authorization decides whether a subject may perform an action on a resource in the current context. A system can authorize anonymous access, so authentication is not literally required for every decision. For protected operations, return 401 when valid authentication credentials are missing or invalid and 403 when the authenticated principal lacks permission.

When should you use JWT vs session-based authentication?

Do not choose JWT merely because an API, SPA, mobile client or microservice is involved. An opaque session cookie or browser BFF is often simpler when the application needs central revocation and tokens need not cross trust boundaries. Use JWT when independently operated recipients need a standardized, signed claim set and can enforce issuer, audience, type, algorithm, lifetime and key rules. Either design can scale and either may require server-side state.

What is a refresh token and why is it needed?

A refresh token represents an authorization grant and lets an eligible client obtain access tokens without repeating user authentication. It is optional and more valuable than a single short-lived access token, so protect it in transit and storage, bind it to the client and grant, limit its lifetime and scope, and support revocation. Public clients need sender-constrained refresh tokens or rotation with reuse detection; simply minting another token is not rotation.

How does OAuth 2.0 work?

OAuth 2.0 delegates authorization; it does not by itself authenticate a user. In a modern interactive flow, the client sends the browser to an authorization server, validates the returned transaction, and exchanges a one-time authorization code using PKCE for an access token intended for a resource server. OpenID Connect adds authentication and an ID token for the client. Client Credentials represents a client acting on its own behalf, not a user login.

Where should you store JWT tokens in a browser?

Prefer an HttpOnly, Secure, narrowly scoped session cookie and a backend-for-frontend that keeps OAuth tokens out of browser JavaScript. HttpOnly limits token exfiltration but an XSS payload can still act as the user, while automatic cookies require CSRF defenses such as SameSite plus a token or strict origin checks where appropriate. Do not put authentication credentials in localStorage or sessionStorage; if JavaScript must hold an access token, keep exposure short and design for compromise.

What are common authentication security vulnerabilities?

Common failures include credential stuffing and account enumeration, weak recovery and MFA enrollment, session fixation, missing CSRF defenses, insecure browser token storage, overbroad tokens, and JWT validation that checks a signature but not issuer, audience, type, algorithm or lifetime. Use maintained identity components, TLS, adaptive password hashing, layered throttling, session rotation and revocation, phishing-resistant MFA where warranted, safe recovery, audit events and tested key rotation.

Sources


Ready to ace your interview?

Get 550+ interview questions with detailed answers in our comprehensive PDF guides.

View PDF Guides