Skip to content

Authentication

This guide demonstrates JWT token-based authentication for securing Teleportal connections. Both WebSocket and HTTP handlers verify tokens before allowing access.

Tokens are always signed with HS256 (symmetric HMAC-SHA-256). verifyToken pins algorithms: ["HS256"], so tokens signed with any other algorithm – including unsecured alg: "none" tokens – are rejected.

  • Setting up JWT token authentication using createTokenManager
  • Using token authentication for secure connections
  • Configuring permission checks with token manager
  • Creating and using JWT tokens on the client side
import { serve } from "crossws/server";
import { Server } from "teleportal/server";
import { createTokenManager } from "teleportal/token";
import { getWebsocketHandlers } from "teleportal/websocket-server";
const tokenManager = createTokenManager({
secret: "your-secret-key",
expiresIn: 3600,
});
const server = new Server({
storage: async (ctx) => {
// Your storage implementation
return documentStorage;
},
checkPermission: async ({ context, documentId, message, type }) => {
const token = (context as any).token;
if (!token) return false;
const result = await tokenManager.verifyToken(token);
if (!result.valid || !result.payload) return false;
const payload = result.payload;
const requiredPermission = type === "read" ? "read" : "write";
return tokenManager.hasDocumentPermission(payload, documentId!, requiredPermission);
},
});
serve({
websocket: getWebsocketHandlers({
server,
onUpgrade: async (request) => {
// Extract token from request
const url = new URL(request.url);
const authHeader = request.headers.get("authorization");
const token =
url.searchParams.get("token") ||
(authHeader && /^bearer\s+/i.test(authHeader)
? authHeader.replace(/^bearer\s+/i, "")
: null);
if (!token) {
throw new Response("No token provided", { status: 401 });
}
const result = await tokenManager.verifyToken(token);
if (!result.valid || !result.payload) {
throw new Response("Invalid token", { status: 401 });
}
return {
context: {
userId: result.payload.userId,
room: result.payload.room,
token,
},
};
},
}),
fetch: () => new Response("Not found", { status: 404 }),
});
import { Provider } from "teleportal/providers";
import { createTokenManager } from "teleportal/token";
import { createEncryptionKey } from "teleportal/encryption-key";
// Create token manager (should match server secret)
const tokenManager = createTokenManager({
secret: "your-secret-key",
});
// Generate token
const token = await tokenManager.createToken("user-123", "org-456", [
{ pattern: "user-123/*", permissions: ["read", "write"] },
]);
// Connect with token
const provider = await Provider.create({
url: `wss://example.com?token=${token}`,
document: "user-123/my-document",
encryptionKey: createEncryptionKey(),
});
await provider.synced;

The documentAccess list carried in each token uses literal glob matching: * is the only wildcard (it matches any run of characters, including none), and every other character – including regex metacharacters like ., [, or ( – is matched literally. A pattern such as logs[prod]* matches the literal text logs[prod]… and never behaves as a regex character class. A !-prefixed pattern is an exclusion: if any exclusion matches the document, access is denied regardless of inclusions.