# How to Use Iroh Connection Hooks for Custom Authentication

> Learn to use iroh connection hooks for custom authentication. Intercept connections before handshake or after identity verification to reject unauthorized sessions. Implement EndpointHooks trait and register your hook.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-07-11

---

**Implement the `EndpointHooks` trait and register your hook via `Endpoint::builder().hooks()` to intercept connections before the QUIC handshake (`before_connect`) or after identity verification (`after_handshake`), returning `Reject` to abort unauthorized sessions.**

The n0-computer/iroh framework provides a pluggable hook system that lets you inspect and control the connection lifecycle. By leveraging **iroh connection hooks for custom authentication**, you can embed token validation, secret verification, or external credential checks directly into the QUIC handshake flow without modifying core library code.

## How Connection Hooks Work in Iroh

Iroh’s endpoint architecture executes registered hooks at specific lifecycle points. Hooks are stored in an `EndpointHooksList` and invoked sequentially, allowing you to build defense-in-depth authentication strategies.

### Hook Registration and Execution Order

Attach hooks during `Endpoint` construction using the builder method. Hooks execute in the order they were added, and any hook can abort the connection by returning a `Reject` variant, preventing subsequent hooks from running.

### Key Hook Points

The `EndpointHooks` trait in [`iroh/src/endpoint/hooks.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/hooks.rs) defines two async callbacks:

- **`before_connect(&self, remote_addr: &SocketAddr, alpn: &str) -> BeforeConnectOutcome`** – Invoked before the QUIC handshake starts. Use this to filter by IP address or ALPN protocol early.
- **`after_handshake(&self, conn: &Connection) -> AfterHandshakeOutcome`** – Invoked after the handshake completes and the remote endpoint’s identity is cryptographically verified. This provides access to the authenticated `Connection` object for validation.

Both methods return `Accept` or `Reject` outcomes defined in `BeforeConnectOutcome` and `AfterHandshakeOutcome` enums.

## Implementing a Custom Authentication Hook

To build custom authentication, define a struct holding your validation state and implement the `EndpointHooks` trait.

### Define the Hook Structure

Store any state required for validation, such as a shared secret, JWT validator, or database client.

```rust
use iroh::endpoint::{EndpointHooks, BeforeConnectOutcome, AfterHandshakeOutcome, Connection};

struct MyAuthHook {
    secret: Vec<u8>,
}

```

### Implement the EndpointHooks Trait

Provide async implementations for both hook points. The `after_handshake` method receives a `&Connection` containing the remote endpoint ID via `conn.remote_id()`, which you can use to validate tokens sent over a dedicated QUIC stream.

```rust
use async_trait::async_trait;
use std::net::SocketAddr;

#[async_trait]
impl EndpointHooks for MyAuthHook {
    async fn before_connect(
        &self,
        _remote_addr: &SocketAddr,
        _alpn: &str,
    ) -> BeforeConnectOutcome {
        // Optionally reject by IP or ALPN before crypto overhead
        BeforeConnectOutcome::Accept
    }

    async fn after_handshake(&self, conn: &Connection) -> AfterHandshakeOutcome {
        let remote_id = conn.remote_id();
        
        // Tokens are typically sent via a dedicated QUIC stream after handshake
        let token = match receive_auth_token(conn).await {
            Ok(t) => t,
            Err(_) => return AfterHandshakeOutcome::Reject,
        };

        if token == self.secret {
            AfterHandshakeOutcome::Accept
        } else {
            AfterHandshakeOutcome::Reject
        }
    }
}

async fn receive_auth_token(conn: &Connection) -> anyhow::Result<Vec<u8>> {
    // Open a stream or accept an incoming stream to exchange tokens
    // Implementation depends on your ALPN protocol
    todo!()
}

```

**Note:** Iroh does not prescribe how authentication tokens are transported. Typically, you open a dedicated QUIC stream after the handshake completes and exchange a small payload before the hook returns `Accept`.

## Installing the Hook on the Server Side

Register your hook during endpoint instantiation. Every incoming connection will flow through your authentication logic.

```rust
let secret = b"my-shared-secret".to_vec();
let auth_hook = MyAuthHook { secret };

let endpoint = iroh::Endpoint::builder()
    .bind_addr("[::]:0".parse().unwrap())
    .hooks(auth_hook)  // Install the custom auth hook
    .build()
    .await?;

```

If the hook returns `Reject`, the connection closes immediately and the remote peer receives a handshake abort error.

## Client-Side Authentication Patterns

For mutual authentication, create a paired hook system:

- **Outgoing hook:** Sends the authentication token once the connection is established.
- **Incoming hook:** Validates the token as shown above.

The repository’s [`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs) demonstrates this pattern with helper functions `auth::outgoing` and `auth::incoming` that return `(hook, task)` tuples. You can adapt these helpers to use JWTs, API keys, or TLS-derived secrets.

## Source Code Reference

The following files in the `n0-computer/iroh` repository contain the relevant implementations:

| Feature | File | Purpose |
|---------|------|---------|
| Hook trait definition | [`iroh/src/endpoint/hooks.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/hooks.rs) | Defines `EndpointHooks`, `BeforeConnectOutcome`, and `AfterHandshakeOutcome` |
| Builder integration | [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) | Contains `Builder::hooks` method and `EndpointHooksList` storage |
| Connection handling | [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) | Invokes `before_connect` and `after_handshake` at lifecycle points |
| Authentication example | [`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs) | Full runnable demo of outgoing and incoming auth hooks |
| Per-connection state | [`iroh/examples/remote-info.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/remote-info.rs) | Shows how to maintain state across hook invocations |
| Connection monitoring | [`iroh/examples/monitor-connections.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/monitor-connections.rs) | Demonstrates diagnostic hooks for logging connection details |

## Summary

- **Register** custom logic by implementing `EndpointHooks` and passing it to `Endpoint::builder().hooks()`.
- **Intercept early** using `before_connect` to filter by IP or ALPN before cryptographic overhead.
- **Validate identity** in `after_handshake` where the remote endpoint ID is cryptographically verified.
- **Abort unauthorized** connections by returning `AfterHandshakeOutcome::Reject` to close the connection immediately.
- **Combine hooks** for mutual authentication, using outgoing hooks on clients and incoming hooks on servers.

## Frequently Asked Questions

### Can I reject connections based on IP address before the QUIC handshake starts?

Yes. Implement the `before_connect` method in your `EndpointHooks` trait and inspect the `remote_addr` parameter. Return `BeforeConnectOutcome::Reject` to block the connection before any cryptographic operations occur, saving CPU resources.

### How do I transmit authentication tokens if the hook doesn't handle transport?

The hook only decides whether to accept or reject; it does not handle wire protocol. Typically, you negotiate a custom ALPN and exchange tokens over a dedicated QUIC stream opened immediately after the handshake. The `after_handshake` hook can then await this data before returning its outcome.

### What happens when multiple hooks are registered and one returns Reject?

Hooks execute in registration order. If any hook returns `Reject`, the connection aborts immediately and subsequent hooks in the `EndpointHooksList` are not invoked. The remote peer receives an error indicating the handshake was aborted.

### Can I use connection hooks for purposes other than authentication?

Yes. The hook system supports any connection lifecycle interception, including logging, metrics collection, rate limiting, or dynamic capability negotiation. The [`iroh/examples/monitor-connections.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/monitor-connections.rs) example demonstrates using hooks purely for diagnostic logging.