# How iroh Connection Hooks Work for Authentication and Authorization

> Learn how iroh connection hooks enable authentication and authorization by intercepting connection attempts. Implement before connect and after handshake callbacks to control peer access.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: deep-dive
- Published: 2026-06-24

---

**iroh connection hooks let you intercept and control connection attempts by implementing the `EndpointHooks` trait, which provides `before_connect` and `after_handshake` callbacks to reject unauthorized peers before or after the QUIC/TLS handshake completes.**

The n0-computer/iroh library exposes a hook-based interception system that allows applications to enforce authentication and authorization policies at the network layer. By implementing the `EndpointHooks` trait and registering hooks via the endpoint builder, you can inspect and abort connection attempts based on custom logic. This mechanism operates on verified identity data from the underlying QUIC/TLS security layer.

## The EndpointHooks Trait Lifecycle

Hooks in iroh are stored in an `EndpointHooksList` maintained by the endpoint builder. When you install hooks using `Builder::hooks`, they are invoked sequentially at specific connection lifecycle events. If any hook returns a **Reject** outcome, the remaining hooks are skipped and the connection terminates immediately.

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

### Intercepting Outgoing Connections with before_connect

The `before_connect` callback runs immediately before an outgoing connection attempt starts, triggered by `Endpoint::connect` or `connect_with_opts`. This hook receives the connection options and can return `BeforeConnectOutcome::Reject` to abort the attempt before any packets are transmitted.

This is useful for client-side rate limiting or preventing connections to blacklisted endpoints before network resources are consumed.

### Validating Established Connections with after_handshake

The `after_handshake` callback executes after the QUIC/TLS handshake completes for both inbound and outbound connections. At this point, the remote peer's identity (`EndpointId`) has been cryptographically verified by the underlying QUIC layer.

The hook can return `AfterHandshakeOutcome::Reject { error_code, reason }` to close the connection immediately with a specific error code. This is the primary mechanism for authorization, as it allows you to make decisions based on a trustworthy peer identifier.

## Building an Authentication System with Hooks

The [`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs) file demonstrates a complete pre-authentication flow using connection hooks. This pattern separates the authentication handshake (verifying a shared secret) from subsequent authorized connections.

### Server-Side Validation with IncomingAuthHook

The `IncomingAuthHook` struct implements `after_handshake` to enforce access control. It maintains an `allowed_remotes` set containing authenticated `EndpointId` values.

When a new connection arrives:

1. The hook checks if the remote's `EndpointId` exists in `allowed_remotes`
2. If present, it returns `Accept` and the connection proceeds
3. If absent, it returns `AfterHandshakeOutcome::Reject` with error code `403` and reason `"Unauthorized"`, causing immediate termination

Because this runs after the TLS handshake, the `EndpointId` is cryptographically verified and cannot be spoofed.

### The Authentication Protocol Handler

`AuthProtocol` is a custom protocol mounted on the router that handles the initial credential exchange. When a peer connects using a dedicated ALPN:

1. The protocol handler reads a shared secret token from a uni-directional stream
2. If the token matches the expected value, the remote's `EndpointId` is inserted into `allowed_remotes`
3. The connection is closed with status code `CLOSE_ACCEPTED`
4. If the token is invalid, the connection closes with `CLOSE_DENIED`

This separation allows the authentication logic to run as a distinct protocol while the connection hooks enforce the policy for all other traffic.

### Client-Side Authentication Flow

The outgoing side uses a helper function that implements the token submission:

1. Opens a uni-directional stream to the remote's authentication endpoint
2. Writes the shared secret token
3. Waits for the remote to close the connection with `CLOSE_ACCEPTED`
4. Only then proceeds to open normal connections

Once this completes, the server has added the client's `EndpointId` to `allowed_remotes`, so subsequent connections pass the `IncomingAuthHook` check.

## Complete Implementation Example

The following example shows how to wire together the authentication hook and protocol:

```rust
use iroh::endpoint::{Builder, EndpointHooks};
use iroh::example::auth_hook::{incoming, outgoing};

// Server setup
let (auth_hook, auth_proto) = incoming(b"shared-secret".to_vec());

let endpoint = Builder::default()
    .hooks(auth_hook)          // Install the IncomingAuthHook
    .router(auth_proto)        // Register the AuthProtocol handler
    .bind()
    .await?;

// Client setup
let (client_hook, token) = outgoing(b"shared-secret".to_vec());
let client = Builder::default()
    .hooks(client_hook)        // Install hook to send auth token
    .bind()
    .await?;

// After successful auth, this connection will be accepted
let conn = client.connect(endpoint.remote_id(), ALPN).await?;

```

This mirrors the implementation in [`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs), demonstrating the complete cycle from token exchange to authorized connection.

## Key Source Files and Implementation Details

The connection hook system is implemented across several core files:

- **[`iroh/src/endpoint/hooks.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/hooks.rs)** – Defines the `EndpointHooks` trait, `BeforeConnectOutcome` and `AfterHandshakeOutcome` enums, and the hook dispatch logic that handles early termination when rejection occurs.
- **[`iroh/examples/auth-hook.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs)** – Complete working example of `IncomingAuthHook`, `AuthProtocol`, and the outgoing authentication helper.
- **[`iroh/src/endpoint/builder.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/builder.rs)** – Contains the `Builder::hooks` method used to register `EndpointHooksList` with the endpoint.
- **[`iroh/src/endpoint/router.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/router.rs)** – Handles protocol registration and routing of inbound streams to the appropriate protocol handlers like `AuthProtocol`.

## Summary

- **iroh connection hooks** implement the `EndpointHooks` trait to intercept connections at `before_connect` or `after_handshake` phases.
- The `before_connect` hook can abort outgoing connections before any packets are sent by returning `BeforeConnectOutcome::Reject`.
- The `after_handshake` hook runs after cryptographic verification, providing a trustworthy `EndpointId` for authorization decisions.
- Authentication is typically implemented by pairing an `after_handshake` hook (for enforcement) with a custom protocol handler (for credential exchange).
- Hooks are registered via the endpoint builder and stored in an `EndpointHooksList` that processes them sequentially until completion or first rejection.

## Frequently Asked Questions

### When do iroh connection hooks execute relative to the QUIC handshake?

The `before_connect` hook executes before any network packets are sent for outgoing connections, while `after_handshake` runs immediately after the QUIC/TLS handshake completes. This means `after_handshake` has access to the cryptographically verified `EndpointId` of the remote peer, ensuring authorization decisions are based on authentic identity data.

### How do I reject a connection in a hook?

Return `BeforeConnectOutcome::Reject` from `before_connect` to stop outgoing connections before they start, or return `AfterHandshakeOutcome::Reject { error_code, reason }` from `after_handshake` to close the connection immediately after handshake completion. When any hook returns a rejection variant, the remaining hooks are skipped and the connection terminates.

### Can I implement rate limiting with connection hooks?

Yes. The `before_connect` hook is ideal for client-side rate limiting because it runs before network resources are allocated. You can track connection attempts per destination and return `BeforeConnectOutcome::Reject` to prevent new connections to specific endpoints that have exceeded limits.

### What identity information is available in the after_handshake hook?

The `after_handshake` hook receives the `EndpointId` of the remote peer, which has been verified during the TLS handshake. This identifier is cryptographically bound to the connection and cannot be spoofed, making it reliable for authorization checks against allowlists or authentication databases.