0
# OAuth Authentication
1
2
OAuth 1.0a and OAuth 2.0 authentication support for integrating with third-party authentication providers like Google, GitHub, Twitter, and custom OAuth servers. This includes authorization code flows, token validation, and user profile retrieval.
3
4
## OAuth 2.0 Authentication
5
6
OAuth 2.0 authorization code flow implementation for modern OAuth providers.
7
8
### Configuration
9
10
```kotlin { .api }
11
fun AuthenticationConfig.oauth(
12
name: String? = null,
13
configure: OAuthAuthenticationProvider.Config.() -> Unit
14
)
15
16
@KtorDsl
17
class OAuthAuthenticationProvider.Config(name: String?) : AuthenticationProvider.Config(name) {
18
lateinit var client: HttpClient
19
lateinit var providerLookup: ApplicationCall.() -> OAuthServerSettings?
20
lateinit var urlProvider: ApplicationCall.(OAuthServerSettings) -> String
21
fun skipWhen(predicate: ApplicationCallPredicate)
22
}
23
```
24
25
### OAuth Version Enum
26
27
```kotlin { .api }
28
enum class OAuthVersion {
29
V10a, V20
30
}
31
```
32
33
### OAuth Server Settings
34
35
```kotlin { .api }
36
sealed class OAuthServerSettings {
37
data class OAuth1aServerSettings(
38
val name: String,
39
val requestTokenUrl: String,
40
val authorizeUrl: String,
41
val accessTokenUrl: String,
42
val consumerKey: String,
43
val consumerSecret: String
44
) : OAuthServerSettings()
45
46
data class OAuth2ServerSettings(
47
val name: String,
48
val authorizeUrl: String,
49
val accessTokenUrl: String,
50
val requestMethod: HttpMethod = HttpMethod.Post,
51
val clientId: String,
52
val clientSecret: String,
53
val defaultScopes: List<String> = emptyList(),
54
val extraAuthParameters: List<Pair<String, String>> = emptyList(),
55
val extraTokenParameters: List<Pair<String, String>> = emptyList(),
56
val accessTokenRequiresBasicAuth: Boolean = false,
57
val onStateCreated: ((call: ApplicationCall, state: String) -> Unit)? = null,
58
val authorizeUrlInterceptor: ((URLBuilder, ApplicationCall) -> Unit)? = null,
59
val passParamsInURL: Boolean = false,
60
val nonceManager: NonceManager = GenerateOnlyNonceManager
61
) : OAuthServerSettings()
62
}
63
```
64
65
### Basic OAuth 2.0 Setup
66
67
```kotlin
68
val googleOAuthProvider = OAuthServerSettings.OAuth2ServerSettings(
69
name = "google",
70
authorizeUrl = "https://accounts.google.com/o/oauth2/auth",
71
accessTokenUrl = "https://www.googleapis.com/oauth2/v4/token",
72
clientId = "your-google-client-id",
73
clientSecret = "your-google-client-secret",
74
defaultScopes = listOf("openid", "profile", "email")
75
)
76
77
install(Authentication) {
78
oauth("auth-oauth-google") {
79
client = HttpClient(Apache)
80
providerLookup = { googleOAuthProvider }
81
urlProvider = { url ->
82
redirectUrl(url, "/callback")
83
}
84
}
85
}
86
87
routing {
88
authenticate("auth-oauth-google") {
89
get("/login") {
90
// Redirects to OAuth provider
91
}
92
93
get("/callback") {
94
val principal = call.principal<OAuthAccessTokenResponse.OAuth2>()
95
if (principal != null) {
96
// Use access token to get user info
97
val userInfo = getUserInfo(principal.accessToken)
98
call.sessions.set(UserSession(userInfo.id))
99
call.respondRedirect("/dashboard")
100
} else {
101
call.respondRedirect("/login?error=oauth")
102
}
103
}
104
}
105
}
106
```
107
108
## OAuth 1.0a Authentication (JVM Only)
109
110
OAuth 1.0a implementation for legacy providers like Twitter API v1.1.
111
112
### OAuth 1.0a Server Settings
113
114
```kotlin { .api }
115
data class OAuth1aServerSettings(
116
val name: String,
117
val requestTokenUrl: String,
118
val authorizeUrl: String,
119
val accessTokenUrl: String,
120
val consumerKey: String,
121
val consumerSecret: String
122
) : OAuthServerSettings()
123
```
124
125
### OAuth 1.0a Setup
126
127
```kotlin
128
val twitterOAuthProvider = OAuthServerSettings.OAuth1aServerSettings(
129
name = "twitter",
130
requestTokenUrl = "https://api.twitter.com/oauth/request_token",
131
authorizeUrl = "https://api.twitter.com/oauth/authorize",
132
accessTokenUrl = "https://api.twitter.com/oauth/access_token",
133
consumerKey = "your-twitter-consumer-key",
134
consumerSecret = "your-twitter-consumer-secret"
135
)
136
137
install(Authentication) {
138
oauth("auth-oauth-twitter") {
139
client = HttpClient(Apache)
140
providerLookup = { twitterOAuthProvider }
141
urlProvider = { url ->
142
redirectUrl(url, "/twitter-callback")
143
}
144
}
145
}
146
```
147
148
## OAuth Callback Types
149
150
```kotlin { .api }
151
sealed class OAuthCallback {
152
data class TokenSingle(val token: String, val state: String) : OAuthCallback()
153
data class TokenPair(val token: String, val tokenSecret: String) : OAuthCallback()
154
data class Error(val error: String, val errorDescription: String?) : OAuthCallback()
155
}
156
```
157
158
## OAuth Access Token Responses
159
160
```kotlin { .api }
161
sealed class OAuthAccessTokenResponse {
162
data class OAuth2(
163
val accessToken: String,
164
val tokenType: String,
165
val expiresIn: Long?,
166
val refreshToken: String?,
167
val extraParameters: Parameters = Parameters.Empty
168
) : OAuthAccessTokenResponse()
169
170
data class OAuth1a(
171
val token: String,
172
val tokenSecret: String,
173
val extraParameters: Parameters = Parameters.Empty
174
) : OAuthAccessTokenResponse()
175
}
176
```
177
178
## Advanced OAuth 2.0 Patterns
179
180
### Custom State Management
181
182
```kotlin
183
oauth("custom-state") {
184
client = HttpClient(CIO)
185
providerLookup = {
186
googleOAuthProvider.copy(
187
onStateCreated = { call, state ->
188
// Store state with additional context
189
call.sessions.set(OAuthState(state, call.request.uri))
190
}
191
)
192
}
193
urlProvider = { url -> redirectUrl(url, "/callback") }
194
}
195
```
196
197
### Multiple OAuth Providers
198
199
```kotlin
200
install(Authentication) {
201
oauth("google") {
202
client = httpClient
203
providerLookup = { googleProvider }
204
urlProvider = { redirectUrl(it, "/auth/google/callback") }
205
}
206
207
oauth("github") {
208
client = httpClient
209
providerLookup = { githubProvider }
210
urlProvider = { redirectUrl(it, "/auth/github/callback") }
211
}
212
}
213
214
routing {
215
get("/auth/{provider}") {
216
val provider = call.parameters["provider"]
217
authenticate(provider) {
218
// Redirect to OAuth provider
219
}
220
}
221
}
222
```
223
224
### Token Refresh Implementation
225
226
```kotlin
227
suspend fun refreshOAuth2Token(refreshToken: String): OAuthAccessTokenResponse.OAuth2? {
228
return try {
229
httpClient.submitForm(
230
url = "https://oauth2.googleapis.com/token",
231
formParameters = parameters {
232
append("grant_type", "refresh_token")
233
append("refresh_token", refreshToken)
234
append("client_id", clientId)
235
append("client_secret", clientSecret)
236
}
237
).body()
238
} catch (e: Exception) {
239
null
240
}
241
}
242
```
243
244
## OAuth 2.0 Verification
245
246
```kotlin { .api }
247
suspend fun verifyWithOAuth2(
248
client: HttpClient,
249
settings: OAuthServerSettings.OAuth2ServerSettings,
250
callbackResponse: OAuthCallback.TokenSingle,
251
configure: HttpRequestBuilder.() -> Unit = {}
252
): OAuthAccessTokenResponse.OAuth2
253
254
suspend fun verifyWithOAuth2(
255
credential: UserPasswordCredential,
256
client: HttpClient,
257
settings: OAuthServerSettings.OAuth2ServerSettings
258
): OAuthAccessTokenResponse.OAuth2
259
```
260
261
### Resource Owner Password Flow
262
263
```kotlin
264
suspend fun authenticateWithResourceOwner(
265
username: String,
266
password: String
267
): OAuthAccessTokenResponse.OAuth2? {
268
return verifyWithOAuth2(
269
client = httpClient,
270
settings = oauth2Settings,
271
callbackResponse = OAuthCallback.TokenSingle(
272
token = "", // Not used in resource owner flow
273
state = ""
274
)
275
) {
276
parameter("grant_type", "password")
277
parameter("username", username)
278
parameter("password", password)
279
}
280
}
281
```
282
283
## OAuth Exceptions
284
285
```kotlin { .api }
286
sealed class OAuth2Exception : Exception() {
287
class InvalidGrant(message: String) : OAuth2Exception()
288
class InvalidNonce(message: String) : OAuth2Exception()
289
class MissingAccessToken(message: String) : OAuth2Exception()
290
class UnsupportedGrantType(message: String) : OAuth2Exception()
291
class UnknownException(message: String, cause: Throwable?) : OAuth2Exception()
292
}
293
294
sealed class OAuth1aException : Exception() {
295
class MissingTokenException(message: String) : OAuth1aException()
296
}
297
```
298
299
## Common OAuth Grant Types
300
301
```kotlin { .api }
302
object OAuthGrantTypes {
303
const val AuthorizationCode = "authorization_code"
304
const val RefreshToken = "refresh_token"
305
const val Password = "password"
306
const val ClientCredentials = "client_credentials"
307
}
308
```
309
310
## OAuth Request/Response Parameters
311
312
```kotlin { .api }
313
object OAuth2RequestParameters {
314
const val ClientId = "client_id"
315
const val ClientSecret = "client_secret"
316
const val Code = "code"
317
const val GrantType = "grant_type"
318
const val RedirectUri = "redirect_uri"
319
const val ResponseType = "response_type"
320
const val Scope = "scope"
321
const val State = "state"
322
}
323
324
object OAuth2ResponseParameters {
325
const val AccessToken = "access_token"
326
const val TokenType = "token_type"
327
const val ExpiresIn = "expires_in"
328
const val RefreshToken = "refresh_token"
329
const val Scope = "scope"
330
const val State = "state"
331
const val Error = "error"
332
const val ErrorDescription = "error_description"
333
}
334
```
335
336
## Platform Availability
337
338
- **OAuth 2.0**: Available on all platforms (common, JVM, JS/WASM, native)
339
- **OAuth 1.0a**: JVM only due to cryptographic signature requirements
340
- **Full OAuth procedure support**: JVM only
341
342
## Security Considerations
343
344
### OAuth 2.0 Security
345
- Always use HTTPS for OAuth flows
346
- Validate state parameter to prevent CSRF attacks
347
- Store client secrets securely (environment variables, secure vaults)
348
- Use PKCE (Proof Key for Code Exchange) for public clients
349
- Implement proper token storage and transmission
350
- Validate redirect URIs to prevent authorization code interception
351
- Use short-lived access tokens with refresh tokens when possible
352
353
### OAuth 1.0a Security
354
- Implement proper signature verification
355
- Use secure random nonce generation
356
- Protect consumer secrets and token secrets
357
- Implement replay attack prevention
358
- Validate all OAuth 1.0a signature components
359
360
### General OAuth Security
361
- Implement proper error handling without information leakage
362
- Log OAuth events for security monitoring
363
- Use minimal required scopes
364
- Implement token revocation when needed
365
- Validate JWT tokens properly if using OpenID Connect