Auth & Signature
How to authenticate channels and sign REST API requests.
Auth & Signature
Private and presence channels require authentication. When a client subscribes, the Pusher SDK sends a request to your authEndpoint. Your server validates the user and returns a signed response.
In Socketo Cloud, use your application key as the WebSocket client key and REST
auth_key. ReplaceAPP_KEYwith that key in the examples below.
Auth Flow
- Client subscribes to a private/presence channel
- SDK sends auth request to your
authEndpoint - Your server validates the user and generates a signature
- Your server returns
{ auth, channel_data? } - SDK sends auth to Socketo to complete subscription
Auth Response Format
Private Channel
{
"auth": "APP_KEY:HMAC_SIGNATURE"
}Presence Channel
{
"auth": "APP_KEY:HMAC_SIGNATURE",
"channel_data": "{\"user_id\":\"123\",\"user_info\":{\"name\":\"Alice\"}}"
}Generating the Signature
The signature is an HMAC-SHA256 hash using your App Secret.
Signature String
For private channels:
SOCKET_ID:CHANNEL_NAMEFor presence channels:
SOCKET_ID:CHANNEL_NAME:CHANNEL_DATAWhere CHANNEL_DATA is the JSON string of channel_data.
Example
import crypto from 'node:crypto'
function generateAuth(socketId, channelName, appKey, appSecret, channelData) {
let stringToSign = `${socketId}:${channelName}`
if (channelData) stringToSign += `:${channelData}`
const signature = crypto
.createHmac('sha256', appSecret)
.update(stringToSign)
.digest('hex')
return {
auth: `${appKey}:${signature}`,
...(channelData && { channel_data: channelData }),
}
}For full documentation and examples in other languages, see the Pusher auth docs.
User Authentication (Signin)
In addition to per-channel authentication, users can be authenticated at the WebSocket connection level. This associates a user_id with the connection, enabling presence channel features and top-level user_id metadata on presence client events.
Signin Request
{
"event": "pusher:signin",
"data": {
"auth": "APP_KEY:HMAC_SIGNATURE",
"user_data": "{\"id\":\"user123\",\"user_info\":{\"name\":\"Alice\"}}"
}
}Signature String
The signin signature is an HMAC-SHA256 hash using your App Secret, with the following string:
SOCKET_ID::user::USER_DATAWhere USER_DATA is the raw JSON string passed as user_data in the request.
The ::user:: segment belongs to the string being signed. It is not part of
the returned auth value.
Signin Success Response
{
"event": "pusher:signin_success",
"data": {
"user_data": "{\"id\":\"user123\",\"user_info\":{\"name\":\"Alice\"}}"
}
}Signin associates the connection with the authenticated user. The client can
receive server-to-user events through #server-to-user-<user_id> after signin.
Generating the Auth
import crypto from 'node:crypto'
function generateSigninAuth(socketId, appKey, appSecret, userData) {
const stringToSign = `${socketId}::user::${userData}`
const signature = crypto
.createHmac('sha256', appSecret)
.update(stringToSign)
.digest('hex')
return {
auth: `${appKey}:${signature}`,
user_data: userData,
}
}
// Usage
const userData = JSON.stringify({
id: 'user123',
user_info: { name: 'Alice' },
})
const auth = generateSigninAuth(socketId, appKey, appSecret, userData)
// Send via WebSocket: { event: 'pusher:signin', data: auth }When using pusher-js, configure its userAuthentication endpoint and call
pusher.signin(). The SDK obtains auth and user_data from that endpoint;
signin() does not take those values as arguments.
REST API Signing
Server-to-server REST API requests (such as triggering events) use a different signing scheme from channel authentication. The signature is sent as a query parameter.
Required Query Parameters
| Parameter | Description |
|---|---|
auth_key | Your application key |
auth_timestamp | Current Unix timestamp in seconds. Must be within 600 seconds of the server's current time |
auth_version | Must be exactly "1.0" |
body_md5 | MD5 hash (hex) of the request body. Only required for POST, PUT, and PATCH requests |
auth_signature | HMAC-SHA256 signature (hex) |
Signing String
VERB\nPATH\nQUERY_STRINGWhere QUERY_STRING is the alphabetically-sorted query parameters (excluding auth_signature):
For POST requests (with body):
auth_key=KEY&auth_timestamp=TIMESTAMP&auth_version=1.0&body_md5=MD5For GET requests (with extra params):
auth_key=KEY&auth_timestamp=TIMESTAMP&auth_version=1.0&info=user_countExample
import crypto from 'node:crypto'
function signRequest(verb, path, appKey, appSecret, body) {
const timestamp = Math.floor(Date.now() / 1000)
const params = [
`auth_key=${appKey}`,
`auth_timestamp=${timestamp}`,
`auth_version=1.0`,
...(body ? [`body_md5=${crypto.createHash('md5').update(body).digest('hex')}`] : []),
].join('&')
const signingString = `${verb.toUpperCase()}\n${path}\n${params}`
const signature = crypto.createHmac('sha256', appSecret).update(signingString).digest('hex')
return `${params}&auth_signature=${signature}`
}
// POST request with body
const body = JSON.stringify({
name: 'my-event',
channels: ['channel-1'],
data: JSON.stringify({ hello: 'world' }),
})
const postQuery = signRequest('POST', '/apps/12345/events', 'APP_KEY', 'APP_SECRET', body)
// auth_key=APP_KEY&auth_timestamp=1716200000&auth_version=1.0&body_md5=abc123def456&auth_signature=...
await fetch(`https://api.socketo.dev/apps/12345/events?${postQuery}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body,
})
// GET request — extra params must be included in the signing string
const getTimestamp = Math.floor(Date.now() / 1000)
const getParams = `auth_key=APP_KEY&auth_timestamp=${getTimestamp}&auth_version=1.0&info=user_count`
const getSigningString = `GET\n/apps/12345/channels\n${getParams}`
const getSignature = crypto.createHmac('sha256', 'APP_SECRET').update(getSigningString).digest('hex')
await fetch(`https://api.socketo.dev/apps/12345/channels?${getParams}&auth_signature=${getSignature}`)Use this signing for all Server Events endpoints, including terminate_connections, batch_events, and channel queries. For GET requests, omit the body argument and include any extra query parameters in the signing string.
Error Responses
| Code | Description |
|---|---|
4009 | Missing auth or user_data |
4009 | Invalid auth signature |
4009 | user_data is not valid JSON |
4009 | user_data does not contain an id field |
Note: The
auth_timestampmust be within 600 seconds (10 minutes) of the server's current time. Requests with timestamps outside this window will receive a401error. Theauth_versionmust be exactly"1.0"— other values will also return401.