
kotlin-server.libraries.ktor.ApiKeyAuth.kt.mustache Maven / Gradle / Ivy
package {{packageName}}.infrastructure
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.auth.*
import io.ktor.request.*
import io.ktor.response.*
enum class ApiKeyLocation(val location: String) {
QUERY("query"),
HEADER("header")
}
data class ApiKeyCredential(val value: String) : Credential
data class ApiPrincipal(val apiKeyCredential: ApiKeyCredential?) : Principal
/**
* Represents a Api Key authentication provider
* @param name is the name of the provider, or `null` for a default provider
*/
class ApiKeyAuthenticationProvider(configuration: Configuration) : AuthenticationProvider(configuration) {
private val authenticationFunction = configuration.authenticationFunction
private val apiKeyName: String = configuration.apiKeyName
private val apiKeyLocation: ApiKeyLocation = configuration.apiKeyLocation
internal fun install() {
pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
val credentials = call.request.apiKeyAuthenticationCredentials(apiKeyName, apiKeyLocation)
val principal = credentials?.let { authenticationFunction(call, it) }
val cause = when {
credentials == null -> AuthenticationFailedCause.NoCredentials
principal == null -> AuthenticationFailedCause.InvalidCredentials
else -> null
}
if (cause != null) {
context.challenge(apiKeyName, cause) {
call.respond(
UnauthorizedResponse(
HttpAuthHeader.Parameterized(
"API_KEY",
mapOf("key" to apiKeyName),
HeaderValueEncoding.QUOTED_ALWAYS
)
)
)
it.complete()
}
}
if (principal != null) {
context.principal(principal)
}
}
}
class Configuration internal constructor(name: String?) : AuthenticationProvider.Configuration(name) {
internal var authenticationFunction: suspend ApplicationCall.(ApiKeyCredential) -> Principal? = {
throw NotImplementedError(
"Api Key auth validate function is not specified. Use apiKeyAuth { validate { ... } } to fix."
)
}
var apiKeyName: String = ""
var apiKeyLocation: ApiKeyLocation = ApiKeyLocation.QUERY
/**
* Sets a validation function that will check given [ApiKeyCredential] instance and return [Principal],
* or null if credential does not correspond to an authenticated principal
*/
fun validate(body: suspend ApplicationCall.(ApiKeyCredential) -> Principal?) {
authenticationFunction = body
}
}
}
fun Authentication.Configuration.apiKeyAuth(
name: String? = null,
configure: ApiKeyAuthenticationProvider.Configuration.() -> Unit
) {
val configuration = ApiKeyAuthenticationProvider.Configuration(name).apply(configure)
val provider = ApiKeyAuthenticationProvider(configuration)
provider.install()
register(provider)
}
fun ApplicationRequest.apiKeyAuthenticationCredentials(
apiKeyName: String,
apiKeyLocation: ApiKeyLocation
): ApiKeyCredential? {
val value: String? = when (apiKeyLocation) {
ApiKeyLocation.QUERY -> this.queryParameters[apiKeyName]
ApiKeyLocation.HEADER -> this.headers[apiKeyName]
}
return when (value) {
null -> null
else -> ApiKeyCredential(value)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy