0
# Bearer Authentication
1
2
Bearer token authentication provider with support for automatic token refresh, access/refresh token pairs, circuit breaker functionality, and configurable token loading and refresh mechanisms for OAuth flows and API token authentication.
3
4
## Capabilities
5
6
### Bearer Authentication Provider Installation
7
8
Install the Bearer authentication provider with configuration for token management.
9
10
```kotlin { .api }
11
/**
12
* Installs the client's BearerAuthProvider
13
* @param block Configuration block for Bearer authentication
14
*/
15
fun AuthConfig.bearer(block: BearerAuthConfig.() -> Unit)
16
```
17
18
**Usage Example:**
19
20
```kotlin
21
import io.ktor.client.*
22
import io.ktor.client.plugins.auth.*
23
import io.ktor.client.plugins.auth.providers.*
24
25
val client = HttpClient {
26
Auth {
27
bearer {
28
loadTokens {
29
BearerTokens("access_token", "refresh_token")
30
}
31
refreshTokens { params ->
32
// Refresh token logic
33
refreshAccessToken(params.oldTokens?.refreshToken)
34
}
35
}
36
}
37
}
38
```
39
40
### Bearer Authentication Configuration
41
42
Configuration class for BearerAuthProvider with token loading, refreshing, and sending behavior control.
43
44
```kotlin { .api }
45
/**
46
* Configuration for BearerAuthProvider
47
*/
48
class BearerAuthConfig {
49
/**
50
* Optional realm specification for Bearer authentication
51
*/
52
var realm: String?
53
54
/**
55
* Configures a callback that refreshes a token when the 401 status code is received
56
* @param block Suspend function that handles token refresh with refresh parameters
57
*/
58
fun refreshTokens(block: suspend RefreshTokensParams.() -> BearerTokens?)
59
60
/**
61
* Configures a callback that loads a cached token from a local storage
62
* Note: Using the same client instance here to make a request will result in a deadlock
63
* @param block Suspend function that returns cached tokens or null
64
*/
65
fun loadTokens(block: suspend () -> BearerTokens?)
66
67
/**
68
* Configures when to send credentials without waiting for HttpStatusCode.Unauthorized
69
* @param block Function that determines based on request whether to send credentials proactively
70
*/
71
fun sendWithoutRequest(block: (HttpRequestBuilder) -> Boolean)
72
}
73
```
74
75
**Usage Examples:**
76
77
```kotlin
78
bearer {
79
// Load tokens from storage
80
loadTokens {
81
loadTokensFromSecureStorage()
82
}
83
84
// Refresh token logic
85
refreshTokens { params ->
86
val oldRefreshToken = params.oldTokens?.refreshToken
87
if (oldRefreshToken != null) {
88
// Use a different client instance to avoid deadlock
89
val refreshClient = HttpClient()
90
91
// Mark as refresh request to prevent infinite loops
92
val response = refreshClient.post("/oauth/refresh") {
93
markAsRefreshTokenRequest()
94
setBody(RefreshTokenRequest(oldRefreshToken))
95
}
96
97
if (response.status.isSuccess()) {
98
val tokenResponse = response.body<TokenResponse>()
99
BearerTokens(tokenResponse.accessToken, tokenResponse.refreshToken)
100
} else null
101
} else null
102
}
103
104
// Send tokens proactively for API calls
105
sendWithoutRequest { request ->
106
request.url.host == "api.example.com"
107
}
108
109
realm = "api"
110
}
111
```
112
113
### Bearer Tokens
114
115
Container class for Bearer authentication tokens supporting access/refresh token pairs.
116
117
```kotlin { .api }
118
/**
119
* Container for bearer tokens
120
* @param accessToken The access token used for authentication
121
* @param refreshToken Optional refresh token used to obtain new access tokens
122
*/
123
class BearerTokens(
124
val accessToken: String,
125
val refreshToken: String?
126
)
127
```
128
129
### Refresh Token Parameters
130
131
Parameters passed to the token refresh callback providing context for the refresh operation.
132
133
```kotlin { .api }
134
/**
135
* Parameters passed to refreshTokens lambda
136
* @param client HttpClient instance for making refresh requests
137
* @param response HttpResponse that triggered the token refresh
138
* @param oldTokens Previously cached tokens, may be null
139
*/
140
class RefreshTokensParams(
141
val client: HttpClient,
142
val response: HttpResponse,
143
val oldTokens: BearerTokens?
144
) {
145
/**
146
* Marks that this request is for refreshing auth tokens, resulting in special handling
147
* This prevents the request from triggering additional authentication flows
148
*/
149
fun HttpRequestBuilder.markAsRefreshTokenRequest()
150
}
151
```
152
153
**Usage Example:**
154
155
```kotlin
156
refreshTokens { params ->
157
val refreshToken = params.oldTokens?.refreshToken
158
if (refreshToken != null) {
159
try {
160
// Create separate client for refresh to avoid deadlock
161
val refreshClient = HttpClient()
162
163
val response = refreshClient.post("/auth/refresh") {
164
// Important: mark as refresh request
165
markAsRefreshTokenRequest()
166
167
header("Authorization", "Bearer $refreshToken")
168
contentType(ContentType.Application.Json)
169
setBody(mapOf("refresh_token" to refreshToken))
170
}
171
172
if (response.status.isSuccess()) {
173
val newTokens = response.body<TokenResponse>()
174
BearerTokens(newTokens.access_token, newTokens.refresh_token)
175
} else {
176
null // Refresh failed
177
}
178
} catch (e: Exception) {
179
null // Handle refresh errors
180
}
181
} else {
182
null // No refresh token available
183
}
184
}
185
```
186
187
### Bearer Authentication Provider
188
189
Authentication provider implementation for the Bearer HTTP authentication scheme supporting OAuth flows and API token authentication.
190
191
```kotlin { .api }
192
/**
193
* Authentication provider for the Bearer HTTP authentication scheme
194
* Bearer authentication involves security tokens called bearer tokens
195
* These tokens can be used as part of OAuth flow to authorize users of your application
196
* by using external providers, such as Google, Facebook, Twitter, and so on
197
*/
198
class BearerAuthProvider(
199
private val refreshTokens: suspend RefreshTokensParams.() -> BearerTokens?,
200
loadTokens: suspend () -> BearerTokens?,
201
private val sendWithoutRequestCallback: (HttpRequestBuilder) -> Boolean = { true },
202
private val realm: String?
203
) : AuthProvider {
204
205
// Note: clearToken() method is available but marked as @InternalAPI
206
207
override fun sendWithoutRequest(request: HttpRequestBuilder): Boolean
208
override fun isApplicable(auth: HttpAuthHeader): Boolean
209
override suspend fun addRequestHeaders(request: HttpRequestBuilder, authHeader: HttpAuthHeader?)
210
override suspend fun refreshToken(response: HttpResponse): Boolean
211
}
212
```
213
214
**Usage Examples:**
215
216
```kotlin
217
import io.ktor.client.*
218
import io.ktor.client.plugins.auth.*
219
import io.ktor.client.plugins.auth.providers.*
220
221
// Simple bearer token setup
222
val client = HttpClient {
223
Auth {
224
bearer {
225
loadTokens {
226
BearerTokens("my-access-token", null)
227
}
228
}
229
}
230
}
231
232
// OAuth flow with refresh tokens
233
val oauthClient = HttpClient {
234
Auth {
235
bearer {
236
loadTokens {
237
// Load from secure storage
238
val tokens = secureStorage.getTokens()
239
if (tokens != null) {
240
BearerTokens(tokens.accessToken, tokens.refreshToken)
241
} else null
242
}
243
244
refreshTokens { params ->
245
val refreshToken = params.oldTokens?.refreshToken
246
if (refreshToken != null) {
247
val refreshClient = HttpClient()
248
249
val response = refreshClient.post("https://oauth.provider.com/token") {
250
markAsRefreshTokenRequest()
251
parameter("grant_type", "refresh_token")
252
parameter("refresh_token", refreshToken)
253
parameter("client_id", "my-client-id")
254
}
255
256
if (response.status.isSuccess()) {
257
val tokenData = response.body<OAuthTokenResponse>()
258
// Cache new tokens
259
secureStorage.saveTokens(tokenData)
260
BearerTokens(tokenData.access_token, tokenData.refresh_token)
261
} else null
262
} else null
263
}
264
265
sendWithoutRequest { request ->
266
// Always send for API requests
267
request.url.host.contains("api.")
268
}
269
}
270
}
271
}
272
273
// Note: clearToken() is available as internal API for clearing cached tokens
274
```
275
276
## Authentication Flow
277
278
1. **Token Loading**: `loadTokens()` is called to retrieve cached tokens
279
2. **Proactive Sending**: If `sendWithoutRequest` returns `true`, Bearer header is added immediately
280
3. **401 Response**: When server responds with 401, provider checks `isApplicable()` for Bearer scheme
281
4. **Realm Matching**: Provider validates realm from WWW-Authenticate header if specified
282
5. **Token Refresh**: `refreshTokens()` is called with refresh parameters
283
6. **Cache Update**: New tokens are cached automatically after successful refresh
284
7. **Retry Request**: Request is retried with new Bearer token
285
8. **Circuit Breaker**: Refresh requests marked with `markAsRefreshTokenRequest()` skip authentication