06 Oct

OpenID Connect: Understanding state, nonce, and code_challenge

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:

Application
|
| 1. Authorization Request
| + code_challenge
v
Identity Provider
|
| 2. Authorization Code
v
Application
|
| 3. Token Request
| + code_verifier
v
Identity Provider
|
| 4. 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.”


How are they different?

ParameterPurposeProtects Against
stateConnects request and callbackCSRF attacks
nonceConnects authentication request with ID tokenID token replay
code_challengeProves possession of the original verifierAuthorization 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

Putting Everything 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!

About the Author

Leave a Reply