com.authdog:authdog-ktor resolves Authdog sessions in Ktor servers. It reads a cookie or bearer token, checks it through the environment's OIDC userinfo endpoint, and gives route handlers an authentication context and gate.
Availability
Unverified release: the SDK README says the artifact is published to Maven Central as com.authdog:authdog-ktor (version 0.1.0), but no maven-metadata.xml for it was found on Maven Central at the time of writing. If Gradle cannot resolve it, build from the `packages/kotlin` directory of the web SDK repository.
Install
// build.gradle.kts
dependencies {
implementation("com.authdog:authdog-ktor:0.1.0")
}The module is built against Ktor 3.5.1 and Kotlin 2.2.0 on JVM 21. It uses the Ktor CIO client for userinfo lookups unless you inject your own.
Configure
Create one Authdog instance with your environment's public key (pk_...):
import com.authdog.Authdog
val authdog = Authdog(System.getenv("PK_AUTHDOG"))The key is safe to expose. It is validated at construction; a malformed key or an identity host outside the trusted HTTPS allowlist throws PublicKeyException immediately. Extra host suffixes can be added with the comma-separated AUTHDOG_ALLOWED_IDENTITY_HOSTS environment variable.
| Parameter | Default | Notes |
|---|---|---|
publicKey |
required | Environment public key (pk_...). |
fetchUser |
true |
When false, resolve surfaces the token but leaves isAuthenticated false. |
client |
HttpClient(CIO) |
Optional Ktor HttpClient for userinfo, for example a MockEngine in tests. |
Resolve the session
authdog.resolve(call) prefers the Authorization: Bearer <token> header, then the authdog-session cookie. A valid token requires a successful userinfo response (meta.code == 200 with a non-null user). It never throws: missing, invalid, or unverifiable tokens, and failed or untrusted lookups, produce an unauthenticated context. The result is cached on the call, so several gates cost one userinfo request.
data class AuthdogContext(
val token: String? = null,
val user: JsonElement? = null,
val isAuthenticated: Boolean = false,
val userInfo: UserInfoResponse? = null,
)get("/session") {
val ctx = authdog.resolve(call)
call.respondText(if (ctx.isAuthenticated) "signed in" else "anonymous")
}With fetchUser = false, the token is surfaced but user stays null and isAuthenticated stays false, so requireAuth responds 401. Use this only when separate server-side validation and enforcement replace Authdog's gate.
Protect a route
requireAuth is the security boundary. It responds 401 {"error":"Unauthorized"} (JSON) and returns null when the session is not authenticated; otherwise it returns the context:
get("/me") {
val ctx = authdog.requireAuth(call) ?: return@get
call.respondText(ctx.user.toString())
}Authentication identifies a caller; it does not authorize actions. Apply your authorization checks after requireAuth. Client-side checks never protect server routes.
Sign out
logout expires the authdog-session cookie (HttpOnly, SameSite=Lax, path /) and issues a temporary redirect (302) to a sanitized same-origin redirect_uri query parameter, falling back to /:
get("/logout") { authdog.logout(call) }This does not revoke a bearer token or end an upstream identity-provider session.