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

jvm-features.mddocs/

0

# JVM-Specific Authentication Features

1

2

JVM-only authentication features including Digest authentication and comprehensive OAuth 1.0a support. These features require JVM-specific cryptographic operations and are not available on other platforms.

3

4

## Digest Authentication

5

6

HTTP Digest authentication implementation that provides more secure username/password authentication than Basic auth by avoiding transmission of plaintext passwords.

7

8

### Configuration

9

10

```kotlin { .api }

11

fun AuthenticationConfig.digest(

12

name: String? = null,

13

configure: DigestAuthenticationProvider.Config.() -> Unit

14

)

15

16

@KtorDsl

17

class DigestAuthenticationProvider.Config(name: String?) : AuthenticationProvider.Config(name) {

18

var realm: String

19

var algorithmName: String

20

var nonceManager: NonceManager

21

fun digestProvider(digest: DigestProviderFunction)

22

fun validate(body: suspend ApplicationCall.(DigestCredential) -> Any?)

23

fun skipWhen(predicate: ApplicationCallPredicate)

24

}

25

```

26

27

### Configuration Properties

28

29

- **`realm`**: Authentication realm for WWW-Authenticate header

30

- **`algorithmName`**: Message digest algorithm (typically "MD5")

31

- **`nonceManager`**: Manages nonce generation and validation

32

33

### Configuration Functions

34

35

- **`digestProvider`**: Function that provides password digest for username/realm

36

- **`validate`**: Validates digest credentials and returns principal

37

- **`skipWhen`**: Predicate to skip authentication for certain calls

38

39

### Basic Digest Authentication

40

41

```kotlin

42

install(Authentication) {

43

digest("auth-digest") {

44

realm = "Protected Area"

45

algorithmName = "MD5"

46

digestProvider { userName, realm ->

47

// Provide digest for username in realm

48

userService.getPasswordDigest(userName, realm)

49

}

50

validate { credentials ->

51

// Additional validation after digest verification

52

UserIdPrincipal(credentials.userName)

53

}

54

}

55

}

56

57

routing {

58

authenticate("auth-digest") {

59

get("/secure") {

60

val principal = call.principal<UserIdPrincipal>()

61

call.respond("Access granted to ${principal?.name}")

62

}

63

}

64

}

65

```

66

67

### Advanced Digest Configuration

68

69

```kotlin

70

digest("custom-digest") {

71

realm = "Secure API"

72

algorithmName = "SHA-256" // Custom algorithm (client support varies)

73

nonceManager = CustomNonceManager() // Custom nonce management

74

75

digestProvider { userName, realm ->

76

// Calculate HA1 = MD5(username:realm:password)

77

val user = userService.getUser(userName)

78

if (user != null) {

79

MessageDigest.getInstance("MD5").digest(

80

"$userName:$realm:${user.password}".toByteArray()

81

)

82

} else null

83

}

84

85

validate { credentials ->

86

// Additional business logic validation

87

val user = userService.getUser(credentials.userName)

88

if (user?.isActive == true) {

89

UserIdPrincipal(credentials.userName)

90

} else null

91

}

92

}

93

```

94

95

## Digest Authentication API

96

97

### Provider Class

98

99

```kotlin { .api }

100

class DigestAuthenticationProvider internal constructor(

101

config: Config

102

) : AuthenticationProvider(config)

103

```

104

105

### Credential Types

106

107

```kotlin { .api }

108

data class DigestCredential(

109

val realm: String,

110

val userName: String,

111

val digestUri: String,

112

val nonce: String,

113

val opaque: String?,

114

val nonceCount: String?,

115

val algorithm: String?,

116

val response: String,

117

val cnonce: String?,

118

val qop: String?

119

)

120

```

121

122

### Function Types

123

124

```kotlin { .api }

125

typealias DigestProviderFunction = suspend (String, String) -> ByteArray?

126

```

127

128

### Credential Extraction and Verification

129

130

```kotlin { .api }

131

fun ApplicationCall.digestAuthenticationCredentials(): DigestCredential?

132

133

fun HttpAuthHeader.Parameterized.toDigestCredential(): DigestCredential?

134

135

suspend fun DigestCredential.verifier(

136

method: HttpMethod,

137

digester: MessageDigest,

138

userNameRealmPasswordDigest: suspend (String, String) -> ByteArray?

139

): Boolean

140

141

suspend fun DigestCredential.expectedDigest(

142

method: HttpMethod,

143

digester: MessageDigest,

144

userNameRealmPasswordDigest: ByteArray

145

): ByteArray

146

```

147

148

## Nonce Management

149

150

### Nonce Manager Interface

151

152

```kotlin { .api }

153

interface NonceManager {

154

suspend fun newNonce(): String

155

suspend fun verifyNonce(nonce: String): Boolean

156

}

157

158

object GenerateOnlyNonceManager : NonceManager {

159

override suspend fun newNonce(): String

160

override suspend fun verifyNonce(nonce: String): Boolean

161

}

162

```

163

164

### Custom Nonce Manager

165

166

```kotlin

167

class DatabaseNonceManager : NonceManager {

168

override suspend fun newNonce(): String {

169

val nonce = generateSecureNonce()

170

// Store nonce in database with expiration

171

nonceRepository.store(nonce, Clock.System.now() + 5.minutes)

172

return nonce

173

}

174

175

override suspend fun verifyNonce(nonce: String): Boolean {

176

return nonceRepository.isValid(nonce)

177

}

178

}

179

```

180

181

## OAuth 1.0a Support (JVM Only)

182

183

Complete OAuth 1.0a implementation with signature generation and verification.

184

185

### OAuth 1.0a Configuration

186

187

OAuth 1.0a server settings for legacy providers:

