Authentication and authorization plugin for Ktor server applications
—
HTTP Basic authentication implementation that validates username and password credentials sent in the Authorization header. This provider extracts credentials from the HTTP Basic authentication scheme and validates them using a custom authentication function.
fun AuthenticationConfig.basic(
name: String? = null,
configure: BasicAuthenticationProvider.Config.() -> Unit
)
@KtorDsl
class BasicAuthenticationProvider.Config(name: String?) : AuthenticationProvider.Config(name) {
var realm: String
var charset: Charset?
fun validate(body: suspend ApplicationCall.(UserPasswordCredential) -> Any?)
fun challenge(body: ChallengeFunction)
fun skipWhen(predicate: ApplicationCallPredicate)
}realm: The authentication realm displayed to users (default: "Ktor Server")charset: Character encoding for credential parsing (default: UTF-8)validate: Suspend function that validates credentials and returns a principalchallenge: Custom challenge function for authentication failuresskipWhen: Predicate to skip authentication for certain callsinstall(Authentication) {
basic("auth-basic") {
realm = "Access to protected resources"
validate { credentials ->
if (credentials.name == "admin" && credentials.password == "secret") {
UserIdPrincipal(credentials.name)
} else {
null
}
}
}
}
routing {
authenticate("auth-basic") {
get("/admin") {
val principal = call.principal<UserIdPrincipal>()
call.respond("Welcome ${principal?.name}!")
}
}
}basic("auth-basic") {
realm = "Secure Area"
validate { credentials ->
// Validate against database
userService.authenticate(credentials)
}
challenge { defaultScheme, realm ->
call.respond(
UnauthorizedResponse(
HttpAuthHeader.basicAuthChallenge(realm)
)
)
}
}val hashedUserTable = UserHashedTableAuth(
table = mapOf(
"admin" to decodeBase64("password_hash"),
"user" to decodeBase64("another_hash")
),
digester = getDigestFunction("SHA-256") { "ktor${it.length}" }
)
basic("auth-basic") {
realm = "Protected Area"
validate { credentials ->
hashedUserTable.authenticate(credentials)
}
}class BasicAuthenticationProvider internal constructor(
config: Config
) : AuthenticationProvider(config)
abstract class AuthenticationProvider(config: Config) {
abstract suspend fun onAuthenticate(context: AuthenticationContext)
open class Config(val name: String?) {
fun skipWhen(predicate: ApplicationCallPredicate)
}
}fun ApplicationRequest.basicAuthenticationCredentials(charset: Charset? = null): UserPasswordCredential?typealias AuthenticationFunction<C> = suspend ApplicationCall.(C) -> Any?typealias ChallengeFunction = suspend PipelineContext<*, ApplicationCall>.(
defaultScheme: String,
realm: String
) -> Unitbasic("database-auth") {
realm = "Database Protected"
validate { credentials ->
transaction {
Users.select { Users.username eq credentials.name }
.singleOrNull()
?.let { user ->
if (BCrypt.checkpw(credentials.password, user[Users.passwordHash])) {
UserIdPrincipal(credentials.name)
} else null
}
}
}
}// Allow both basic and bearer authentication
authenticate("auth-basic", "auth-bearer", strategy = AuthenticationStrategy.FirstSuccessful) {
get("/api/data") {
// Handle authenticated request
}
}Basic authentication failures are handled through the challenge system:
sealed class AuthenticationFailedCause {
object NoCredentials : AuthenticationFailedCause()
object InvalidCredentials : AuthenticationFailedCause()
class Error(val message: String) : AuthenticationFailedCause()
}The provider automatically returns appropriate HTTP status codes:
Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-server-auth-jvm