Authentication Mechanisms Every Developer Should Know
Have you ever found authentication confusing? Sessions, JWTs, OAuth, API keys, SSO, passkeys: the list keeps growing, and it’s not always clear which one to use or why.
In this article, we’ll go through the authentication mechanisms the industry relies on today, look at how each one works under the hood and when to use which and their pros and cons.
What is Authentication?
Authentication is how a system checks “Are you really who you say you are?”
Every time a user logs in or a service calls an API, the system needs proof of identity before it can trust the request.
The proof could be a password, a token, a certificate, or a passkey. If it’s valid, the request goes through. If not, it’s rejected with a 401 Unauthorized.
Basic Authentication
This is the most simple authentication mechanism. It involves sending username and password i.e the user credentials with each request to the server.
The server then verifies these credentials before granting access to the requested resource. Though Basic Authentication is easy to understand and implement , but it raises security concerns due to the credentials in plain text.
How it works:
- First request: The client asks for a protected resource, such as
GET /orders, without any credentials. - Server challenge: The server replies with 401 Unauthorized and a
WWW-Authenticate: Basicheader. This tells the client it needs to log in using Basic Auth. - Build the credentials: The client joins the username and password with a colon,
alice:secret123, and Base64-encodes the result:YWxpY2U6c2VjcmV0MTIz. - Send the header: The client sends the request again, this time with the header:
Authorization: Basic YWxpY2U6c2VjcmV0MTIz
This works with any HTTP method: GET, POST, PUT, and so on. - Decode: The server reads the header and decodes the Base64 string to get the username and password back.
- Verify: The server checks the username and password against its user store.
- Result:
- Valid: the server returns 200 OK with the requested data.
- Invalid: the server returns 401 Unauthorized again. The client usually asks the user for credentials again and goes back to step 3.
⚠️ Base64 is encoding, not encryption. Anyone who intercepts the header can decode it in a second. Always use Basic Auth over HTTPS.
When to use it: internal tools, quick prototypes, or simple service-to-service calls over HTTPS.
When to avoid it: public-facing apps. The password goes out with every request, and there’s no built-in logout or expiry.
Digest Authentication
Digest Authentication was designed to fix Basic Auth’s biggest problem: sending the password over the network. With Digest, the password itself is never sent. The client sends a hash that proves it knows the password.
How it works
- First request: The client asks for
GET /userswithout any credentials. - Server challenge: The server replies with 401 Unauthorized and a
WWW-Authenticate: Digestheader. This header contains a nonce: a random value that's valid for a short time only. - Build the hash: The client mixes four things together and hashes them with MD5:
- the password
- the nonce from the server
- a counter (
nc), which starts at 1 and goes up with every request - the request itself (method + URL, for example
GET /users)
4. Retry: The client sends the request again with an Authorization: Digest ... header. It contains the username, the counter and the hash, but not the password.
5. Recompute: The server already knows the user’s password hash. It mixes the same four values and computes the hash on its side.
6. Compare: If both hashes match, the client must know the password.
7. Result: If they match, the server returns 200 OK with the data. If not, it returns 401 Unauthorized.
Why a copied hash can’t be reused
Each hash is tied to one nonce, one counter value and one URL. The server also remembers which counter values it has already accepted.
- The attacker sends the same request again: that counter value has already been used, so the server rejects it.
- The attacker raises the counter: the hash no longer matches, and they can’t compute a new one without the password.
- The attacker tries later: the nonce has expired, so the server rejects it.
Simple example: Alice sends
nc=1with hashX1and gets in. An attacker copiesnc=1, X1, but the server says "counter 1 is already used." The attacker changes it tonc=2, but the correct hash fornc=2isY7, and they can't makeY7without Alice's password.
Why it’s rarely used today
- MD5 is weak. A captured hash can be cracked offline to recover weak passwords.
- The server stores password-equivalent hashes. If its database leaks, an attacker can log in as any user.
- It still needs HTTPS. Without it, an attacker in the middle can tamper with requests.
- Better options exist. HTTPS combined with token-based authentication solves the same problem more cleanly.
Session-Based Authentication (Cookie-Based)
With Basic Auth, the client sends its password on every request. Session authentication fixes that: the user logs in once, and the server remembers them.
After a successful login, the server creates a session and gives the browser a random session ID. From then on, the browser sends that ID with each request instead of the password.
How It Works
① Login: the session is created
- The user sends a username and password to
POST /login. - The server checks the password.
- If it’s correct, the server creates a session with a random ID, such as
sess_8f3k. It saves that ID along with the user's data (userId: 42) in a session store, which can be memory, Redis, or a database. - The server sends the ID back to the browser in a cookie:
Set-Cookie: SESSION_ID=sess_8f3k; HttpOnly; Secure
② Every request after login
- The browser automatically adds the cookie to every request to that site. The frontend doesn’t need any extra code for this.
- The server reads the session ID from the cookie and looks it up in the session store.
- The store returns who the user is.
- The server handles the request for that user.
③ Logout: the session ends
- The user logs out.
- The server deletes the session from the store.
- The server tells the browser to clear the cookie.
A session can also end on its own after a period of inactivity (a session timeout). Either way, the old session ID stops working. If the browser sends it again, the lookup fails and the server returns 401 Unauthorized.
Pros
- The password is sent only once, at login, not with every request.
- Easy to revoke: delete the session and the user is logged out immediately.
- Simple for browsers: cookies are sent automatically.
Cons
- The server has to store state. Every request needs a lookup, and with multiple servers you need a shared session store like Redis.
- Session hijacking: if someone steals the session ID, they can act as the user. Use
HttpOnlyandSecurecookies, and create a new session ID after login. - CSRF risk: the browser sends cookies automatically, even on requests triggered by other sites. Use
SameSitecookies or CSRF tokens. - Not a good fit for mobile apps or APIs. Cookies work best in browsers, and this leads naturally into tokens (JWT), the next section.
API Key Authentication
An API key is a long random string that identifies the calling application. You’ve probably used one already: you sign up for a service, open the dashboard, click “Generate API key”, and paste it into your code.
How it works:
- Generate: The service creates a unique key for the client, such as
sk_live_9f8a7b2c…, and shows it once. - Send: The client includes the key in every request, usually in a header:
X-API-Key: sk_live_9f8a7b2c…orAuthorization: Bearer sk_live_9f8a7b2c… - Look up: The server hashes the key and looks it up in its database to find who owns it and what it’s allowed to do(its scopes).
- Result:
- Valid key: 200 OK with the data.
- Missing, unknown or revoked key: 401 Unauthorized.
API key vs JWT: An API key looks like a token, but it’s just a random string with no information inside it. The server can’t tell who owns it without a database lookup. A JWT carries that information inside the token itself, as we’ll see shortly.
Watch out for:
- Leaks: anyone who has the key can use it. Never commit keys to Git or put them in frontend code.
- No built-in expiry: keys live until someone revokes them, so rotate them regularly.
- Store only a hash of each key, the same way you’d store a password.
Best for: server-to-server calls, public developer APIs, and scripts. Not suited for identifying end users.
Bearer Authentication and JWT
With sessions, the server has to remember every logged-in user. Token-based authentication flips that around: the client carries the proof with it.
Bearer is how you send it, JWT is what you send
A Bearer token simply means “whoever bears (holds) this token gets access.” It’s a way of sending a token in a header:
Authorization: Bearer <token>It doesn’t say anything about what the token looks like. That’s where JWT (JSON Web Token) comes in. JWT is the most common token format sent as a bearer token.
Inside JWT token:
A JWT has three parts, separated by dots:
- Header: the signing algorithm, for example
HS256. - Payload: the claims, which are facts about the user such as user ID (
sub), role, and expiry time (exp). - Signature: created with a secret key that only the server knows. If anyone changes even one character of the payload, the signature no longer matches.
⚠️ A JWT is signed, not encrypted. Anyone can decode and read the payload, so never put passwords or secrets in it.
How it works
① Login (once)
- The client sends its username and password to the Auth Server.
- The Auth Server checks them and creates a signed JWT with the user’s claims.
- It returns the JWT to the client.
② Every request after login
- The client sends the JWT in the
Authorization: Bearerheader. - The API Server checks the signature and expiry locally, with no database call.
- If the token is valid, the server returns the data. A bad signature or expired token gets 401 Unauthorized.
Why this matters
Before JWT, a token was just a random string, like an API key, and every request needed a database lookup. A JWT is self-contained: it carries its own claims and proves they haven’t been changed. This makes it stateless, so any server that has the key can verify it. That’s why it scales so well across microservices.
The trade-off: because the server doesn’t keep any state, you can’t easily revoke a JWT before it expires. If one is stolen, it works until its exp time. The solution is to keep access tokens short-lived, which leads to the next section.
Access Tokens and Refresh Tokens
A short-lived JWT is safer, but nobody wants to log in again every 15 minutes. Modern systems solve this with two tokens:
How it works:
① Login: The user logs in once and gets both tokens.
② Normal use: The client sends the access token with every API request.
③ Access token expires: After 15 minutes, the API returns 401 Unauthorized.
Get Veenarao’s stories in your inbox
Join Medium for free to get updates from this writer.
④ Silent refresh: The client sends the refresh token to /refresh and gets a new access token. It then retries the original request. The user never notices.
Why two tokens?
- If an access token is stolen, it’s only useful for a few minutes.
- The refresh token is sent much less often, and only to the auth server, so it’s harder to steal.
- To log a user out, or cut off a stolen device, revoke the refresh token. No new access tokens will be issued.
💡 Store the refresh token in an HttpOnly cookie, not localStorage. JavaScript can’t read HttpOnly cookies, which protects the token from XSS attacks. Note that only a server can set an HttpOnly cookie, so this needs a backend to do the token exchange. A single-page app that talks to the auth server directly gets the refresh token in JavaScript, and should rely on refresh token rotation and short lifetimes instead.
OAuth 2: Authorization, Not Authentication
This is the most misunderstood item on the list. OAuth 2 is an authorization framework. It answers:
“What is this app allowed to access on the user’s behalf?”
It does not answer “who is the user?”
Example: a photo-printing app wants to read your Google Drive files. You don’t want to give it your Google password, so you use OAuth 2 to grant limited, revocable access instead.
The four roles:
- User (resource owner): owns the data
- Your App (client): wants access to it
- Google Auth (authorization server): asks the user for consent and issues tokens
- Drive API (resource server): holds the data
How it works (Authorization Code flow):
- The user clicks “Connect Google Drive” in your app.
- Your app redirects them to Google, asking for a specific scope, for example
drive.readonly. - The user logs in to Google, not your app, and clicks Allow.
- Google redirects back to your app with a short-lived authorization code.
- Your app’s backend exchanges the code, plus its client secret, for an access token. This happens server to server, so the browser never sees the token.
- Google returns the access token.
- Your app calls the Drive API with
Authorization: Bearer <access token>. - The Drive API returns the user’s files.
The confusing part: at the end, your app has an access token and can read files. It’s easy to think the user has “logged in”. But the access token only proves that the app has permission. It doesn’t reliably tell your app who the user is. For that, you need OpenID Connect.
OpenID Connect (OIDC): Authentication on Top of OAuth 2
OpenID Connect adds an identity layer to OAuth 2. It uses the same redirects, the same code exchange, and the same flow, plus one extra item: an ID token.
This is what runs when you click “Sign in with Google”, “Sign in with GitHub” or “Sign in with Microsoft”.
How it works:
- The user clicks “Sign in with Google”.
- Your app redirects to Google with
scope=openid email profile. Theopenidscope is what turns this into OIDC. - The user logs in to Google and gives consent.
- Google redirects back with an authorization code.
- Your app exchanges the code for tokens.
- Google returns two tokens:
- Access token: for calling Google APIs (the OAuth 2 part)
- ID token: a JWT about the user (the OIDC part)
7. Your app checks the ID token’s signature and reads the claims: sub (a unique user ID), email, name.
8. The user is logged in. Your app usually creates its own session or tokens from here.
OAuth 2 → “This app may read your files.” OIDC → “This is Alice, and Google confirms it.”
That’s why most modern apps use OIDC for login. Your app never handles passwords, and the identity provider takes care of MFA, password resets and account security.
Single Sign-On (SSO) and Identity Protocols
SSO is a user experience, not an authentication method. It means: log in once, and get access to many apps.
You see it every day with Google. Sign in once, then open Gmail, Drive, YouTube and Calendar without logging in again. Companies do the same with Okta or Microsoft Entra ID for Slack, Jira, Salesforce and internal tools.
How it works:
① First app: log in once
- The user opens Gmail.
- Gmail sees they aren’t logged in and redirects them to the Identity Provider (IdP).
- The user enters their credentials. This is the only login screen they’ll see.
- The IdP creates a global session and sets an SSO cookie in the browser. It also sends a token proving the user’s identity to Gmail.
- The browser opens Gmail again, this time with the token.
- Gmail checks the token and shows the inbox.
② Second app: no login screen
- The user opens Drive.
- Drive also redirects to the IdP.
- The browser automatically sends the SSO cookie.
- The IdP sees an active session and immediately sends back a token for Drive, without showing a login screen.
- The browser opens Drive with that token.
- Drive shows the user’s files.
From the user’s point of view, they’re just “already logged in”.
Identity protocols: what SSO runs on
SSO needs a standard way for the IdP to tell an app “this user is Alice”. Two protocols do this:
Both are secure and widely used. New projects should usually choose OIDC. SAML is still common because many enterprises built their systems on it.
Quick Recap
Interview Questions
1. What’s the difference between authentication and authorization? Authentication checks who you are. Authorization decides what you’re allowed to do. Authentication always happens first.
2. When do you return 401 and when do you return 403? 401 Unauthorized means the server doesn’t know who you are: credentials are missing, invalid or expired. 403 Forbidden means the server knows who you are, but you don’t have permission.
3. Is Base64 encryption? No. Base64 is encoding, and anyone can reverse it. That’s why Basic Auth is only safe over HTTPS.
Sessions vs Tokens
4. Session-based vs token-based authentication: when would you pick each? Sessions are stateful: the server stores them, and revocation is easy and instant. They’re a good fit for traditional web apps. Tokens (JWT) are stateless: each one carries its own claims, so there’s no lookup and they scale easily across services. They’re a good fit for APIs, mobile apps and microservices.
5. How do you scale session-based auth across multiple servers? Use a shared session store such as Redis, so every server can read every session. The alternative is sticky sessions on the load balancer, but that breaks when a server goes down.
6. What are session hijacking and session fixation, and how do you prevent them? Hijacking: an attacker steals a valid session ID. Fixation: an attacker gets the victim to log in using a session ID the attacker already knows. To prevent both, use HttpOnly and Secure cookies, create a new session ID after login, and set timeouts.
7. What is CSRF, and why do cookies make it possible? Browsers send cookies automatically, even on requests triggered by another site. A malicious page can make the victim’s browser send a request that carries their session cookie. To defend against it, use SameSite=Lax/Strict cookies, CSRF tokens, or tokens sent in the Authorization header instead of cookies.
JWT
8. What’s inside a JWT? Is it encrypted? A JWT has a header, a payload (claims) and a signature. It’s signed, not encrypted, so anyone can read the payload. Never put secrets in it. (JWE is the encrypted variant, but it’s rarely used.)
9. How do you revoke a JWT before it expires? You can’t do it directly, because the server keeps no state. In practice:
- keep access tokens short-lived and revoke the refresh token;
- keep a denylist of revoked token IDs (
jti) in Redis, which brings back a little state; - store a token version per user and bump it to invalidate all of that user’s tokens.
10. HS256 vs RS256: what’s the difference? HS256 uses one shared secret to both sign and verify, so every service that verifies tokens could also create them. RS256 uses a private key to sign and a public key to verify. Only the auth server can issue tokens, while any service can verify them. RS256 is preferred in microservices, often with the public keys published through a JWKS endpoint.
11. What JWT validation mistakes lead to security holes? Accepting alg: none, not checking the signature, ignoring exp, not validating iss and aud, and algorithm confusion (an RS256 public key being accepted as an HS256 secret). Always pin the expected algorithm.
12. Where should you store tokens in the browser? Avoid localStorage, because any XSS script can read it.
- Browser-only SPA (Authorization Code + PKCE in the browser): the SPA keeps both tokens in memory. The refresh token can’t be HttpOnly, so use refresh token rotation, short lifetimes and, ideally, DPoP to bind tokens to the client.
- Token-mediating backend: a backend does the code exchange as a confidential client (client secret + PKCE), keeps the refresh token in an HttpOnly, Secure, SameSite cookie, and gives the SPA a short-lived access token to keep in memory.
- Backend-for-Frontend (BFF): the backend does the code exchange, keeps all tokens on the server and proxies API calls. The browser only holds a session cookie, so XSS can’t steal any token. This is the option the IETF’s OAuth 2.0 for Browser-Based Applications guidance recommends first for sensitive apps.
Access and Refresh Tokens
13. Why use two tokens instead of one long-lived token? To limit the damage if one is stolen. A stolen access token only works for minutes. The refresh token is sent rarely and only to the auth server, and it can be revoked.
14. What is refresh token rotation? Each time a refresh token is used, the server issues a new one and invalidates the old one. If an old refresh token is ever used again, that’s a sign of theft, and the server can revoke the whole token family.
OAuth 2 and OIDC
15. Is OAuth 2 an authentication protocol? No. OAuth 2 is an authorization framework: it gives an app limited access to a user’s resources. OpenID Connect adds authentication on top of it through the ID token.
16. What’s the difference between an access token and an ID token? The access token is for the API: it says what the app may access. The ID token is for the client app: it’s a JWT that says who the user is. Never send an ID token to an API as an access token.
17. Why does OAuth 2 use an authorization code instead of returning the token directly? The code passes through the browser, where it could be exposed. The actual token is fetched server to server using the client secret. Even if someone steals the code, they can’t exchange it without that secret.
18. What is PKCE, and why is it needed? Mobile apps and single-page apps can’t safely keep a client secret. PKCE solves this: the app generates a random code_verifier and sends its hash with the first request. Only the app that knows the original verifier can exchange the code. PKCE is now recommended for all clients.For SPAs, a Backend-for-Frontend (BFF) can do the exchange instead of the browser, so no tokens reach JavaScript (see Q12).
19. What are the OAuth 2 grant types, and which ones should you avoid?
- Authorization Code + PKCE: user login for web, mobile and SPAs.
- Client Credentials: service-to-service, with no user involved.
- Device Code: TVs and CLI tools.
- Avoid Implicit and Password (ROPC). Both are deprecated in OAuth 2.1.
SSO and Design
20. SAML vs OIDC: which would you choose? OIDC for new projects: it uses JSON and JWT, and works well with mobile apps and APIs. Use SAML when you need to integrate with enterprise or legacy systems that already use it.
21. How would you authenticate service-to-service calls in a microservices system? Use the OAuth 2 Client Credentials flow to get short-lived JWTs, or mTLS, where each service proves its identity with a certificate. Many systems use both, often through a service mesh. Avoid long-lived shared API keys.
22. Design an authentication system for a web app and a mobile app.
- OIDC login, either your own or through an IdP like Auth0, Cognito or Keycloak
- Authorization Code + PKCE
- short-lived RS256 access tokens
- rotating refresh tokens in secure storage
- JWT verification at the API gateway using JWKS
- Redis for revocation
- rate limiting on login
- MFA
- HTTPS everywhere
Thank you for reading this article. Please provide your valuable suggestions/ feedback.
- Clap and Share if you liked the content.
- 📰 Read more content on my Medium (on Java Developer interview questions)
- Reach out to me for resume and interview preparation . Contact me on topmate.
Please find my other helpful articles on Java Developer interview questions.
Multithreading and Concurrency Top Interview Questions-Part1
Multithreading and Concurrency Top Interview Questions-Part2
Following are some of the frequently asked Java 8 Interview Questions
Frequently Asked Java Programs
Dear Readers, these are the commonly asked Java programs to check your ability in writing the logic.
SpringBoot Interview Questions | Medium