188

189

```kotlin { .api }

190

data class OAuth1aServerSettings(

191

val name: String,

192

val requestTokenUrl: String,

193

val authorizeUrl: String,

194

val accessTokenUrl: String,

195

val consumerKey: String,

196

val consumerSecret: String

197

) : OAuthServerSettings()

198

```

199

200

### OAuth 1.0a Flow Implementation

201

202

```kotlin

203

val twitterOAuth1a = OAuthServerSettings.OAuth1aServerSettings(

204

name = "twitter",

205

requestTokenUrl = "https://api.twitter.com/oauth/request_token",

206

authorizeUrl = "https://api.twitter.com/oauth/authorize",

207

accessTokenUrl = "https://api.twitter.com/oauth/access_token",

208

consumerKey = System.getenv("TWITTER_CONSUMER_KEY"),

209

consumerSecret = System.getenv("TWITTER_CONSUMER_SECRET")

210

)

211

212

install(Authentication) {

213

oauth("twitter-oauth1a") {

214

client = HttpClient()

215

providerLookup = { twitterOAuth1a }

216

urlProvider = { url -> redirectUrl(url, "/auth/twitter/callback") }

217

}

218

}

219

220

routing {

221

authenticate("twitter-oauth1a") {

222

get("/auth/twitter") {

223

// Initiates OAuth 1.0a flow

224

}

225

226

get("/auth/twitter/callback") {

227

val principal = call.principal<OAuthAccessTokenResponse.OAuth1a>()

228

if (principal != null) {

229

// Use OAuth 1.0a tokens to make API calls

230

val userProfile = getTwitterProfile(principal.token, principal.tokenSecret)

231

call.sessions.set(UserSession(userProfile.id))

232

call.respondRedirect("/dashboard")

233

} else {

234

call.respondRedirect("/login?error=oauth")

235

}

236

}

237

}

238

}

239

```

240

241

### OAuth 1.0a Token Types

242

243

```kotlin { .api }

244

data class OAuthAccessTokenResponse.OAuth1a(

245

val token: String,

246

val tokenSecret: String,

247

val extraParameters: Parameters = Parameters.Empty

248

) : OAuthAccessTokenResponse()

249

250

data class OAuthCallback.TokenPair(

251

val token: String,

252

val tokenSecret: String

253

) : OAuthCallback()

254

```

255

256

### OAuth 1.0a Exceptions

257

258

```kotlin { .api }

259

sealed class OAuth1aException : Exception() {

260

class MissingTokenException(message: String) : OAuth1aException()

261

}

262

```

263

264

## Full OAuth Procedure Support

265

266

The JVM platform provides complete OAuth implementation including the OAuth procedure class:

267

268

```kotlin { .api }

269

class OAuthProcedure {

270

suspend fun requestToken(): OAuthCallback.TokenPair

271

suspend fun authorizeUrl(requestToken: OAuthCallback.TokenPair): String

272

suspend fun accessToken(authorizedToken: OAuthCallback.TokenPair): OAuthAccessTokenResponse.OAuth1a

273

}

274

```

275

276

## Platform-Specific Cryptographic Features

277

278

### Digest Algorithm Support

279

280

```kotlin

281

digest("sha256-digest") {

282

algorithmName = "SHA-256" // JVM supports multiple algorithms

283

realm = "SHA-256 Protected"

284

digestProvider { userName, realm ->

285

MessageDigest.getInstance("SHA-256").digest(

286

"$userName:$realm:${getPassword(userName)}".toByteArray()

287

)

288

}

289

}

290

```

291

292

### Secure Random Generation

293

294

```kotlin

295

class SecureNonceManager : NonceManager {

296

private val secureRandom = SecureRandom()

297

298

override suspend fun newNonce(): String {

299

val bytes = ByteArray(16)

300

secureRandom.nextBytes(bytes)

301

return bytes.encodeBase64()

302

}

303

}

304

```

305

306

## Integration Examples

307

308

### Digest with Database

309

310

```kotlin

311

digest("database-digest") {

312

realm = "Database Protected"

313

digestProvider { userName, realm ->

314

transaction {

315

Users.select { Users.username eq userName }

316

.singleOrNull()

317

?.let { user ->

318

// Pre-computed digest stored in database

319

user[Users.digestHA1]

320

}

321

}

322

}

323

}

324

```

325

326

### OAuth 1.0a with Custom Signature

327

328

```kotlin

329

class CustomOAuth1aClient(

330

private val consumerKey: String,

331

private val consumerSecret: String

332

) {

333

suspend fun makeSignedRequest(

334

url: String,

335

token: String,

336

tokenSecret: String,

337

parameters: Map<String, String> = emptyMap()

338

): HttpResponse {

339

val oauthParams = generateOAuthParams(url, parameters, token)

340

val signature = generateSignature(url, oauthParams, tokenSecret)

341

342

return httpClient.get(url) {

343

header("Authorization", buildOAuthHeader(oauthParams, signature))

344

parameters.forEach { (key, value) ->

345

parameter(key, value)

346

}

347

}

348

}

349

}

350

```

351

352

## Security Considerations

353

354

### Digest Authentication Security

355

- More secure than Basic auth (no plaintext password transmission)

356

- Vulnerable to rainbow table attacks if using weak hashing

357

- Requires secure nonce management to prevent replay attacks

358

- Client support may be limited compared to Basic/Bearer auth

359

- Use strong digest algorithms when client support allows

360

361

### OAuth 1.0a Security

362

- Requires proper signature generation and verification

363

- More complex than OAuth 2.0 but provides built-in request signing

364

- Protect consumer secrets and token secrets securely

365

- Implement proper timestamp and nonce validation

366

- Handle signature verification failures gracefully