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

form-session-auth.mddocs/

0

# Form and Session Authentication

1

2

Form-based authentication for HTML forms and session-based authentication for maintaining user sessions. These providers are commonly used together in web applications for traditional login/logout workflows.

3

4

## Form Authentication

5

6

HTML form-based authentication that processes username and password from POST form data.

7

8

### Configuration

9

10

```kotlin { .api }

11

fun AuthenticationConfig.form(

12

name: String? = null,

13

configure: FormAuthenticationProvider.Config.() -> Unit

14

)

15

16

@KtorDsl

17

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

18

var userParamName: String

19

var passwordParamName: String

20

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

21

fun challenge(body: FormAuthChallengeFunction)

22

fun challenge(redirectUrl: String)

23

fun challenge(redirect: Url)

24

fun skipWhen(predicate: ApplicationCallPredicate)

25

}

26

```

27

28

### Configuration Properties

29

30

- **`userParamName`**: HTML form parameter name for username (default: "user")

31

- **`passwordParamName`**: HTML form parameter name for password (default: "password")

32

33

### Configuration Functions

34

35

- **`validate`**: Suspend function that validates form credentials

36

- **`challenge`**: Function to handle authentication failures (typically redirects to login page)

37

- **`challenge(redirectUrl: String)`**: Convenience function to redirect to URL on failure

38

- **`challenge(redirect: Url)`**: Convenience function to redirect to Url on failure

39

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

40

41

### Basic Form Authentication

42

43

```kotlin

44

install(Authentication) {

45

form("auth-form") {

46

userParamName = "username"

47

passwordParamName = "password"

48

validate { credentials ->

49

if (credentials.name == "admin" && credentials.password == "secret") {

50

UserIdPrincipal(credentials.name)

51

} else {

52

null

53

}

54

}

55

challenge {

56

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

57

}

58

}

59

}

60

61

routing {

62

authenticate("auth-form") {

63

post("/secure-form") {

64

val principal = call.principal<UserIdPrincipal>()

65

call.respond("Form submitted by ${principal?.name}")

66

}

67

}

68

}

69

```

70

71

## Session Authentication

72

73

Session-based authentication that maintains user sessions using Ktor's session management.

74

75

### Configuration

76

77

```kotlin { .api }

78

fun <T : Any> AuthenticationConfig.session(

79

name: String? = null,

80

configure: SessionAuthenticationProvider.Config<T>.() -> Unit

81

)

82

83

@KtorDsl

84

class SessionAuthenticationProvider.Config<T : Any>(name: String?) : AuthenticationProvider.Config(name) {

85

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

86

fun challenge(body: SessionAuthChallengeFunction<T>)

87

fun challenge(redirectUrl: String)

88

fun challenge(redirect: Url)

89

fun skipWhen(predicate: ApplicationCallPredicate)

90

}

91

```

92

93

### Configuration Functions

94

95

- **`validate`**: Function that validates session data and returns principal

96

- **`challenge`**: Function to handle session authentication failures

97

- **`challenge(redirectUrl: String)`**: Convenience function to redirect to URL on failure

98

- **`challenge(redirect: Url)`**: Convenience function to redirect to Url on failure

99

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

100

101

### Basic Session Authentication

102

103

```kotlin

104

// Define session data class

105

@Serializable

106

data class UserSession(val userId: String, val loginTime: Long)

107

108

install(Sessions) {

109

cookie<UserSession>("user_session") {

110

cookie.httpOnly = true

111

cookie.secure = true

112

}

113

}

114

115

install(Authentication) {

116

session<UserSession>("auth-session") {

117

validate { session ->

118

// Validate session is not expired

119

if (Clock.System.now().epochSeconds - session.loginTime < 3600) {

120

UserIdPrincipal(session.userId)

121

} else {

122

null

123

}

124

}

125

challenge {

126

call.respondRedirect("/login")

127

}

128

}

129

}

130

131

routing {

132

authenticate("auth-session") {

133

get("/profile") {

134

val principal = call.principal<UserIdPrincipal>()

135

call.respond("Profile for ${principal?.name}")

136

}

137

}

138

}

139

```

140

141

## Combined Form and Session Authentication

142

143

Typical web application pattern combining form login with session maintenance:

144

145

```kotlin

146

@Serializable

147

data class UserSession(val userId: String, val loginTime: Long)

148

149

install(Sessions) {

150

cookie<UserSession>("user_session") {

151

cookie.httpOnly = true

152

cookie.secure = true

153

cookie.maxAgeInSeconds = 3600

154

}

155

}

156

157

install(Authentication) {

158

// Form authentication for login

159

form("login-form") {

160

userParamName = "username"

161

passwordParamName = "password"

162

validate { credentials ->

163

userService.authenticate(credentials)

164

}

165

challenge {

166

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

167

}

168

}

169

170

// Session authentication for protected pages

171

session<UserSession>("user-session") {

172

validate { session ->

173

userService.getUser(session.userId)?.let { user ->

174

UserIdPrincipal(user.id)

175

}

176

}

177

challenge {

178

call.respondRedirect("/login")

179

}

180

}

181

}

182

183

routing {

184

// Login page and form processing

185

get("/login") {

186

call.respondText(loginPageHtml, ContentType.Text.Html)

187

}

188

189

authenticate("login-form") {

190

post("/login") {

191

val principal = call.principal<UserIdPrincipal>()!!

192

call.sessions.set(UserSession(principal.name, Clock.System.now().epochSeconds))

193

call.respondRedirect("/dashboard")

194

}

195

}

196

197

// Protected pages using session auth

198

authenticate("user-session") {

199

get("/dashboard") {

200

val principal = call.principal<UserIdPrincipal>()

201

call.respond("Welcome to dashboard, ${principal?.name}!")

202

}

203

204

post("/logout") {

205

call.sessions.clear<UserSession>()

206

call.respondRedirect("/")

207

}

208

}

209

}

210

```

