0
# Bearer Authentication
1
2
Bearer token authentication for API access tokens, JWT tokens, and other token-based authentication schemes. This provider extracts bearer tokens from the `Authorization` header and validates them using a custom authentication function.
3
4
## Configuration
5
6
```kotlin { .api }
7
fun AuthenticationConfig.bearer(
8
name: String? = null,
9
configure: BearerAuthenticationProvider.Config.() -> Unit
10
)
11
12
@KtorDsl
13
class BearerAuthenticationProvider.Config(name: String?) : AuthenticationProvider.Config(name) {
14
var realm: String?
15
var authHeader: (ApplicationCall) -> HttpAuthHeader?
16
fun authenticate(body: suspend ApplicationCall.(BearerTokenCredential) -> Any?)
17
fun challenge(body: ChallengeFunction)
18
fun skipWhen(predicate: ApplicationCallPredicate)
19
fun authSchemes(defaultScheme: String, vararg additionalSchemes: String)
20
}
21
```
22
23
### Configuration Properties
24
25
- **`realm`**: Optional authentication realm for challenge responses
26
- **`authHeader`**: Custom function to extract auth header from request
27
28
### Configuration Functions
29
30
- **`authenticate`**: Suspend function that validates bearer token and returns a principal
31
- **`challenge`**: Custom challenge function for authentication failures
32
- **`skipWhen`**: Predicate to skip authentication for certain calls
33
- **`authSchemes`**: Configure supported authentication schemes (default: "Bearer")
34
35
## Basic Usage
36
37
```kotlin
38
install(Authentication) {
39
bearer("auth-bearer") {
40
realm = "Access to the API"
41
authenticate { tokenCredentials ->
42
if (tokenCredentials.token == "valid-token") {
43
UserIdPrincipal("user")
44
} else {
45
null
46
}
47
}
48
}
49
}
50
51
routing {
52
authenticate("auth-bearer") {
53
get("/api/protected") {
54
val principal = call.principal<UserIdPrincipal>()
55
call.respond("Access granted to ${principal?.name}")
56
}
57
}
58
}
59
```
60
61
## JWT Token Validation
62
63
```kotlin
64
bearer("jwt-auth") {
65
realm = "JWT Protected API"
66
authenticate { tokenCredentials ->
67
try {
68
val jwt = JWT.require(Algorithm.HMAC256("secret"))
69
.build()
70
.verify(tokenCredentials.token)
71
72
UserIdPrincipal(jwt.getClaim("username").asString())
73
} catch (exception: JWTVerificationException) {
74
null
75
}
76
}
77
}
78
```
79
80
## API Key Authentication
81
82
```kotlin
83
bearer("api-key-auth") {
84
realm = "API Key Required"
85
authenticate { tokenCredentials ->
86
val apiKey = tokenCredentials.token
87
if (apiKeyService.isValid(apiKey)) {
88
val user = apiKeyService.getUser(apiKey)
89
UserIdPrincipal(user.id)
90
} else {
91
null
92
}
93
}
94
}
95
```
96
97
## Custom Header Extraction
98
99
```kotlin
100
bearer("custom-header") {
101
authHeader { call ->
102
// Extract token from custom header
103
val token = call.request.headers["X-API-Token"]
104
token?.let { HttpAuthHeader.Single("Bearer", it) }
105
}
106
authenticate { tokenCredentials ->
107
validateCustomToken(tokenCredentials.token)
108
}
109
}
110
```
111
112
## API Reference
113
114
### Provider Class
115
116
```kotlin { .api }
117
class BearerAuthenticationProvider internal constructor(
118
config: Config
119
) : AuthenticationProvider(config)
120
```
121
122
### Credential Type
123
124
```kotlin { .api }
125
data class BearerTokenCredential(val token: String)
126
```
127
128
### Authentication Function Type
129
130
```kotlin { .api }
131
typealias AuthenticationFunction<C> = suspend ApplicationCall.(C) -> Any?
132
```
133
134
### Header Extraction Function Type
135
136
```kotlin { .api }
137
typealias AuthHeaderFunction = (ApplicationCall) -> HttpAuthHeader?
138
```
139
140
## Advanced Configuration
141
142
### Multi-Token Support
143
144
```kotlin
145
bearer("multi-token") {
146
authenticate { tokenCredentials ->
147
when {
148
tokenCredentials.token.startsWith("jwt_") ->
149
validateJWT(tokenCredentials.token.removePrefix("jwt_"))
150
tokenCredentials.token.startsWith("api_") ->
151
validateApiKey(tokenCredentials.token.removePrefix("api_"))
152
else -> null
153
}
154
}
155
}
156
```
157
158
### Token Refresh Integration
159
160
```kotlin
161
bearer("refreshable-token") {
162
authenticate { tokenCredentials ->
163
val token = tokenCredentials.token
164
when (val result = tokenService.validateToken(token)) {
165
is TokenResult.Valid -> UserIdPrincipal(result.userId)
166
is TokenResult.Expired -> {
167
// Trigger token refresh flow
168
call.response.headers.append("X-Token-Refresh-Required", "true")
169
null
170
}
171
is TokenResult.Invalid -> null
172
}
173
}
174
}
175
```
176
177
### Custom Challenge Response
178
179
```kotlin
180
bearer("custom-challenge") {
181
realm = "Protected API"
182
challenge { defaultScheme, realm ->
183
call.respond(
184
HttpStatusCode.Unauthorized,
185
mapOf(
186
"error" -> "invalid_token",
187
"error_description" -> "The access token is invalid or expired"
188
)
189
)
190
}
191
authenticate { tokenCredentials ->
192
validateToken(tokenCredentials.token)
193
}
194
}
195
```
196
197
## Integration Patterns
198
199
### Database Token Validation
200
201
```kotlin
202
bearer("database-tokens") {
203
authenticate { tokenCredentials ->
204
transaction {
205
AccessTokens.select { AccessTokens.token eq tokenCredentials.token }
206
.singleOrNull()
207
?.let { tokenRow ->
208
if (tokenRow[AccessTokens.expiresAt] > Clock.System.now()) {
209
UserIdPrincipal(tokenRow[AccessTokens.userId])
210
} else {
211
null
212
}
213
}
214
}
215
}
216
}
217
```
218
219
### OAuth 2.0 Access Token Validation
220
221
```kotlin
222
bearer("oauth2-token") {
223
authenticate { tokenCredentials ->
224
val introspectionResponse = httpClient.post("${oauthServer}/introspect") {
225
parameter("token", tokenCredentials.token)
226
basicAuth(clientId, clientSecret)
227
}.body<IntrospectionResponse>()
228
229
if (introspectionResponse.active) {
230
UserIdPrincipal(introspectionResponse.sub)
231
} else {
232
null
233
}
234
}
235
}
236
```
237
238
## Error Handling
239
240
Bearer authentication uses the standard authentication failure causes:
241
242
```kotlin { .api }
243
sealed class AuthenticationFailedCause {
244
object NoCredentials : AuthenticationFailedCause()
245
object InvalidCredentials : AuthenticationFailedCause()
246
class Error(val message: String) : AuthenticationFailedCause()
247
}
248
```
249
250
Common HTTP responses:
251
- **401 Unauthorized**: When token is missing, invalid, or expired
252
- **403 Forbidden**: When token is valid but lacks required permissions
253
254
## Security Best Practices
255
256
- Use secure token generation with sufficient entropy
257
- Implement token expiration and refresh mechanisms
258
- Store tokens securely (avoid local storage for sensitive tokens)
259
- Use HTTPS to protect tokens in transit
260
- Implement proper token revocation
261
- Consider using short-lived access tokens with refresh tokens
262
- Validate token format and signature before processing
263
- Log authentication failures for security monitoring