FluxyChat

How-to Guides

Authentication (JWT)

API keys, member JWTs, roles, token refresh, and WebSocket auth.

Authentication

FluxyChat uses two credential forms:

  • API key (X-Fluxy-Api-Key): server-to-server, identifies the tenant/project.
  • JWT (Authorization: Bearer …): client-to-worker/SDK. Claims include sub (userId), tid (projectId), roles, exp.

Rule: fc_ stays on the server. Browsers may use pk_ (publishable) for guest-session and anonymous tokens only. See Publishable keys.

Mint a member JWT (server-side)

export FLUXY_BASE_URL="http://127.0.0.1:8787"
export FLUXY_API_KEY="fc_..."

curl -sS -X POST "$FLUXY_BASE_URL/auth/token" \
  -H "Content-Type: application/json" \
  -H "X-Fluxy-Api-Key: $FLUXY_API_KEY" \
  -d '{"userId":"alice","roles":["member"],"ttlSeconds":3600}'

Response includes token, expiresIn, and echoed claims.

Roles

RoleTypical access
memberSend/edit own messages, reactions, read receipts, inbox
moderatorMute/ban, some admin routes
adminWebhooks, projects, alert rules
ownerSensitive tenant operations

Admin routes enforce role checks per endpoint. See OpenAPI docs for each route.

Next.js Route Handler example

import { NextResponse } from "next/server";

export async function POST(request: Request) {
  const body = await request.json().catch(() => null);
  const userId = body?.userId;
  if (!userId) return NextResponse.json({ error: "userId required" }, { status: 400 });

  const res = await fetch(`${process.env.FLUXY_BASE_URL}/auth/token`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Fluxy-Api-Key": process.env.FLUXY_API_KEY!,
    },
    body: JSON.stringify({ userId, roles: ["member"], ttlSeconds: 3600 }),
  });

  return NextResponse.json(await res.json(), { status: res.status });
}

Wire the returned JWT into FluxyRealtimeProvider or FluxyChatClient({ token }).

WebSocket auth

GET /ws/room/:roomId?token=<JWT>

Membership is enforced: 403 / close 1008 when the user is not a room member.

Token refresh

FluxyChatClient.setToken(next) (alias updateToken) replaces the session JWT. Returns true when sub changed. Reconnect active rooms.

When exp passes:

  1. Mint a new JWT on your backend (POST /auth/token).
  2. Call client.setToken(jwt), or pass a function to FluxyRealtimeProvider authTokenProvider so it remints before expiry:
<FluxyRealtimeProvider
  workerUrl={process.env.NEXT_PUBLIC_FLUXYCHAT_WORKER_URL!}
  authTokenProvider={async () => {
    const res = await fetch("/api/fluxy/token", { method: "POST" });
    const data = (await res.json()) as { token: string };
    return data.token;
  }}
>
  {children}
</FluxyRealtimeProvider>

Keep FLUXY_API_KEY (fc_) in the Route Handler only. Public rooms skip this path: publishableKey or joinPublicRoomAsGuest.

Common errors

SymptomCauseFix
401 invalid api keyRevoked or wrong project keyRotate key in Projects console
FluxyTokenExpiredErrorJWT exp passedMint new JWT
FluxyNotMemberErrorUser not in roomAdd membership or join room
FluxyAnonymousNotAllowedErrorRoom requires authUse member JWT

See Troubleshooting integration for copy-paste fixes.

On this page