`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](https://github.com/authdog/web-sdk/tree/main/packages/kotlin) of the web SDK repository.

## Install

```kotlin
// 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_...`):

```kotlin
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.

```kotlin
data class AuthdogContext(
    val token: String? = null,
    val user: JsonElement? = null,
    val isAuthenticated: Boolean = false,
    val userInfo: UserInfoResponse? = null,
)
```

```kotlin
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:

```kotlin
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](/docs/concepts/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 `/`:

```kotlin
get("/logout") { authdog.logout(call) }
```

This does not revoke a bearer token or end an upstream identity-provider session.

## Next steps

- [Backend requests](/docs/backend)
- [Fastify](/docs/backend/fastify)
- [Express](/docs/backend/express)
- [Calling the REST API](/docs/api)
