or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

basic-auth.mdbearer-auth.mdform-session-auth.mdindex.mdjvm-features.mdoauth.md

oauth.mddocs/

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