package {{packageName}}.infrastructure import io.ktor.application.ApplicationCall import io.ktor.application.call import io.ktor.auth.* import io.ktor.request.ApplicationRequest import io.ktor.response.respond import io.ktor.application.* import io.ktor.pipeline.* import io.ktor.request.* import io.ktor.response.* import java.util.* enum class ApiKeyLocation(val location: String) { QUERY("query"), HEADER("header") } data class ApiKey(val value: String): Credential data class ApiPrincipal(val apiKey: ApiKey?) : Principal fun ApplicationCall.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? = request.apiKey(key, keyLocation) fun ApplicationRequest.apiKey(key: String, keyLocation: ApiKeyLocation = ApiKeyLocation.valueOf("header")): ApiKey? { val value: String? = when(keyLocation) { ApiKeyLocation.QUERY -> this.queryParameters[key] ApiKeyLocation.HEADER -> this.headers[key] } when (value) { null -> return null else -> return ApiKey(value) } } fun AuthenticationPipeline.apiKeyAuth(apiKeyName: String, authLocation: String, validate: suspend (ApiKey) -> ApiPrincipal?) { intercept(AuthenticationPipeline.RequestAuthentication) { context -> val credentials = call.request.apiKey(apiKeyName, ApiKeyLocation.values().first { it.location == authLocation }) val principal = credentials?.let { validate(it) } val cause = when { credentials == null -> AuthenticationFailedCause.NoCredentials principal == null -> AuthenticationFailedCause.InvalidCredentials else -> null } if (cause != null) { context.challenge(apiKeyName, cause) { // TODO: Verify correct response structure here. call.respond(UnauthorizedResponse(HttpAuthHeader.Parameterized("API_KEY", mapOf("key" to apiKeyName), HeaderValueEncoding.QUOTED_ALWAYS))) it.complete() } } if (principal != null) { context.principal(principal) } } }