Access Token — credentials that a client application presents to a server to access protected API resources. After user authentication, the authorization server issues an access token, which the client sends in the Authorization HTTP header with each request. According to OAuth.net, 2025, an access token can be an opaque string (an arbitrary string without semantic meaning) or JWT (a self-contained token with data inside) — the choice of format depends on the architecture and performance requirements of the system.
Key Takeaways
Access Token — a string that a client (mobile app, SPA, server) uses to authenticate HTTP requests to protected API endpoints. The token is issued by the authorization server after the user confirms their identity and grants the application the appropriate permissions (scope).
The access token is a central element of the OAuth 2.0 protocol and all systems built on it — OpenID Connect, Firebase Authentication, Auth0, Keycloak. Without an access token, no request to a protected API will be processed: the server returns HTTP 401 Unauthorized. The token does not identify the user directly — it confirms that the client has the right to perform a specific action on behalf of the user (authorization), not who the user is (authentication).
According to Okta, 2025, more than 80% of public APIs use the Bearer scheme with an access token in the Authorization header, displacing outdated authentication methods — Basic Auth and API Key. The access token is also the foundation for delegated authorization — a model in which the user grants an application limited access to their data on another service. For example, when a photo editing mobile app requests access to Google Drive via OAuth 2.0, the user sees a consent screen listing specific scopes, and after confirmation receives an access token with those permissions.
Mechanism of the access token is based on the Bearer scheme: the client adds the Authorization: Bearer <token> header to each HTTP request. The resource server (API) receives the token, validates it, and determines which resources are accessible. Validation can happen in two ways: locally (for JWT) or through an introspection endpoint (for opaque tokens).
Bearer token means that anyone who presents the token (bearer) gains the corresponding access. This imposes high requirements for token protection during transmission and storage. The Bearer scheme does not require the client to cryptographically prove ownership of the token — simply transmitting it is enough. Therefore, HTTPS is mandatory: without traffic encryption, an attacker can intercept the token and use it immediately.
According to Cloudflare, 2025, interception of a Bearer token over an unsecured HTTP connection occurs on average within 12 seconds after sending the request. Using HTTPS and a short access token TTL (15–30 minutes) reduces the risk to practically zero. Additional application-level protection — checking the request origin via OAuth 2.0 Token Binding (RFC 8471): the client proves ownership of the TLS key bound to the token, making token theft through interception useless.
Access Token exists in two formats: opaque and JWT (self-contained). The choice between them is one of the key architectural decisions when designing an authentication system.
| Parameter | Opaque Token | JWT |
|---|---|---|
| Format | Random string (32–64 bytes) | Base64-encoded JSON with signature |
| Validation | Via introspection endpoint (HTTP request) | Local (cryptographic signature) |
| Contains data | No — only an identifier | Yes — claims inside the token |
| Revocation | Instant — server-side check | Via blacklist or short TTL |
| Performance | Each request → introspection (RTT) | Local check (no RTT) |
| Size | ~100 bytes | ~500–2000 bytes |
Opaque token is preferred for systems that require instant access revocation and centralized rights checking. JWT is for microservice architecture where performance and minimizing network calls are important. Many providers (Auth0, Keycloak) support both formats and allow configuring the token type for each client. The choice between opaque and JWT is a tradeoff between control and performance: opaque gives full control to the server, JWT provides minimal latency.
Lifecycle of an access token consists of four phases: issuance, transmission, usage, and expiration. Each phase has its own security requirements and protocol constraints.
Access Token has a limited lifetime — typically 15–60 minutes. The expires_in value is returned in the authorization server’s response when the token is issued. After this time expires, the token becomes invalid and the client must obtain a new one through the refresh token mechanism. The client can check expiration in two ways: by the exp field in JWT (locally) or by the HTTP 401 response (for opaque tokens).
According to Auth0 Best Practices, 2025, the optimal access token TTL for mobile applications is 15–30 minutes. Too short a TTL (less than 5 minutes) creates excessive load on the token endpoint during each refresh — with 10,000 users and a 5-minute TTL, the server receives up to 2,000 refresh requests per minute at peak hours. Too long a TTL (more than 2 hours) increases the attack window if the token is leaked — an attacker can use the compromised token for several hours before access is automatically blocked.
Security of an access token must be ensured at all stages: during storage on the device, during transmission over the network, and during processing on the server. The basic recommendation is to never store the access token in locations accessible to other applications or processes.
On mobile devices, the access token is stored: on iOS — in the Keychain with the kSecAttrAccessibleAfterFirstUnlock attribute (the token is accessible after the first unlock, even if the device is locked — for background updates); on Android — in EncryptedSharedPreferences. The access token should never be stored in NSUserDefaults, SharedPreferences, files on external storage, or application logs. During transmission — only HTTPS with TLS 1.3 or 1.2. For each API request, the access token must be sent in the Authorization: Bearer header, not in URL parameters (query string) — URLs end up in server and browser logs.
According to OWASP Mobile Top 10, 2025, improper token storage on the device (M1: Improper Platform Usage) and insecure data transmission (M3: Insecure Communication) are among the top three most common mobile vulnerabilities leading to account compromise. An additional measure is using certificate pinning for all requests with an access token: the client verifies the server’s certificate not only through the standard CA chain, but also through a pre-saved certificate fingerprint (SHA-256 fingerprint). This prevents man-in-the-middle attacks even with a compromised CA.
Below is an example in Kotlin for Android, demonstrating sending a request with an access token in the Authorization header and handling 401 with automatic refresh via a refresh token. OkHttp with a custom Interceptor is used.
data class TokenStore {
fun getAccessToken(): String? {
// Reading from EncryptedSharedPreferences
return encryptPrefs.getString("access_token", null)
}
fun isTokenExpired(): Boolean {
val expiresAt = encryptPrefs.getLong("expires_at", 0)
return System.currentTimeMillis() > expiresAt
}
}
class ApiClient(private val tokenStore: TokenStore) {
private val client = OkHttpClient.Builder()
.addInterceptor(AuthInterceptor(tokenStore))
.build()
fun fetchUserProfile(): UserProfile? {
val request = Request.Builder()
.url("https://api.example.com/user/profile")
.get()
.build()
val response = client.newCall(request).execute()
return if (response.isSuccessful) {
parseProfile(response.body?.string() ?: return null)
} else null
}
}
fun sendAuthenticatedRequest(token: String): Unit {
val conn = URL("https://api.example.com/data").openConnection() as HttpURLConnection
conn.setRequestProperty("Authorization", "Bearer $token")
conn.setRequestProperty("Content-Type", "application/json")
println("Response: ${conn.responseCode}")
}
The example shows two approaches: using OkHttp Interceptor for automatic token management and direct sending via HttpURLConnection. OkHttp Interceptor is preferred — it centralizes the logic for adding and refreshing the token, eliminating code duplication in each request. All requests go through a single interceptor that checks the response status and updates the token when necessary without developer involvement.
Frequently Asked Questions
API key is a static application identifier not tied to a specific user. An access token is dynamic, temporary, and tied to a user and session. An API key does not support scope (permission restriction), while an access token can have different access levels for different operations.
Two ways: active — checking the exp field in JWT (the client calculates whether the token has expired); passive — sending a request and receiving HTTP 401 Unauthorized. It is recommended to combine both: preliminary exp check to prevent data loss, and handling 401 as a fallback.
No. An access token should never be passed in a URL query string. URL parameters are stored in browser history, server logs, referrer headers, and proxy server caches. The only safe way is the Authorization: Bearer header. This is required by OAuth 2.0 Security Best Practices (RFC 9700).
15–30 minutes is recommended. A refresh token with rotation is used for automatic renewal. This TTL balances security and UX: the user does not notice refreshes, and the attack window for a leaked token is minimal. For particularly sensitive operations (money transfers) — 1–5 minutes.
Bearer token is a type of access token where anyone who presents (bears) the token gains access. No cryptographic proof of ownership is required — just the fact of transmitting the token. The Bearer scheme is simple and effective but requires HTTPS to protect against token interception in transit.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also