Pusher Channels JavaScript library for browsers, React Native, NodeJS and web workers
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Authentication and authorization system for private channels, presence channels, and user authentication with customizable auth handlers.
Configuration for private and presence channel authentication.
/**
* Channel authorization configuration
*/
interface ChannelAuthorizationOptions {
transport: 'ajax' | 'jsonp';
endpoint: string;
params?: any;
headers?: any;
paramsProvider?: () => any;
headersProvider?: () => any;
customHandler?: ChannelAuthorizationHandler;
}
/**
* Channel authorization request parameters
*/
interface ChannelAuthorizationRequestParams {
socketId: string;
channelName: string;
}
/**
* Channel authorization response data
*/
interface ChannelAuthorizationData {
auth: string;
channel_data?: string;
shared_secret?: string;
}
/**
* Channel authorization callback function
*/
type ChannelAuthorizationCallback = (
error: Error | null,
authData: ChannelAuthorizationData | null
) => void;
/**
* Custom channel authorization handler
*/
interface ChannelAuthorizationHandler {
(params: ChannelAuthorizationRequestParams, callback: ChannelAuthorizationCallback): void;
}Channel Authorization Examples:
// Basic endpoint authorization
const pusher = new Pusher("app-key", {
cluster: "us2",
channelAuthorization: {
endpoint: "/pusher/auth",
transport: "ajax"
}
});
// Authorization with custom headers and parameters
const pusher = new Pusher("app-key", {
cluster: "us2",
channelAuthorization: {
endpoint: "/api/pusher/auth",
transport: "ajax",
headers: {
"X-CSRF-Token": "csrf-token",
"Authorization": "Bearer token"
},
params: {
userId: "123"
}
}
});
// Dynamic headers and parameters
const pusher = new Pusher("app-key", {
cluster: "us2",
channelAuthorization: {
endpoint: "/pusher/auth",
transport: "ajax",
headersProvider: () => ({
"Authorization": `Bearer ${getCurrentToken()}`,
"X-User-ID": getCurrentUserId()
}),
paramsProvider: () => ({
timestamp: Date.now(),
nonce: generateNonce()
})
}
});
// Custom authorization handler
const pusher = new Pusher("app-key", {
cluster: "us2",
channelAuthorization: {
endpoint: "/pusher/auth", // Still required but not used
transport: "ajax",
customHandler: (params, callback) => {
// Custom authorization logic
fetch("/api/custom-auth", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
socket_id: params.socketId,
channel_name: params.channelName,
user_id: getCurrentUserId()
})
})
.then(response => response.json())
.then(data => callback(null, data))
.catch(error => callback(error, null));
}
}
});Configuration for user authentication in presence channels.
/**
* User authentication configuration
*/
interface UserAuthenticationOptions {
transport: 'ajax' | 'jsonp';
endpoint: string;
params?: any;
headers?: any;
paramsProvider?: () => any;
headersProvider?: () => any;
customHandler?: UserAuthenticationHandler;
}
/**
* User authentication request parameters
*/
interface UserAuthenticationRequestParams {
socketId: string;
}
/**
* User authentication response data
*/
interface UserAuthenticationData {
auth: string;
user_data: string;
}
/**
* User authentication callback function
*/
type UserAuthenticationCallback = (
error: Error | null,
authData: UserAuthenticationData | null
) => void;
/**
* Custom user authentication handler
*/
interface UserAuthenticationHandler {
(params: UserAuthenticationRequestParams, callback: UserAuthenticationCallback): void;
}User Authentication Examples:
// Basic user authentication
const pusher = new Pusher("app-key", {
cluster: "us2",
userAuthentication: {
endpoint: "/pusher/user-auth",
transport: "ajax"
}
});
// User authentication with custom data
const pusher = new Pusher("app-key", {
cluster: "us2",
userAuthentication: {
endpoint: "/api/pusher/user-auth",
transport: "ajax",
headers: {
"Authorization": "Bearer user-token"
},
params: {
user_id: "user-123"
}
}
});
// Custom user authentication handler
const pusher = new Pusher("app-key", {
cluster: "us2",
userAuthentication: {
endpoint: "/pusher/user-auth",
transport: "ajax",
customHandler: (params, callback) => {
const userData = {
id: getCurrentUserId(),
name: getCurrentUserName(),
avatar: getCurrentUserAvatar()
};
// Generate auth signature and user data
generateUserAuth(params.socketId, userData)
.then(authData => callback(null, authData))
.catch(error => callback(error, null));
}
}
});
// Sign in user for presence channels
pusher.signin();Legacy authorization options maintained for backward compatibility.
/**
* Deprecated authorization options (for backward compatibility)
*/
interface DeprecatedAuthOptions {
params?: any;
headers?: any;
}
/**
* Deprecated channel authorizer interface
*/
interface DeprecatedChannelAuthorizer {
authorize(socketId: string, callback: ChannelAuthorizationCallback): void;
}
/**
* Deprecated authorizer generator function
*/
interface ChannelAuthorizerGenerator {
(channel: Channel, options: DeprecatedAuthorizerOptions): DeprecatedChannelAuthorizer;
}
interface DeprecatedAuthorizerOptions {
authTransport: 'ajax' | 'jsonp';
authEndpoint: string;
auth?: DeprecatedAuthOptions;
}Deprecated Authorization Examples:
// Legacy auth configuration (still supported)
const pusher = new Pusher("app-key", {
cluster: "us2",
authEndpoint: "/pusher/auth",
authTransport: "ajax",
auth: {
params: { user_id: "123" },
headers: { "X-CSRF-Token": "token" }
}
});
// Legacy custom authorizer
const pusher = new Pusher("app-key", {
cluster: "us2",
authEndpoint: "/pusher/auth",
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
// Custom authorization logic
customAuthRequest(socketId, channel.name, callback);
}
};
}
});Enumeration of authentication request types.
/**
* Authentication request type enumeration
*/
enum AuthRequestType {
UserAuthentication = "user-authentication",
ChannelAuthorization = "channel-authorization"
}Shared callback type for both channel and user authentication.
/**
* Generic authentication transport callback
*/
type AuthTransportCallback = ChannelAuthorizationCallback | UserAuthenticationCallback;
/**
* Internal authentication options interface
*/
interface InternalAuthOptions {
transport: 'ajax' | 'jsonp';
endpoint: string;
params?: any;
headers?: any;
paramsProvider?: () => any;
headersProvider?: () => any;
}// Server endpoint should return JSON like:
// { "auth": "app_key:signature" }
const pusher = new Pusher("app-key", {
cluster: "us2",
channelAuthorization: {
endpoint: "/pusher/auth",
transport: "ajax"
}
});
// Subscribe to private channel
const privateChannel = pusher.subscribe("private-user-messages");
privateChannel.bind("pusher:subscription_succeeded", () => {
console.log("Successfully authenticated and subscribed");
});
privateChannel.bind("pusher:subscription_error", (error) => {
console.error("Authentication failed:", error);
});// Server endpoint should return JSON like:
// {
// "auth": "app_key:signature",
// "channel_data": "{\"user_id\":\"123\",\"user_info\":{\"name\":\"Alice\"}}"
// }
const pusher = new Pusher("app-key", {
cluster: "us2",
channelAuthorization: {
endpoint: "/pusher/auth",
transport: "ajax",
headers: {
"Authorization": "Bearer user-token"
}
}
});
// Subscribe to presence channel
const presenceChannel = pusher.subscribe("presence-chat-room");
presenceChannel.bind("pusher:subscription_succeeded", (members) => {
console.log("Joined presence channel");
console.log("Current members:", members);
});// Server endpoint should return JSON like:
// {
// "auth": "app_key:user_signature",
// "user_data": "{\"id\":\"123\",\"name\":\"Alice\",\"avatar\":\"url\"}"
// }
const pusher = new Pusher("app-key", {
cluster: "us2",
userAuthentication: {
endpoint: "/pusher/user-auth",
transport: "ajax"
}
});
// Sign in user (enables presence without channel_data)
pusher.signin();
// Now presence channels work without channel_data in auth response
const presenceChannel = pusher.subscribe("presence-general");/**
* HTTP authentication error
*/
class HTTPAuthError extends Error {
status: number;
constructor(status: number, msg?: string);
}Error Handling Examples:
// Handle authentication errors
pusher.bind("error", (error) => {
if (error instanceof HTTPAuthError) {
console.error(`Auth failed with status ${error.status}:`, error.message);
switch (error.status) {
case 401:
// Redirect to login
window.location.href = "/login";
break;
case 403:
// Show access denied message
showError("Access denied to this channel");
break;
default:
showError("Authentication error occurred");
}
}
});
// Channel-specific error handling
channel.bind("pusher:subscription_error", (error) => {
console.error("Channel subscription failed:", error);
// Handle channel-specific auth failure
});// Example Express.js endpoint for channel authorization
app.post('/pusher/auth', express.json(), (req, res) => {
const { socket_id, channel_name } = req.body;
const user = authenticateUser(req); // Your auth logic
if (!user) {
return res.status(401).json({ error: 'Unauthorized' });
}
// For private channels
if (channel_name.startsWith('private-')) {
const auth = pusher.authenticate(socket_id, channel_name);
res.json(auth);
}
// For presence channels
else if (channel_name.startsWith('presence-')) {
const presenceData = {
user_id: user.id,
user_info: {
name: user.name,
avatar: user.avatar
}
};
const auth = pusher.authenticate(socket_id, channel_name, presenceData);
res.json(auth);
} else {
res.status(400).json({ error: 'Invalid channel' });
}
});// Example user authentication endpoint
app.post('/pusher/user-auth', express.json(), (req, res) => {
const { socket_id } = req.body;
const user = authenticateUser(req);
if (!user) {
return res.status(401).json({ error: 'Unauthorized' });
}
const userData = {
id: user.id,
name: user.name,
avatar: user.avatar,
role: user.role
};
const auth = pusher.authenticateUser(socket_id, userData);
res.json(auth);
});Install with Tessl CLI
npx tessl i tessl/npm-pusher-js