Authorization endpoints
When a client subscribes to a private- or presence- channel, the SDK posts to your auth endpoint and expects a signed token. Below are working examples in the most common server stacks.
What the endpoint receives
Form-encoded body:
| Field | Description |
|---|---|
socket_id | The connection’s unique id, assigned by Mawjly when the socket opens. Looks like 123.456789. |
channel_name | The name the client is trying to subscribe to. |
What the endpoint returns
JSON:
{ "auth": "YOUR_KEY:hex_hmac_sha256_signature" }The signature is HMAC-SHA256 of the string "<socket_id>:<channel_name>" using your app secret.
For presence channels, also include a channel_data field (see the next page).
Laravel (automatic)
You don’t write the endpoint manually — Laravel ships it. Just define your channels in routes/channels.php:
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('user.{id}', function ($user, $id) {
return $user->id === (int) $id;
});
Broadcast::channel('order.{id}', function ($user, $id) {
return Order::find($id)?->user_id === $user->id;
});Laravel signs with PUSHER_APP_SECRET and Mawjly verifies. Done.
Node.js (Express)
import express from 'express';
import Pusher from 'pusher';
const app = express();
app.use(express.urlencoded({ extended: true }));
const pusher = new Pusher({
appId: process.env.MAWJLY_APP_ID!,
key: process.env.MAWJLY_KEY!,
secret: process.env.MAWJLY_SECRET!,
cluster: 'sa',
host: 'ws-sa.mawjly.com',
port: '443',
useTLS: true,
});
app.post('/pusher/auth', requireAuth, (req, res) => {
const { socket_id, channel_name } = req.body;
const userId: string = res.locals.userId;
// Permission check
if (channel_name === `private-user.${userId}`) {
const auth = pusher.authorizeChannel(socket_id, channel_name);
return res.json(auth);
}
return res.sendStatus(403);
});PHP (vanilla)
<?php
require __DIR__ . '/vendor/autoload.php';
session_start();
if (empty($_SESSION['user_id'])) {
http_response_code(401);
exit;
}
$userId = (int) $_SESSION['user_id'];
$channel = $_POST['channel_name'] ?? '';
$socketId = $_POST['socket_id'] ?? '';
if ($channel !== "private-user.{$userId}") {
http_response_code(403);
exit;
}
$pusher = new Pusher\Pusher(
'YOUR_APP_KEY',
'YOUR_APP_SECRET',
'YOUR_APP_ID',
[
'host' => 'ws-sa.mawjly.com',
'port' => 443,
'scheme' => 'https',
'cluster' => 'sa',
'useTLS' => true,
],
);
header('Content-Type: application/json');
echo $pusher->authorizeChannel($channel, $socketId);Python (Flask)
from flask import Flask, request, session, jsonify, abort
import pusher
app = Flask(__name__)
p = pusher.Pusher(
app_id='YOUR_APP_ID',
key='YOUR_APP_KEY',
secret='YOUR_APP_SECRET',
cluster='sa',
host='ws-sa.mawjly.com',
port=443,
ssl=True,
)
@app.post('/pusher/auth')
def auth():
user_id = session.get('user_id')
if not user_id:
abort(401)
channel = request.form['channel_name']
socket_id = request.form['socket_id']
if channel != f'private-user.{user_id}':
abort(403)
return jsonify(p.authenticate(channel=channel, socket_id=socket_id))Common pitfalls
- Returning
200 OKwith an empty / wrong body → SDK retries forever. Always return either the signed token or a 403. - Hashing the wrong order — must be
socket_id:channel_name, not the other way around. - Generating the signature on the client (don’t — your secret would leak).
- CORS. The auth endpoint must accept the request method/headers your client sends. Most SDKs use
POSTwithapplication/x-www-form-urlencoded.