# How to Implement Connection Authentication and Authorization in iroh

> Learn to implement connection authentication and authorization in iroh using EndpointHooks. Secure your iroh network by exchanging tokens and tracking verified peers effectively.

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

---

**Implement connection authentication and authorization in iroh by leveraging the `EndpointHooks` trait to intercept connections before and after the TLS handshake, using a dedicated authentication protocol to exchange tokens and a shared state to track verified peers.**

The iroh networking library provides a flexible, hook-based extension point that allows developers to implement custom connection authentication and authorization without modifying core protocol handlers. By implementing the **`EndpointHooks`** trait, you can inspect every connection attempt and reject unauthorized peers before they reach your application logic. This approach enables you to secure existing iroh protocols—such as file transfer or NAT traversal—by simply mounting authentication hooks on your **`Endpoint`** builder.

## Understanding the EndpointHooks Architecture

The authentication system in iroh operates through the **`EndpointHooks`** trait defined in [`iroh/src/protocol.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/protocol.rs). This trait provides two critical interception points: **`before_connect`** for outbound connections and **`after_handshake`** for inbound connections.

### The before_connect Hook

The `before_connect` method runs when your endpoint attempts to connect to a remote peer. It receives the **`EndpointAddr`** and ALPN identifier, allowing you to implement pre-authentication logic. According to the implementation in [`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs), this hook can return **`BeforeConnectOutcome::Accept`** to proceed or **`BeforeConnectOutcome::Reject`** to terminate the connection before any packets are exchanged.

### The after_handshake Hook

For incoming connections, the `after_handshake` method executes immediately after the TLS handshake completes. This hook receives the **`Connection`** object and can return **`AfterHandshakeOutcome::Accept`** or **`AfterHandshakeOutcome::Reject`**. This is where you verify whether the remote peer has previously authenticated through your custom protocol.

## Building a Token-Based Authentication Flow

The reference implementation in [`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs) demonstrates a complete token-based pre-authentication design. The flow works as follows:

1. **Setup** – Both sides create auth hooks (`incoming` for server, `outgoing` for client) and attach them to the endpoint builder.
2. **Router configuration** – The server registers the **`AuthProtocol`** for the auth ALPN alongside application protocols.
3. **Connection** – When `Endpoint::connect` is called, the **`OutgoingAuthHook`** checks the `allowed_remotes` set. If the remote is not authenticated, it spawns a temporary connection using the auth ALPN (`b"iroh-example/auth/0"`) to exchange the secret token.

### OutgoingAuthHook and OutgoingAuthTask

The **`OutgoingAuthHook`** implements `EndpointHooks::before_connect`. When your application calls `Endpoint::connect`, this hook checks whether the remote **`EndpointId`** exists in a shared **`allowed_remotes`** set. If not, it sends a request to the **`OutgoingAuthTask`**, which initiates a secondary connection using the dedicated authentication ALPN to exchange the secret token.

### AuthProtocol Handler

The **`AuthProtocol`** is a standard **`ProtocolHandler`** registered on the router that listens for the authentication ALPN. When a client connects, it reads the token from a unidirectional stream and compares it against the expected secret. On success, it inserts the remote's `EndpointId` into the `allowed_remotes` set and closes the connection with a success code.

### IncomingAuthHook

The **`IncomingAuthHook`** implements `EndpointHooks::after_handshake`. After the TLS handshake completes, this hook verifies whether the remote peer exists in the `allowed_remotes` set. If the peer is unauthenticated and the current connection is not the authentication protocol itself, the hook returns `AfterHandshakeOutcome::Reject`, preventing unauthorized access to application protocols.

## Implementation Example

The following code demonstrates how to set up a server that requires token-based authentication before allowing connections to an application protocol. This example uses the `auth::incoming` function from the reference implementation.

```rust
use iroh::{Endpoint, endpoint::presets, protocol::Router};
use iroh::examples::auth;

// Initialize the incoming hook with a secret token
let (in_hook, auth_proto) = auth::incoming(b"super-secret".to_vec());

// Build the endpoint with the authentication hook
let server_ep = Endpoint::builder(presets::N0)
    .hooks(in_hook)
    .bind()
    .await?;

// Create router that accepts auth protocol and application protocol
let router = Router::builder(server_ep)
    .accept(auth::ALPN, auth_proto)
    .accept(echo::ALPN, Echo)
    .spawn();

```

The client setup requires mounting the outgoing hook and spawning the background authentication task:

```rust
let (out_hook, out_task) = auth::outgoing(b"super-secret".to_vec());

let client_ep = Endpoint::builder(presets::N0)
    .hooks(out_hook)
    .bind()
    .await?;

// Spawn the background authentication task
let _guard = out_task.spawn(client_ep.clone());

// Connect to server - authentication happens transparently
Echo::connect(&client_ep, server_addr, b"hello").await?;

```

## Hook Implementation Details

The actual hook logic in [`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs) handles edge cases such as skipping authentication when the auth ALPN itself is requested. This prevents the authentication protocol from requiring its own authentication, avoiding circular dependencies.