211

212

## Additional Session DSL Functions

213

214

```kotlin { .api }

215

fun <T : Any> AuthenticationConfig.session(name: String? = null): SessionAuthenticationProvider<T>

216

217

fun <T : Any> AuthenticationConfig.session(

218

name: String?,

219

kClass: KClass<T>

220

): SessionAuthenticationProvider<T>

221

222

fun <T : Any> AuthenticationConfig.session(

223

name: String? = null,

224

configure: SessionAuthenticationProvider.Config<T>.() -> Unit

225

): SessionAuthenticationProvider<T>

226

227

fun <T : Any> AuthenticationConfig.session(

228

name: String?,

229

kClass: KClass<T>,

230

configure: SessionAuthenticationProvider.Config<T>.() -> Unit

231

): SessionAuthenticationProvider<T>

232

```

233

234

## API Reference

235

236

### Form Authentication Types

237

238

```kotlin { .api }

239

class FormAuthenticationProvider internal constructor(

240

config: Config

241

) : AuthenticationProvider(config)

242

243

typealias FormAuthChallengeFunction = suspend PipelineContext<*, ApplicationCall>.(

244

context: FormAuthChallengeContext

245

) -> Unit

246

247

data class FormAuthChallengeContext(val call: ApplicationCall)

248

```

249

250

### Session Authentication Types

251

252

```kotlin { .api }

253

class SessionAuthenticationProvider<T : Any> internal constructor(

254

config: Config<T>

255

) : AuthenticationProvider(config)

256

257

typealias SessionAuthChallengeFunction<T> = suspend PipelineContext<*, ApplicationCall>.(

258

context: SessionChallengeContext

259

) -> Unit

260

261

data class SessionChallengeContext(val call: ApplicationCall)

262

263

val SessionAuthChallengeKey: String

264

```

265

266

## Advanced Patterns

267

268

### Multi-Step Form Authentication

269

270

```kotlin

271

form("multi-step-auth") {

272

validate { credentials ->

273

val step = call.parameters["step"]

274

when (step) {

275

"1" -> {

276

// First step: validate username

277

if (userService.userExists(credentials.name)) {

278

// Store partial authentication state

279

call.sessions.set(PartialAuth(credentials.name))

280

null // Don't complete authentication yet

281

} else null

282

}

283

"2" -> {

284

// Second step: validate password + 2FA

285

val partial = call.sessions.get<PartialAuth>()

286

if (partial?.username == credentials.name) {

287

val twoFactorCode = call.parameters["2fa_code"]

288

if (userService.validateCredentials(credentials) &&

289

twoFactorService.verify(credentials.name, twoFactorCode)) {

290

call.sessions.clear<PartialAuth>()

291

UserIdPrincipal(credentials.name)

292

} else null

293

} else null

294

}

295

else -> null

296

}

297

}

298

}

299

```

300

301

### Session with Role-Based Access

302

303

```kotlin

304

@Serializable

305

data class UserSession(val userId: String, val roles: List<String>)

306

307

session<UserSession>("admin-session") {

308

validate { session ->

309

if ("admin" in session.roles) {

310

UserIdPrincipal(session.userId)

311

} else null

312

}

313

challenge {

314

call.respond(HttpStatusCode.Forbidden, "Admin access required")

315

}

316

}

317

```

318

319

### Custom Session Storage

320

321

```kotlin

322

session<UserSession>("database-session") {

323

validate { session ->

324

// Validate against database instead of just session data

325

transaction {

326

Sessions.select { Sessions.sessionId eq session.sessionId }

327

.singleOrNull()

328

?.let { row ->

329

if (row[Sessions.expiresAt] > Clock.System.now()) {

330

UserIdPrincipal(row[Sessions.userId])

331

} else null

332

}

333

}

334

}

335

}

336

```

337

338

## Security Considerations

339

340

### Form Authentication

341

- Always use HTTPS to protect credentials in transit

342

- Implement CSRF protection for form submissions

343

- Use rate limiting to prevent brute force attacks

344

- Validate and sanitize all form inputs

345

- Consider implementing account lockout mechanisms

346

347

### Session Authentication

348

- Use secure session cookies (httpOnly, secure, sameSite)

349

- Implement session timeout and rotation

350

- Store minimal data in sessions, reference detailed data from server

351

- Invalidate sessions on logout and privilege changes

352

- Use secure session storage for sensitive applications

353

- Implement session fixation protection