Authentication
Sign private, presence, encrypted, and user authentication requests with trusted server SDKs.
Sockudo never trusts client-provided access to protected channels. Clients request a subscription, your application server decides whether the user is allowed, then the server returns a signed response.
Channel authorization flow
- Client subscribes to
private-ordersorpresence-room. - The client SDK sends
socket_idandchannel_nameto your auth endpoint. - Your backend validates the current user and channel policy.
- A server SDK signs the response using the app secret.
- Sockudo validates the signature and completes the subscription.
Node.js auth endpoint
import express from "express";
import { Sockudo } from "sockudo";
const app = express();
app.use(express.urlencoded({ extended: false }));
const sockudo = new Sockudo({
appId: process.env.SOCKUDO_APP_ID!,
key: process.env.SOCKUDO_APP_KEY!,
secret: process.env.SOCKUDO_APP_SECRET!,
host: "127.0.0.1",
port: 6001,
useTLS: false,
});
app.post("/sockudo/auth", (req, res) => {
const { socket_id, channel_name } = req.body;
const user = requireUser(req);
if (!canAccessChannel(user, channel_name)) {
return res.status(403).json({ error: "Forbidden" });
}
if (channel_name.startsWith("presence-")) {
return res.json(
sockudo.authorizeChannel(socket_id, channel_name, {
user_id: user.id,
user_info: { name: user.name, role: user.role },
}),
);
}
return res.json(sockudo.authorizeChannel(socket_id, channel_name));
});Client configuration
import Sockudo from "@sockudo/client";
const client = new Sockudo("app-key", {
wsHost: "127.0.0.1",
wsPort: 6001,
forceTLS: false,
channelAuthorization: {
endpoint: "/sockudo/auth",
},
});
client.subscribe("private-orders");User authentication
User authentication signs a connection identity. It powers watchlists and user-targeted server events.
app.post("/sockudo/user-auth", (req, res) => {
const user = requireUser(req);
res.json(
sockudo.authenticateUser(req.body.socket_id, {
id: user.id,
user_info: { name: user.name },
}),
);
});Protocol V2 capability tokens
Protocol V2 clients may authenticate the WebSocket connection with a short-lived capability token:
const client = new Sockudo("app-key", {
protocolVersion: 2,
auth: {
endpoint: "/sockudo/token",
},
});Your backend issues the JWT after authenticating the user. Never ship the app secret to browsers, mobile apps, or untrusted clients.
Node.js example:
import jwt from "jsonwebtoken";
import crypto from "node:crypto";
app.post("/sockudo/token", (req, res) => {
const user = requireUser(req);
const now = Math.floor(Date.now() / 1000);
const capability = {
"private-orders:*": ["subscribe", "history"],
"presence-orders:*": ["subscribe", "presence"],
"private-orders:input": ["publish"],
};
const token = jwt.sign(
{
"x-sockudo-capability": JSON.stringify(capability),
"x-sockudo-client-id": user.id,
iat: now,
exp: now + 3600,
jti: crypto.randomUUID(),
},
process.env.SOCKUDO_APP_SECRET!,
{
algorithm: "HS256",
header: { kid: process.env.SOCKUDO_APP_KEY! },
},
);
res.json({ token });
});Rust example:
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
use serde::Serialize;
use std::collections::BTreeMap;
#[derive(Serialize)]
struct Claims {
#[serde(rename = "x-sockudo-capability")]
capability: String,
#[serde(rename = "x-sockudo-client-id")]
client_id: String,
iat: i64,
exp: i64,
jti: String,
}
fn issue_token(app_key: &str, app_secret: &str, client_id: &str) -> anyhow::Result<String> {
let now = chrono::Utc::now().timestamp();
let mut capability = BTreeMap::new();
capability.insert("private-orders:*", vec!["subscribe", "history"]);
let mut header = Header::new(Algorithm::HS256);
header.kid = Some(app_key.to_owned());
Ok(encode(
&header,
&Claims {
capability: serde_json::to_string(&capability)?,
client_id: client_id.to_owned(),
iat: now,
exp: now + 3600,
jti: uuid::Uuid::new_v4().to_string(),
},
&EncodingKey::from_secret(app_secret.as_bytes()),
)?)
}Token rules:
- Header
kidmust equal the Sockudo app key andalgmust beHS256. x-sockudo-capabilityis a stringified JSON map from channel pattern to operations.- Operations are
publish,subscribe,history, andpresence. - Patterns are exact names, namespace prefixes such as
orders:*, or*; matching is case-sensitive. x-sockudo-client-idis the verified identity used for presence and user-limited channels.iat,exp, andjtiare required. Tokens may live at most 24 hours; 1 hour or less is recommended.- Clients refresh in place with
sockudo:authcarrying{ "token": "..." }. Expired tokens emitsockudo:token_expiredwith code40142and close after a 30 second grace window.
Encrypted channels
Encrypted channels start with private-encrypted-. The server signs the subscription and returns a per-channel shared secret derived from your encryption master key.
const sockudo = new Sockudo({
appId: "app-id",
key: "app-key",
secret: "app-secret",
host: "127.0.0.1",
port: 6001,
encryptionMasterKeyBase64: process.env.SOCKUDO_ENCRYPTION_MASTER_KEY!,
});Keep the master key outside the browser. Client SDKs only receive the derived shared secret for authorized encrypted channels.
Push authorization boundary
Push registration and push publish helpers are also authentication boundaries. Mobile and browser clients should call your backend proxy, not Sockudo directly with app secrets.
app.post("/sockudo/push", async (req, res) => {
const user = requireUser(req);
await assertDeviceBelongsToUser(user, req.body.device_id);
const response = await sockudo.publishPush({
recipients: [{ type: "client", client_id: user.id }],
payload: req.body.payload,
sync: false,
});
res.status(202).json(response);
});Security checklist
- Read
socket_idandchannel_namefrom the request body; do not accept them from query parameters unless your framework requires it. - Authenticate the user before signing anything.
- Enforce channel ownership and tenant boundaries before calling the SDK signing helper.
- Return presence user data that is safe for all channel members to see.
- Keep app secrets, webhook secrets, push provider credentials, and encryption master keys server-side only.
- Issue capability tokens from a backend endpoint only; do not mint them in client code.
- Use short request timeouts and structured audit logs for denied auth attempts.