Outgoing hook logic:

```rust
impl EndpointHooks for OutgoingAuthHook {
    async fn before_connect<'a>(
        &'a self,
        remote_addr: &'a EndpointAddr,
        alpn: &'a [u8],
    ) -> BeforeConnectOutcome {
        // Allow auth protocol connections without additional checks
        if alpn == auth::ALPN {
            return BeforeConnectOutcome::Accept;
        }

        // Check if remote is pre-authenticated
        match self.authenticate(remote_addr.id).await {
            Ok(()) => BeforeConnectOutcome::Accept,
            Err(_) => BeforeConnectOutcome::Reject,
        }
    }
}

```

Auth protocol implementation:

```rust
impl ProtocolHandler for AuthProtocol {
    async fn accept(&self, connection: Connection) -> Result<(), AcceptError> {
        let mut stream = connection.accept_uni().await?;
        let token = stream.read_to_end(256).await?;
        
        if token == self.token {
            self.allowed_remotes.lock().unwrap()
                .insert(connection.remote_id());
            connection.close(CLOSE_ACCEPTED.into(), b"accepted");
        } else {
            connection.close(CLOSE_DENIED.into(), b"rejected");
        }
        Ok(())
    }
}

```

## Key Source Files

The authentication mechanism relies on the following files in the n0-computer/iroh repository. Understanding these modules helps you customize the hook behavior for your specific security requirements.

- **[`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs)** – Complete reference implementation showing the `OutgoingAuthHook`, `IncomingAuthHook`, `OutgoingAuthTask`, and `AuthProtocol` working together.
- **[`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)** – Defines `EndpointBuilder` and the `hooks` method used to install custom `EndpointHooks`.
- **[`iroh/src/protocol.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/protocol.rs)** – Contains the `EndpointHooks` trait, `ProtocolHandler` trait, and outcome enums (`BeforeConnectOutcome`, `AfterHandshakeOutcome`).
- **[`iroh/src/runtime.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/runtime.rs)** – Provides async runtime utilities used by `OutgoingAuthTask` for managing background tasks.

## Summary

- **Mount `EndpointHooks`** on your `Endpoint` builder to intercept connections at `before_connect` (outbound) and `after_handshake` (inbound) phases.
- **Implement a custom `ProtocolHandler`** for the authentication ALPN to exchange tokens and populate a shared `allowed_remotes` set.
- **Return `Reject` outcomes** from your hooks to block unauthorized connections before they reach application protocols.
- **Reference [`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs)** for the complete token-based authentication implementation.

## Frequently Asked Questions

### Can I use this hook system with existing iroh protocols?

Yes. Because authentication operates at the endpoint level through hooks, you can secure any existing iroh protocol—including file transfer or NAT traversal—without modifying the protocol implementation itself. Simply mount the hooks on your `Endpoint` builder and register the authentication protocol alongside your application protocols.

### What happens if the authentication token is incorrect?

If the token verification fails in the `AuthProtocol` handler, the connection is closed with a rejection code and the remote `EndpointId` is not added to the `allowed_remotes` set. Subsequent connection attempts from that peer will be rejected by the `IncomingAuthHook` returning `AfterHandshakeOutcome::Reject`, or by the `OutgoingAuthHook` failing its internal check.

### How does the outbound hook avoid infinite loops when connecting to the auth service?

The `OutgoingAuthHook` checks if the current connection's ALPN matches the authentication protocol's ALPN (`b"iroh-example/auth/0"`). If it does, the hook immediately returns `BeforeConnectOutcome::Accept`, allowing the authentication connection to proceed without attempting recursive pre-authentication.

### Where is the authenticated peer state stored?

The authenticated peer state is stored in a thread-safe shared data structure, typically a `Mutex<HashSet<EndpointId>>` wrapped in an `Arc`, and shared between the protocol handler and the hooks. In the reference implementation, this is the `allowed_remotes` field that tracks which peers have successfully presented valid tokens.