Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MercuryWorkshop/epoxy-tls/llms.txt

Use this file to discover all available pages before exploring further.

The password authentication extension lets a server require that clients prove their identity with a username and password before any streams are created. Both sides exchange credentials as part of the Wisp v2 handshake, before the mux is returned to application code. The server rejects the connection immediately if the credentials do not match a known user.

Extension details

FieldValue
Extension ID0x02
Extension enumPasswordProtocolExtension
Builder enumPasswordProtocolExtensionBuilder
Feature gatenone (always available)
Passwords are sent in plain text as part of the Wisp handshake payload. The Wisp protocol itself does not encrypt credentials. In practice, epoxy-client tunnels Wisp over a TLS WebSocket, which protects credentials in transit — but you must ensure your transport layer provides encryption before relying on this extension for security.

Client setup

Use PasswordProtocolExtensionBuilder::new_client to create the client-side builder. Pass Some((username, password)) to provide credentials, or None if you are on a server that has marked the extension as optional.
use wisp_mux::extensions::{
    password::{PasswordProtocolExtension, PasswordProtocolExtensionBuilder},
    AnyProtocolExtensionBuilder,
};

let username = "alice".to_string();
let password = "s3cr3t".to_string();

let auth_builder = PasswordProtocolExtensionBuilder::new_client(Some((username, password)));

let extensions: Vec<AnyProtocolExtensionBuilder> = vec![
    AnyProtocolExtensionBuilder::new(auth_builder),
];
Then pass the extension list to WispV2Handshake and enforce that the server accepted it:
use wisp_mux::{ClientMux, WispV2Handshake};

let (mux, fut) = ClientMux::new(rx, tx, Some(WispV2Handshake::new(extensions)))
    .await?
    .with_required_extensions(&[PasswordProtocolExtension::ID])
    .await?;

Server setup

Use PasswordProtocolExtensionBuilder::new_server and provide a HashMap<String, String> mapping usernames to their expected passwords.
users
HashMap<String, String>
required
Map of allowed username → password pairs. The server checks the client’s submitted credentials against this map.
required
bool
required
When true, clients that do not send credentials are rejected with PasswordExtensionNoCreds. When false, unauthenticated clients are allowed to proceed.
use std::collections::HashMap;
use wisp_mux::extensions::{
    password::PasswordProtocolExtensionBuilder,
    AnyProtocolExtensionBuilder,
};

let mut users = HashMap::new();
users.insert("alice".to_string(), "s3cr3t".to_string());
users.insert("bob".to_string(), "hunter2".to_string());

let server_builder = PasswordProtocolExtensionBuilder::new_server(users, true);

let extensions: Vec<AnyProtocolExtensionBuilder> = vec![
    AnyProtocolExtensionBuilder::new(server_builder),
];

Error handling

Error variantWhen it occurs
WispError::PasswordExtensionCredsInvalidThe client sent credentials but the username or password did not match any entry in the server’s user map
WispError::PasswordExtensionNoCredsThe client was created with new_client(None) and has no credentials to send when the handshake requires them
Both errors are returned from ClientMux::new / the server handshake future, before the mux is available to application code.

Full example

use std::collections::HashMap;
use wisp_mux::{
    extensions::{
        password::{PasswordProtocolExtension, PasswordProtocolExtensionBuilder},
        AnyProtocolExtensionBuilder,
    },
    ClientMux, WispV2Handshake,
};

// Credentials from the simple-wisp-client --auth flag (format: "username:password")
let auth_str = "alice:s3cr3t";
let split: Vec<_> = auth_str.split(':').collect();
let username = split[0].to_string();
let password = split[1..].join(":");

let extensions: Vec<AnyProtocolExtensionBuilder> = vec![
    AnyProtocolExtensionBuilder::new(
        PasswordProtocolExtensionBuilder::new_client(Some((username, password))),
    ),
];

let (mux, fut) = ClientMux::new(rx, tx, Some(WispV2Handshake::new(extensions)))
    .await?
    .with_required_extensions(&[PasswordProtocolExtension::ID])
    .await?;
Credentials can also be set or changed after the builder is created using set_creds((username, password)). This is useful when credentials are determined asynchronously after the builder is constructed but before the handshake begins.

Build docs developers (and LLMs) love