## TL;DR
The authorization code lived past its short lifetime, was already used, or the PKCE verifier does not match. Ensure the app exchanges the code immediately and exactly once, with the correct verifier and redirect URI.

## The query
```text
okta oauth "invalid_grant: the authorization code has expired"
```

## Use this when
- token exchange fails with invalid_grant code expired
- the error is intermittent under load
- a new integration never completes the OAuth flow

## Not for
- invalid_client secret errors
- redirect URI mismatch errors
- refresh token failures

## Steps
1. Confirm the app exchanges the authorization code immediately after receiving it, within seconds. Expected output: the exchange timing is measured and short.
2. Verify the code is used exactly once; retries with the same code always fail. Expected output: no retry logic reuses codes.
3. Check the PKCE code verifier sent at token time matches the challenge from the authorize call. Expected output: verifier and challenge align.
4. Confirm the redirect URI in the token request exactly matches the authorize request. Expected output: the two URIs are identical.
5. Fix the app flow and retry the login. Expected output: the token exchange succeeds.

## Applies to
Okta OAuth 2.0 and OIDC, public and confidential clients, PKCE flows, current Okta versions.

## Variant phrasings
### Expired code only on mobile apps
App switching delays the exchange; use the system browser flow that returns faster.

### Intermittent under load
A retry storm reuses codes; fix the retry logic to restart the flow instead.

## Why it happens
Authorization codes are single-use and live about a minute by design. Slow exchanges, double submissions, or verifier mismatches all surface as this same expired-code error.

## Edge cases
- Server clock skew beyond a minute can prematurely expire codes; sync time.
- Logging the code for debugging is fine; logging it in production is a leak.
- Some frameworks exchange the code server-side; confirm which component actually calls the token endpoint.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_LST-f-kfCnAkObaWm3uXpg
