Docs/Auth & Signature
Docs/Authentication

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. Replace APP_KEY with that key in the examples below.

Auth Flow

  1. Client subscribes to a private/presence channel
  2. SDK sends auth request to your authEndpoint
  3. Your server validates the user and generates a signature
  4. Your server returns { auth, channel_data? }
  5. SDK sends auth to Socketo to complete subscription

Auth Response Format

Private Channel

json
{
  "auth": "APP_KEY:HMAC_SIGNATURE"
}

Presence Channel

json
{
  "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:

plaintext
SOCKET_ID:CHANNEL_NAME

For presence channels:

plaintext
SOCKET_ID:CHANNEL_NAME:CHANNEL_DATA

Where CHANNEL_DATA is the JSON string of channel_data.

Example

js
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

json
{
  "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:

plaintext
SOCKET_ID::user::USER_DATA

Where 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

json
{
  "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

js
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

ParameterDescription
auth_keyYour application key
auth_timestampCurrent Unix timestamp in seconds. Must be within 600 seconds of the server's current time
auth_versionMust be exactly "1.0"
body_md5MD5 hash (hex) of the request body. Only required for POST, PUT, and PATCH requests
auth_signatureHMAC-SHA256 signature (hex)

Signing String

plaintext
VERB\nPATH\nQUERY_STRING

Where QUERY_STRING is the alphabetically-sorted query parameters (excluding auth_signature):

For POST requests (with body):

plaintext
auth_key=KEY&auth_timestamp=TIMESTAMP&auth_version=1.0&body_md5=MD5

For GET requests (with extra params):

plaintext
auth_key=KEY&auth_timestamp=TIMESTAMP&auth_version=1.0&info=user_count

Example

js
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

CodeDescription
4009Missing auth or user_data
4009Invalid auth signature
4009user_data is not valid JSON
4009user_data does not contain an id field

Note: The auth_timestamp must be within 600 seconds (10 minutes) of the server's current time. Requests with timestamps outside this window will receive a 401 error. The auth_version must be exactly "1.0" — other values will also return 401.