PieSocket Authentication

Anyone holding your API key, public by design unlike your API secret, can subscribe to any of your channels and see everything published on them. Private channels and forced authentication are how you restrict that to users your own backend has approved.

Private channels

Channels prefixed private- require a valid JWT to connect.

Forced authentication

To require a JWT on every channel, not just private- ones, open your PieSocket from the dashboard and enable "forced authentication" from its Settings tab.

Rejected connections

A connection that needs a JWT but doesn't supply a valid one is rejected immediately, see Errors in the protocol reference for the exact error frame.

Getting a JWT to your client

There are two ways to do this:

  1. Automatic, configure your SDK with an authEndpoint on your own backend, and it fetches the token for you whenever a connection needs one.
  2. Manual, generate the JWT yourself and pass it directly when connecting.

Automatic: let the SDK fetch it for you

All four official SDKs, can fetch a JWT from your own backend automatically, so you don't have to write any client-side token-fetching code yourself.

Configure it on the client:

const piesocket = new PieSocket({
  version: 4,
  clusterId: 'YOUR_CLUSTER_ID',
  apiKey: 'YOUR_API_KEY',
  authEndpoint: '/broadcasting/auth', // this is already the default
  authHeaders: { 'X-CSRF-TOKEN': csrfToken },
});

authEndpoint only has a default value (/broadcasting/auth, resolved against the current page) in the JavaScript SDK, since it's the only one running in a browser with an implicit origin to resolve a relative path against. The other three require an absolute URL.

Whenever the SDK connects to a channel, it decides whether to call authEndpoint in this order:

  1. If you passed a jwt option directly, that's used as-is, authEndpoint is never called for that connection.
  2. Otherwise, the channel needs to be "guarded", either its name is prefixed private-, or you set forceAuth: true on the client (which guards every channel, not just private- ones). If it isn't guarded, the connection proceeds with no token and no authEndpoint call at all.
  3. If it is guarded, the SDK calls authEndpoint and uses the auth field from the response as the token.

forceAuth is a client-side option, it's how you make your own SDK instance always authenticate, even for channels that aren't private-. It's independent from the dashboard's account-wide forced authentication above, that one is enforced by the server for every connection to your API key regardless of what any client does; forceAuth only changes what this one SDK instance does on its own.

Your endpoint just needs to respond with:

{ "auth": "JWT_TOKEN" }

The request your endpoint receives differs slightly by platform:

  • JavaScript sends a multipart/form-data POST with a channel_name field, cookies included. If you're on Laravel, see PieSocket + Laravel, the default broadcasting auth route just works with no changes.
  • Android, Flutter, and Swift send a JSON body instead: { "channel_name": "...", "connection_uuid": "..." }, with Content-Type: application/json.

Either way, your endpoint should check that the current user is allowed to join channel_name and respond with a JWT signed the same way as manual generation below, with sub set to channel_name. Add custom headers (an auth token, a CSRF header) with authHeaders.

Manual: pass a JWT directly

Method Key Example
Query param jwt wss://demo.piesocket.com/v4/CHANNEL_ID?api_key=xxx&jwt=JWT_TOKEN
Header jwt jwt: JWT_TOKEN

Generate the token on your own server using your API secret, HS256, and this payload:

{
  "sub": CHANNEL_ID,
  "iat": ISSUED_TIME,
  "exp": EXPIRY_TIME (OPTIONAL)
  "user": NUMBER|STRING|JSON (OPTIONAL)
}

The token is only valid for the channel named in sub, generate a separate one per channel. You can generate one for testing with our JWT Encoder.

You can sign this payload in any language with a standard JWT library:

use Firebase\JWT\JWT;

$payload = [
    'sub' => 'CHANNEL_ID',
    'iat' => time(),
    'exp' => time() + 3600,
    'user' => $userId,
];

$token = JWT::encode($payload, 'YOUR_API_SECRET', 'HS256');

Libraries used above: firebase/php-jwt, PyJWT, jsonwebtoken, jwt-go, ruby-jwt.

Once your backend hands the client this token (however you deliver it, an API response, embedded in the page, etc.), pass it as the jwt option when connecting. This skips the authEndpoint call entirely, see the precedence order above:

const piesocket = new PieSocket({
  version: 4,
  clusterId: 'YOUR_CLUSTER_ID',
  apiKey: 'YOUR_API_KEY',
  jwt: token,
});

Not using an official SDK? Pass it directly on the connection instead, see the query param or header table earlier in this section.

Identifying a user

There are two ways to set a connection's user identity:

  1. Unsecured: pass &user=id on the connection URL. Simple, but anyone who can reach your API key can claim any identity.
  2. Secure: put the user id/name/JSON into the JWT payload's user claim, as described above.

If you want to notify one specific user, use a channel name derived from their ID, for example, every user joins a channel named user-123 where 123 is their own ID. To notify user 456, publish to user-456. Since that requires a JWT, other users can't join it without your server issuing them one.