When building applications with OpenID Connect (OIDC), you will often see parameters such as state, nonce, and code_challenge in the authorization request.
At first, these parameters can look confusing because they are all random values. However, they solve different security problems.
Understanding the difference is important when implementing authentication with providers such as Microsoft Entra ID, Auth0, Okta, or other OIDC providers.
1. What is state?
The state parameter protects the application against CSRF (Cross-Site Request Forgery) attacks.
When the application redirects the user to the identity provider, it generates a random state value.
For example:
state = ABC123XYZ
The application stores this value temporarily, usually in a secure session or cookie.
The authorization request looks like:
https://login.example.com/authorize? client_id=my-client&response_type=code &redirect_uri=https://myapp.com/callback &state=ABC123XYZ
After authentication, the identity provider sends the user back:
https://myapp.com/callback?code=AUTH_CODE &state=ABC123XYZ
The application compares the returned state with the value it originally generated.
Expected: ABC123XYZ
Received: ABC123XYZ
If they don't match, the application should reject the request.
Think of state as:
"Is this callback really associated with the login request I started?"
2. What is nonce?
The nonce is mainly used to protect against replay attacks involving the ID token.
The application generates a random nonce:
nonce = XYZ789ABC
It sends the nonce in the authorization request.
The identity provider includes the nonce in the resulting ID token.
The application then validates that the nonce in the ID token matches the original value.
For example:
Authorization request:
nonce = XYZ789ABC
ID Token:
nonce = XYZ789ABC
If the values don't match, the application rejects the ID token.
Think of nonce as:
"Is this ID token associated with the authentication request I initiated?"
The nonce is especially important for OpenID Connect because OIDC uses ID tokens to represent the authenticated user's identity.
3. What is code_challenge?
code_challenge belongs to PKCE (Proof Key for Code Exchange).
PKCE protects the authorization code from being stolen and exchanged by another party.
The application first creates a secret random value called:
code_verifier
For example:
code_verifier = random-secret-value
It then creates a SHA-256 hash of that value and sends the encoded result as:
code_challenge = BASE64URL(SHA256(code_verifier))
The authorization request contains:
code_challenge=...
code_challenge_method=S256
After authentication, the identity provider returns an authorization code.
When exchanging the code for tokens, the application sends the original:
code_verifier
The identity provider calculates the challenge again and verifies that it matches the original code_challenge.
So:
Login request
|
| code_challenge
v
Identity Provider
|
| authorization code
v
Application
|
| code_verifier
v
Identity Provider
|
| Access Token + ID Token
v
Application
An attacker who steals the authorization code doesn't have the code_verifier, so they cannot successfully exchange the code for tokens.
Think of code_challenge as:
"Prove that you are the same application that started this authorization request."
Parameter |
Purpose |
Protects Against |
|---|---|---|
state |
Connects request and callback |
CSRF attacks |
nonce |
Connects authentication request with ID token |
ID token replay |
code_challenge |
Proves possession of the original verifier |
Authorization code interception |
A useful way to remember them is:
state
→ Protects the login flow
nonce
→ Protects the ID token
code_challenge
→ Protects the authorization code
How state, nonce, and code_challenge Work Together
A modern Angular SPA using OpenID Connect with PKCE might send something like:
/authorize?client_id=12345&response_type=code&scope=openid profile email&redirect_uri=https://myapp.com/callback
&state=ABC123
&nonce=XYZ789
&code_challenge=DEF456
&code_challenge_method=S256
These values work together but have different responsibilities.
This distinction is important when troubleshooting authentication flows. If you remember only three things, remember this:
· state - validates the flow.
· Nonce - validates the ID token.
· code_challenge - validates the authorization-code exchange.
Together, they provide important protections for a modern OAuth 2.0 and OpenID Connect authentication flow.
Happy Coding!

Join the conversation! Your thoughts help the community grow.