{"id":"ce32cdc3-9705-49dc-885a-87a2346ed11a","shortId":"y3PrGu","kind":"skill","title":"cometchat-native-production","tagline":"Production-readiness for React Native — server-minted auth tokens, user management CRUD, external-backend recipes (Express / Hono / Firebase Functions / Vercel Serverless). RN has no API routes, so the backend is always external.","description":"## Purpose\n\nTeaches Claude how to move a React Native CometChat integration from dev-mode Auth Key to production-ready server-minted auth tokens + user CRUD. Covers:\n\n1. Why the dev `authKey` can't ship to production\n2. Auth Key vs REST API Key — which lives where\n3. Server endpoint recipes (Express / Hono / Firebase Functions / Vercel)\n4. Client-side: `CometChatUIKit.login({ authToken })` + token refresh\n5. User CRUD endpoints + auth-provider integration (Firebase Auth / Supabase / Clerk / Auth0)\n6. Security checklist + rate limits\n\n**Read `cometchat-native-core` first** (init/login/wrapper chain) before this skill — production just swaps one prop on the provider, but understanding the provider lifecycle is the prerequisite.\n\nGround truth: `docs/ui-kit/react-native/methods.mdx`, and the cross-platform REST API at `https://{APP_ID}.api-{REGION}.cometchat.io/v3/`.\n\n---\n\n## 1. Why production auth matters\n\nIn dev mode, `CometChatUIKit.login({ uid: \"...\" })` uses the `authKey` passed to `CometChatUIKit.init({ authKey })`. That key is embedded in your React Native bundle. For a signed iOS `.ipa` or Android `.apk`/`.aab`, anyone can extract it with standard reverse-engineering tools (unzip, strings, `apktool`, `ReverseAPK`) and use it to log in as **ANY** user in your CometChat app — read private messages, send as other users, access every conversation.\n\nProduction MUST use server-side token generation:\n\n- Your **server** holds the REST API Key (a different key from the client Auth Key).\n- On user login, your server calls CometChat's REST API with the REST API Key to mint a short-lived **Auth Token** for that specific UID.\n- Your client receives the Auth Token and calls `CometChatUIKit.login({ authToken })`.\n- If the token leaks, the blast radius is one user session, not your whole app.\n\n**Exactly the same threat model as JWTs for a REST API.** If you've built a login flow before, this is that.\n\n---\n\n## 2. Auth Key vs REST API Key — two different keys\n\nEasy to confuse. Both come from the CometChat Dashboard (your app → API & Auth Keys), but they live in different places and have different privileges.\n\n| Key | Where in dashboard | Purpose | Where it lives |\n|---|---|---|---|\n| **Auth Key** | \"Auth Keys\" table | Client-side SDK `login({ uid })` in dev mode | **Client bundle** — dev only. Never in production builds. |\n| **REST API Key** | \"REST API Keys\" table | Server-to-server: token generation, user CRUD, custom-message-send | **Server only.** Never in an RN bundle, `app.json extra`, `EXPO_PUBLIC_*` var, or git-committed file. |\n\nIf the project only has an Auth Key, the user needs to generate a REST API Key in the dashboard: **API & Auth Keys → REST API Keys → Add Key**. Pick \"Full Access\" for server-side use.\n\n---\n\n## 3. The token auth pattern (4 steps)\n\n```\n1. Client logs into YOUR auth (Firebase Auth / Supabase / Clerk / Auth0 / custom)\n   ↓\n2. Client asks YOUR backend for a CometChat auth token\n   ↓ (POST /api/cometchat-token { uid })\n3. Backend calls CometChat REST API → gets an Auth Token for that UID\n   ↓ POST https://{APP_ID}.api-{REGION}.cometchat.io/v3/users/{uid}/auth_tokens\n     with header apiKey: <REST_API_KEY>\n   ↓\n4. Client calls CometChatUIKit.login({ authToken: \"...\" })\n```\n\nThe RN client never sees the REST API Key. The server never ships a password or email to the client. The CometChat SDK holds the auth token, not a static key.\n\n---\n\n## 4. Server endpoint recipes\n\nRN projects don't have Next.js-style API routes. You need a separate backend. Pick the one the user already has, or the simplest if they're starting fresh.\n\n### 4a. Express (Node.js backend)\n\n```ts\n// server/routes/cometchat-token.ts\nimport { Router } from \"express\";\nimport { requireAuth } from \"../middleware/auth\";   // your existing auth\n\nconst router = Router();\nconst APP_ID = process.env.COMETCHAT_APP_ID!;\nconst REGION = process.env.COMETCHAT_REGION!;\nconst REST_API_KEY = process.env.COMETCHAT_REST_API_KEY!;\n\nrouter.post(\"/cometchat-token\", requireAuth, async (req, res) => {\n  // Derive UID from authenticated session — NOT from the request body in prod.\n  const uid = req.user.id;\n\n  const r = await fetch(\n    `https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}/auth_tokens`,\n    {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        appId: APP_ID,\n        apiKey: REST_API_KEY,\n      },\n      body: JSON.stringify({}),\n    },\n  );\n\n  if (!r.ok) {\n    const error = await r.text();\n    console.error(\"CometChat token error:\", error);\n    return res.status(r.status).json({ error: \"Failed to generate auth token\" });\n  }\n\n  const data = await r.json();\n  return res.json({ authToken: data.data.authToken });\n});\n\nexport default router;\n```\n\n### 4b. Hono (Cloudflare Workers / Bun / Node)\n\n```ts\n// server/cometchat-token.ts\nimport { Hono } from \"hono\";\n\nconst app = new Hono();\n\napp.post(\"/api/cometchat-token\", async (c) => {\n  const user = c.get(\"user\");   // your middleware-resolved user\n  if (!user) return c.json({ error: \"unauthorized\" }, 401);\n\n  const APP_ID = c.env.COMETCHAT_APP_ID;\n  const REGION = c.env.COMETCHAT_REGION;\n  const REST_API_KEY = c.env.COMETCHAT_REST_API_KEY;\n\n  const r = await fetch(\n    `https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(user.id)}/auth_tokens`,\n    {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        appId: APP_ID,\n        apiKey: REST_API_KEY,\n      },\n      body: JSON.stringify({}),\n    },\n  );\n\n  if (!r.ok) return c.json({ error: \"token mint failed\" }, 502);\n  const data = await r.json();\n  return c.json({ authToken: data.data.authToken });\n});\n\nexport default app;\n```\n\n### 4c. Firebase Cloud Functions\n\n```ts\n// functions/src/cometchat-token.ts\nimport { onCall, HttpsError } from \"firebase-functions/v2/https\";\n\nexport const getCometChatToken = onCall(\n  { secrets: [\"COMETCHAT_APP_ID\", \"COMETCHAT_REGION\", \"COMETCHAT_REST_API_KEY\"] },\n  async (request) => {\n    if (!request.auth) throw new HttpsError(\"unauthenticated\", \"Sign in required\");\n    const uid = request.auth.uid;\n\n    const APP_ID = process.env.COMETCHAT_APP_ID!;\n    const REGION = process.env.COMETCHAT_REGION!;\n    const REST_API_KEY = process.env.COMETCHAT_REST_API_KEY!;\n\n    const r = await fetch(\n      `https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}/auth_tokens`,\n      {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          appId: APP_ID,\n          apiKey: REST_API_KEY,\n        },\n        body: JSON.stringify({}),\n      },\n    );\n\n    if (!r.ok) throw new HttpsError(\"internal\", \"token mint failed\");\n    const data = await r.json();\n    return { authToken: data.data.authToken };\n  },\n);\n```\n\nClient call:\n\n```tsx\nimport functions from \"@react-native-firebase/functions\";\nconst result = await functions().httpsCallable(\"getCometChatToken\")();\nconst authToken = result.data.authToken;\n```\n\n### 4d. Vercel Serverless / Next.js API Route\n\nEven if the RN app isn't Next.js, the user's existing web app often is. Reuse the same backend:\n\n```ts\n// pages/api/cometchat-token.ts  (or app/api/cometchat-token/route.ts)\nimport type { NextApiRequest, NextApiResponse } from \"next\";\nimport { getServerSession } from \"next-auth\";\nimport { authOptions } from \"./auth/[...nextauth]\";\n\nexport default async function handler(req: NextApiRequest, res: NextApiResponse) {\n  if (req.method !== \"POST\") return res.status(405).end();\n\n  const session = await getServerSession(req, res, authOptions);\n  if (!session?.user) return res.status(401).json({ error: \"unauthorized\" });\n\n  const APP_ID = process.env.COMETCHAT_APP_ID!;\n  const REGION = process.env.COMETCHAT_REGION!;\n  const REST_API_KEY = process.env.COMETCHAT_REST_API_KEY!;\n  const uid = session.user.id;\n\n  const r = await fetch(\n    `https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}/auth_tokens`,\n    {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\", appId: APP_ID, apiKey: REST_API_KEY },\n      body: JSON.stringify({}),\n    },\n  );\n\n  if (!r.ok) return res.status(502).json({ error: \"token mint failed\" });\n  const data = await r.json();\n  return res.json({ authToken: data.data.authToken });\n}\n```\n\n---\n\n## 5. Client-side: `CometChatUIKit.login({ authToken })`\n\nOnce the server is serving tokens, update the RN client to fetch the token and use it. This is a change to the `CometChatProvider` (see `cometchat-native-core` § 6) — swap the `uid` prop for an `authToken` prop.\n\n### 5a. Update the provider to support authToken\n\n```tsx\n// CometChatProvider.tsx — production-aware version\nimport React, { createContext, useContext, useEffect, useState, type ReactNode } from \"react\";\nimport { CometChatUIKit } from \"@cometchat/chat-uikit-react-native\";\n\nlet initialized = false;\nlet loginInFlight: Promise<unknown> | null = null;\n\nasync function ensureLoggedIn(authToken?: string, uid?: string): Promise<void> {\n  const existing = await CometChatUIKit.getLoggedInUser();\n  if (existing) return;\n  if (loginInFlight) {\n    await loginInFlight;\n    return;\n  }\n  // Production — prefer authToken\n  if (authToken) {\n    loginInFlight = CometChatUIKit.login({ authToken });\n  } else if (uid) {\n    loginInFlight = CometChatUIKit.login({ uid });   // dev fallback\n  } else {\n    return;  // nothing to log in with yet\n  }\n  try {\n    await loginInFlight;\n  } finally {\n    loginInFlight = null;\n  }\n}\n\ninterface Props {\n  appId: string;\n  region: string;\n  authKey?: string;      // dev only; omit in production\n  authToken?: string;    // production — from your backend\n  uid?: string;          // dev only\n  children: ReactNode;\n}\n\nexport function CometChatProvider({ appId, region, authKey, authToken, uid, children }: Props) {\n  const [isReady, setIsReady] = useState(false);\n  const [error, setError] = useState<string | null>(null);\n\n  useEffect(() => {\n    async function setup() {\n      try {\n        if (!initialized) {\n          initialized = true;\n          await CometChatUIKit.init({\n            appId,\n            region,\n            subscriptionType: \"ALL_USERS\",\n            ...(authKey ? { authKey } : {}),\n          });\n        }\n        await ensureLoggedIn(authToken, uid);\n        setIsReady(true);\n      } catch (e) {\n        setError(String(e));\n      }\n    }\n    setup();\n  }, [appId, region, authKey, authToken, uid]);\n\n  if (!isReady) return null;\n  return <>{children}</>;\n}\n```\n\n**Push registration lands here** — right after `ensureLoggedIn` resolves,\nbefore `setIsReady(true)`. The CometChat SDK scopes push tokens to the\nlogged-in user, so registering before login associates the token with\n\"anonymous\" and the device won't receive pushes.\n\n```tsx\nimport { bootstrapPushAfterLogin } from \"../push/bootstrap\";\n//...\nawait ensureLoggedIn(authToken, uid);\nawait bootstrapPushAfterLogin();   // registers FCM/APNs token with CometChat\nsetIsReady(true);\n```\n\nAnd unregister BEFORE `CometChatUIKit.logout()` — the SDK needs the user\ncontext to dissociate the token. See `cometchat-native-push § 7` for\nthe full `bootstrapPushAfterLogin` / `unregisterPushTokenOnLogout` helper\npair.\n\n### 5b. Fetch the token from your backend\n\nTypical app flow:\n\n```tsx\n// App.tsx\nimport { useState, useEffect } from \"react\";\nimport { CometChatProvider } from \"./src/providers/CometChatProvider\";\nimport { useMyAppAuth } from \"./src/hooks/useMyAppAuth\";   // your existing auth\n\nexport default function App() {\n  const { user, isAuthenticated } = useMyAppAuth();\n  const [cometChatToken, setCometChatToken] = useState<string | null>(null);\n\n  useEffect(() => {\n    if (!isAuthenticated) {\n      setCometChatToken(null);\n      return;\n    }\n    // Fetch a CometChat auth token from your backend\n    fetch(\"https://api.yourapp.com/cometchat-token\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${user.jwt}` },\n    })\n      .then((r) => r.json())\n      .then((data) => setCometChatToken(data.authToken))\n      .catch((e) => console.error(\"CometChat token fetch failed:\", e));\n  }, [isAuthenticated, user?.jwt]);\n\n  if (!isAuthenticated) return <LoginScreen />;\n  if (!cometChatToken) return <LoadingScreen message=\"Connecting chat...\" />;\n\n  return (\n    <CometChatProvider\n      appId={COMETCHAT_APP_ID}\n      region={COMETCHAT_REGION}\n      authToken={cometChatToken}\n      // no authKey prop in production\n    >\n      <AppNavigator />\n    </CometChatProvider>\n  );\n}\n```\n\n### 5c. Handle token expiry / refresh\n\nAuth tokens have a configurable TTL (default 24 hours). On token expiry, SDK calls start failing. Handle this by re-minting on 401:\n\n```tsx\nuseEffect(() => {\n  const LISTENER_ID = \"TOKEN_EXPIRY_LISTENER\";\n  CometChat.addConnectionListener(\n    LISTENER_ID,\n    new CometChat.ConnectionListener({\n      onDisconnected: async () => {\n        // Connection dropped. Token might be expired.\n        // Re-fetch and re-login.\n        const freshToken = await fetchCometChatToken(user.jwt);\n        setCometChatToken(freshToken);\n        await CometChatUIKit.login({ authToken: freshToken });\n      },\n    }),\n  );\n  return () => CometChat.removeConnectionListener(LISTENER_ID);\n}, [user?.jwt]);\n```\n\nA simpler approach for apps that can tolerate a forced re-login: on any 401 from the SDK, log the user out and force them through your app's sign-in flow again.\n\n---\n\n## 6. User management CRUD\n\nWhen someone signs up in your app, you need to create a matching CometChat user. Same for profile updates (name/avatar change) and deletion. These happen on your backend, using the REST API with the REST API Key.\n\n### 6a. Create a user on signup\n\n```ts\nasync function createCometChatUser(uid: string, name: string, avatarUrl?: string) {\n  const r = await fetch(\n    `https://${APP_ID}.api-${REGION}.cometchat.io/v3/users`,\n    {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        appId: APP_ID,\n        apiKey: REST_API_KEY,\n      },\n      body: JSON.stringify({\n        uid,\n        name,\n        avatar: avatarUrl,\n      }),\n    },\n  );\n  if (!r.ok) throw new Error(`CometChat user create failed: ${await r.text()}`);\n  return r.json();\n}\n```\n\n### 6b. Update a user on profile change\n\n```ts\nasync function updateCometChatUser(uid: string, updates: Partial<{ name: string; avatar: string; metadata: any }>) {\n  const r = await fetch(\n    `https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}`,\n    {\n      method: \"PUT\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        appId: APP_ID,\n        apiKey: REST_API_KEY,\n      },\n      body: JSON.stringify(updates),\n    },\n  );\n  if (!r.ok) throw new Error(`CometChat user update failed: ${await r.text()}`);\n  return r.json();\n}\n```\n\n### 6c. Delete a user on account deletion\n\n```ts\nasync function deleteCometChatUser(uid: string) {\n  const r = await fetch(\n    `https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${encodeURIComponent(uid)}`,\n    {\n      method: \"DELETE\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        appId: APP_ID,\n        apiKey: REST_API_KEY,\n      },\n      body: JSON.stringify({ permanent: true }),\n    },\n  );\n  if (!r.ok) throw new Error(`CometChat user delete failed: ${await r.text()}`);\n}\n```\n\n### 6d. Where to wire these calls\n\nThe CRUD functions live on your backend; you call them from your existing auth event handlers:\n\n| Auth event | When to call | Function |\n|---|---|---|\n| User signs up | After your app's user creation succeeds | `createCometChatUser(newUser.id, newUser.name, newUser.avatarUrl)` |\n| User updates name or avatar | After your app's profile update succeeds | `updateCometChatUser(user.id, { name, avatar })` |\n| User deletes account | Before/after your app's user deletion | `deleteCometChatUser(user.id)` |\n\n---\n\n## 7. Auth-provider integration recipes\n\nYour RN app's auth layer typically comes from one of these SDKs. How to wire CometChat into each:\n\n### 7a. Firebase Auth\n\nFirebase issues a UID per user. Use that same UID in CometChat.\n\n```ts\n// Backend (Firebase Cloud Function)\nimport { onDocumentCreated } from \"firebase-functions/v2/firestore\";\nimport { onUserCreated, onUserDeleted } from \"firebase-functions/v2/auth\";\n\nexport const onSignup = onUserCreated(async (event) => {\n  const { uid, displayName, photoURL } = event.data;\n  await createCometChatUser(uid, displayName ?? \"User\", photoURL);\n});\n\nexport const onAccountDelete = onUserDeleted(async (event) => {\n  await deleteCometChatUser(event.data.uid);\n});\n\n// Profile updates are app-level — hook into your profile-update handler\n```\n\nClient — get the Firebase ID token, send to your `cometchat-token` endpoint:\n\n```tsx\nimport auth from \"@react-native-firebase/auth\";\nconst idToken = await auth().currentUser!.getIdToken();\nconst r = await fetch(\"/api/cometchat-token\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${idToken}` },\n});\nconst { authToken } = await r.json();\n```\n\n### 7b. Supabase Auth\n\nSupabase also issues a UID. Use it as the CometChat UID.\n\n```ts\n// Backend — Supabase Edge Function triggered on signup\nDeno.serve(async (req) => {\n  const event = await req.json();\n  if (event.type === \"INSERT\" && event.table === \"users\") {\n    const { id, email } = event.record;\n    await createCometChatUser(id, email.split(\"@\")[0]);\n  }\n  return new Response(\"ok\");\n});\n```\n\nClient:\n\n```tsx\nimport { supabase } from \"./supabase\";\nconst { data: { session } } = await supabase.auth.getSession();\nconst r = await fetch(\"/api/cometchat-token\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${session!.access_token}` },\n});\n```\n\n### 7c. Clerk Expo\n\nClerk is Expo-friendly and has its own webhooks for user lifecycle events.\n\n```tsx\n// Client — React Native\nimport { useAuth } from \"@clerk/clerk-expo\";\nconst { getToken, userId } = useAuth();\nconst jwt = await getToken();\nconst r = await fetch(\"/api/cometchat-token\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${jwt}` },\n});\n```\n\nBackend — use a Clerk webhook to trigger CRUD on user lifecycle events.\n\n### 7d. Auth0\n\n```tsx\nimport { useAuth0 } from \"react-native-auth0\";\nconst { getCredentials } = useAuth0();\nconst { accessToken } = await getCredentials();\nconst r = await fetch(\"/api/cometchat-token\", {\n  method: \"POST\",\n  headers: { Authorization: `Bearer ${accessToken}` },\n});\n```\n\nBackend — use Auth0 Actions or Rules to trigger CRUD webhooks.\n\n### 7e. Custom JWT / bespoke auth\n\nIf the user's auth is custom (their own JWT), the pattern is the same: client includes `Authorization: Bearer <jwt>`, server validates + extracts UID + mints CometChat auth token.\n\n---\n\n## 8. Environment variables — split between client + server\n\n| Variable | Location | Visibility |\n|---|---|---|\n| `COMETCHAT_APP_ID` | Client AND server | OK client-side |\n| `COMETCHAT_REGION` | Client AND server | OK client-side |\n| `COMETCHAT_AUTH_KEY` | **Dev client only.** Remove from production. | Should NEVER ship in a production RN bundle |\n| `COMETCHAT_REST_API_KEY` | **Server only.** Your backend's env. | Never ships to client, ever |\n| `COMETCHAT_TOKEN_ENDPOINT` | Client | Your backend URL (e.g. `https://api.yourapp.com/cometchat-token`) — safe in client bundle |\n\n### Production RN client `.env` (or app.json extra):\n\n```\nCOMETCHAT_APP_ID=your_app_id\nCOMETCHAT_REGION=us\nCOMETCHAT_TOKEN_ENDPOINT=https://api.yourapp.com/cometchat-token\n# No COMETCHAT_AUTH_KEY in production\n# No COMETCHAT_REST_API_KEY — server-only\n```\n\n### Server `.env`:\n\n```\nCOMETCHAT_APP_ID=your_app_id\nCOMETCHAT_REGION=us\nCOMETCHAT_REST_API_KEY=your_rest_api_key\n```\n\n---\n\n## 9. Security checklist\n\nBefore releasing to production, verify:\n\n- [ ] `COMETCHAT_AUTH_KEY` removed from client `.env` / `app.json extra` / any `EXPO_PUBLIC_*` var\n- [ ] Production provider uses `authToken` prop, not `authKey`\n- [ ] `COMETCHAT_REST_API_KEY` lives only on your backend (check with `grep -r REST_API_KEY src/`)\n- [ ] Token endpoint is behind auth — unauthenticated users can't mint a token for an arbitrary UID\n- [ ] UID derivation on the token endpoint comes from the authenticated session, NOT from the request body (otherwise anyone can mint a token for anyone)\n- [ ] Rate limit on the token endpoint (prevents abuse)\n- [ ] HTTPS-only — no HTTP in production\n- [ ] User CRUD endpoints are authenticated (or called from webhooks with signature verification)\n- [ ] CometChat user deletion happens on account deletion (GDPR / privacy compliance)\n\n---\n\n## 10. Rate limits + retry\n\nCometChat's REST API has rate limits per app. For the token endpoint:\n\n- Default: 100 requests/minute per app\n- Token generation is cheap — if you're hitting limits, you're likely minting too often (e.g. one mint per RN screen mount). Mint once per sign-in, cache client-side, reuse until expiry.\n\nRetry policy:\n\n- 5xx — retry with exponential backoff (1s, 2s, 4s, give up)\n- 4xx — do NOT retry. Surface the error.\n\n---\n\n## 11. Anti-patterns\n\n1. **NEVER ship the REST API Key in an RN bundle.** Not under any env-var name or prefix. Not in `app.json extra`. Not in `EXPO_PUBLIC_*`. Not in a .gitignored file the user commits by accident. If you see yourself writing an env var for `REST_API_KEY` in a client-side config, stop.\n\n2. **NEVER let the client specify the UID to mint a token for.** The server must derive UID from the authenticated session. A `POST /cometchat-token { uid: \"...\" }` that trusts the body is equivalent to no auth — anyone can impersonate anyone.\n\n3. **Don't cache the auth token to disk forever.** It expires. Either re-mint on every cold start or store with a short TTL and refresh on 401.\n\n4. **Don't use `login({ uid })` in production.** `uid` mode requires an Auth Key on the UIKit settings. In production you should set neither `authKey` on the UIKitSettings nor call `login({ uid })` — both are dev-only patterns.\n\n5. **Don't forget user CRUD.** A user who signs up in your app but has no matching CometChat user will get \"user does not exist\" errors on `login({ authToken })`. The token endpoint mints tokens, but the user must already exist in CometChat.\n\n6. **Don't skip the security checklist.** Production bugs in auth are catastrophic.\n\n7. **Don't retry 4xx errors.** Token-endpoint 400s are config mistakes (wrong REST API Key, malformed UID, etc.). Retrying makes it worse.\n\n---\n\n## 12. Verifying production auth works\n\n1. Build a production-configuration version of the app (no Auth Key, only AppId + Region + token endpoint).\n2. Log in as a real user through your app's normal flow.\n3. In the RN debugger network tab, confirm `POST /cometchat-token` returns `{ authToken: \"...\" }`.\n4. Confirm `CometChatUIKit.login({ authToken })` resolves.\n5. Send a message — verify delivery.\n6. Force-close the app, reopen — token fetch + login should happen again on cold start.\n7. (Optional) Wait out the token TTL (default 24hr), verify the 401-refresh path works.\n\nIf any step fails, see `cometchat-native-troubleshooting` § Auth / Token issues.\n\n---\n\n## Skill routing reference\n\n| Skill | When to route |\n|---|---|\n| `cometchat-native-core` | Init / login / provider wrapper chain — prerequisite |\n| `cometchat-native-components` | The base component props (nothing production-specific there) |\n| `cometchat-native-placement` | Where your chat UI goes (no change in production) |\n| `cometchat-native-expo-patterns` | Expo-specific env var wiring (`expo-constants` vs `EXPO_PUBLIC_*`) |\n| `cometchat-native-bare-patterns` | Bare RN env var wiring (`react-native-config`) |\n| `cometchat-native-theming` | Theme customization (independent of auth) |\n| `cometchat-native-features` | Feature flags (polls, extensions, etc. — all still work in prod) |\n| `cometchat-native-customization` | If customization depends on server-side data (user tags, metadata) |\n| `cometchat-native-production` | This skill — server tokens + user CRUD |\n| `cometchat-native-troubleshooting` | 401 on token fetch, \"user does not exist\" on login, token-endpoint rate limit |","tags":["cometchat","native","production","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-native-production","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-native-production","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (24,038 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:54.742Z","embedding":null,"createdAt":"2026-05-07T13:05:15.080Z","updatedAt":"2026-05-18T19:04:54.742Z","lastSeenAt":"2026-05-18T19:04:54.742Z","tsv":"'/api/cometchat-token':504,739,2041,2114,2160,2200 '/auth':997,2030 '/auth_tokens':528,673,789,899,1065 '/cometchat-token':640,1461,2320,2346,2663,2871 '/functions':942 '/middleware/auth':614 '/push/bootstrap':1360 '/src/hooks/usemyappauth':1425 '/src/providers/cometchatprovider':1421 '/supabase':2104 '/v2/auth':1969 '/v2/firestore':1961 '/v2/https':839 '/v3/':168 '/v3/users':1688 '/v3/users/':526 '/v3/users/$':670,786,896,1062,1753,1809 '0':2094 '1':69,169,481,2582,2831 '10':2502 '100':2520 '11':2578 '12':2826 '1s':2566 '2':79,338,493,2639,2849 '24':1524 '24hr':2909 '2s':2567 '3':89,474,506,2678,2862 '4':98,479,532,568,2708,2874 '400s':2811 '401':757,1027,1540,1601,2707,2912,3054 '405':1013 '4a':601 '4b':722 '4c':826 '4d':952 '4s':2568 '4xx':2571,2806 '5':106,1100,2746,2879 '502':814,1086 '5a':1144 '5b':1401 '5c':1512 '5xx':2561 '6':119,1135,1621,2789,2885 '6a':1662 '6b':1722 '6c':1786 '6d':1841 '7':1393,1910,2802,2901 '7a':1935 '7b':2052 '7c':2123 '7d':2179 '7e':2217 '8':2249 '9':2380 'aab':203 'abus':2472 'access':238,468,2121 'accesstoken':2193,2206 'accid':2619 'account':1791,1901,2497 'action':2210 'add':464 'alreadi':591,2785 'also':2056 'alway':38 'android':201 'anonym':1348 'anti':2580 'anti-pattern':2579 'anyon':204,2458,2464,2674,2677 'api':32,84,160,164,254,273,277,326,343,359,403,406,453,458,462,511,522,544,579,633,637,666,686,770,774,782,802,852,880,884,892,912,956,1043,1047,1058,1078,1656,1660,1684,1701,1749,1768,1805,1824,2297,2356,2374,2378,2410,2422,2509,2587,2630,2817 'api.yourapp.com':1460,2319,2345 'api.yourapp.com/cometchat-token':1459,2318,2344 'apikey':531,684,800,910,1076,1699,1766,1822 'apk':202 'apktool':216 'app':162,230,315,358,520,622,625,664,682,735,759,762,780,798,825,846,869,872,890,908,962,971,1032,1035,1056,1074,1409,1432,1500,1590,1614,1631,1682,1697,1747,1764,1803,1820,1874,1890,1904,1918,2000,2260,2333,2336,2364,2367,2514,2523,2759,2840,2858,2890 'app-level':1999 'app.json':428,2330,2395,2604 'app.post':738 'app.tsx':1412 'app/api/cometchat-token/route.ts':981 'appid':681,797,907,1073,1231,1257,1287,1306,1498,1696,1763,1819,2845 'application/json':680,796,906,1072,1468,1695,1762,1818 'approach':1588 'arbitrari':2439 'ask':495 'associ':1344 'async':642,740,854,1001,1179,1277,1555,1669,1730,1794,1974,1991,2075 'auth':14,55,64,80,111,115,172,262,285,295,339,360,380,382,444,459,477,486,488,501,514,562,617,709,993,1428,1453,1517,1860,1863,1912,1920,1937,2024,2034,2054,2221,2226,2247,2279,2349,2389,2429,2673,2683,2720,2799,2829,2842,2925,3010 'auth-provid':110,1911 'auth0':118,491,2180,2188,2209 'authent':648,2450,2484,2659 'authkey':73,181,185,1235,1259,1292,1293,1308,1508,2407,2732 'authopt':995,1021 'author':1469,2045,2118,2164,2204,2239 'authtoken':103,300,536,717,821,930,950,1098,1105,1142,1150,1182,1201,1203,1206,1242,1260,1296,1309,1363,1505,1578,2049,2404,2775,2873,2877 'avatar':1707,1739,1887,1898 'avatarurl':1676,1708 'await':662,694,713,778,817,888,927,945,1017,1054,1094,1189,1196,1224,1285,1294,1361,1365,1571,1576,1680,1718,1745,1782,1801,1839,1981,1993,2033,2039,2050,2079,2090,2108,2112,2154,2158,2194,2198 'awar':1155 'backend':21,36,497,507,585,604,977,1247,1407,1457,1652,1853,1951,2067,2167,2207,2302,2315,2416 'backoff':2565 'bare':2991,2993 'base':2950 'bearer':1470,2046,2119,2165,2205,2240 'before/after':1902 'behind':2428 'bespok':2220 'blast':306 'bodi':654,688,804,914,1080,1703,1770,1826,2456,2668 'bootstrappushafterlogin':1358,1366,1397 'bug':2797 'build':401,2832 'built':330 'bun':726 'bundl':194,395,427,2294,2324,2592 'c':741 'c.env.cometchat':761,766,772 'c.get':744 'c.json':754,809,820 'cach':2552,2681 'call':269,298,508,534,933,1530,1846,1855,1867,2486,2737 'catastroph':2801 'catch':1300,1479 'chain':131,2943 'chang':1126,1645,1728,2968 'chat':2964 'cheap':2527 'check':2417 'checklist':121,2382,2795 'children':1252,1262,1316 'claud':42 'clerk':117,490,2124,2126,2170 'clerk/clerk-expo':2147 'client':100,261,292,386,394,482,494,533,539,556,932,1102,1115,2009,2099,2141,2237,2254,2262,2267,2271,2276,2282,2308,2313,2323,2327,2393,2554,2635,2643 'client-sid':99,385,1101,2266,2275,2553,2634 'close':2888 'cloud':828,1953 'cloudflar':724 'cold':2696,2899 'come':352,1923,2447 'cometchat':2,49,126,229,270,355,500,509,558,697,845,848,850,1132,1329,1371,1390,1452,1482,1499,1503,1638,1714,1778,1835,1932,1949,2019,2064,2246,2259,2269,2278,2295,2310,2332,2338,2341,2348,2354,2363,2369,2372,2388,2408,2492,2506,2764,2788,2922,2936,2946,2959,2972,2989,3003,3012,3026,3041,3051 'cometchat-native-bare-pattern':2988 'cometchat-native-compon':2945 'cometchat-native-cor':125,1131,2935 'cometchat-native-custom':3025 'cometchat-native-expo-pattern':2971 'cometchat-native-featur':3011 'cometchat-native-plac':2958 'cometchat-native-product':1,3040 'cometchat-native-push':1389 'cometchat-native-them':3002 'cometchat-native-troubleshoot':2921,3050 'cometchat-token':2018 'cometchat.addconnectionlistener':1549 'cometchat.connectionlistener':1553 'cometchat.io':167,525,669,785,895,1061,1687,1752,1808 'cometchat.io/v3/':166 'cometchat.io/v3/users':1686 'cometchat.io/v3/users/':524 'cometchat.io/v3/users/$':668,784,894,1060,1751,1807 'cometchat.removeconnectionlistener':1581 'cometchat/chat-uikit-react-native':1170 'cometchatprovid':1129,1256,1419,1497 'cometchatprovider.tsx':1152 'cometchattoken':1438,1494,1506 'cometchatuikit':1168 'cometchatuikit.getloggedinuser':1190 'cometchatuikit.init':184,1286 'cometchatuikit.login':102,177,299,535,1104,1205,1211,1577,2876 'cometchatuikit.logout':1377 'commit':436,2617 'complianc':2501 'compon':2948,2951 'config':2637,2813,3001 'configur':1521,2836 'confirm':2869,2875 'confus':350 'connect':1556 'console.error':696,1481 'const':618,621,627,631,657,660,692,711,734,742,758,764,768,776,815,841,865,868,874,878,886,925,943,949,1015,1031,1037,1041,1049,1052,1092,1187,1264,1269,1433,1437,1543,1569,1678,1743,1799,1971,1976,1988,2031,2037,2048,2077,2086,2105,2110,2148,2152,2156,2189,2192,2196 'constant':2984 'content':678,794,904,1070,1466,1693,1760,1816 'content-typ':677,793,903,1069,1465,1692,1759,1815 'context':1383 'convers':240 'core':128,1134,2938 'cover':68 'creat':1635,1663,1716 'createcometchatus':1671,1879,1982,2091 'createcontext':1159 'creation':1877 'cross':157 'cross-platform':156 'crud':18,67,108,416,1624,1848,2174,2215,2481,2751,3049 'currentus':2035 'custom':418,492,2218,2228,3007,3028,3030 'custom-message-send':417 'dashboard':356,375,457 'data':712,816,926,1093,1476,2106,3036 'data.authtoken':1478 'data.data.authtoken':718,822,931,1099 'debugg':2866 'default':720,824,1000,1430,1523,2519,2908 'delet':1647,1787,1792,1813,1837,1900,1907,2494,2498 'deletecometchatus':1796,1908,1994 'deliveri':2884 'deno.serve':2074 'depend':3031 'deriv':645,2442,2655 'dev':53,72,175,392,396,1213,1237,1250,2281,2743 'dev-mod':52 'dev-on':2742 'devic':1351 'differ':257,346,366,370 'disk':2686 'displaynam':1978,1984 'dissoci':1385 'docs/ui-kit/react-native/methods.mdx':153 'drop':1557 'e':1301,1304,1480,1486 'e.g':2317,2539 'easi':348 'edg':2069 'either':2690 'els':1207,1215 'email':553,2088 'email.split':2093 'embed':189 'encodeuricompon':671,787,897,1063,1754,1810 'end':1014 'endpoint':91,109,570,2021,2312,2343,2426,2446,2470,2482,2518,2778,2810,2848,3066 'engin':212 'ensureloggedin':1181,1295,1323,1362 'env':2304,2328,2362,2394,2597,2626,2979,2995 'env-var':2596 'environ':2250 'equival':2670 'error':693,699,700,705,755,810,1029,1088,1270,1713,1777,1834,2577,2772,2807 'etc':2821,3019 'even':958 'event':1861,1864,1975,1992,2078,2139,2178 'event.data':1980 'event.data.uid':1995 'event.record':2089 'event.table':2084 'event.type':2082 'ever':2309 'everi':239,2695 'exact':316 'exist':616,969,1188,1192,1427,1859,2771,2786,3061 'expir':1561,2689 'expiri':1515,1528,1547,2558 'expo':430,2125,2129,2398,2608,2974,2977,2983,2986 'expo-const':2982 'expo-friend':2128 'expo-specif':2976 'exponenti':2564 'export':719,823,840,999,1254,1429,1970,1987 'express':23,93,602,610 'extens':3018 'extern':20,39 'external-backend':19 'extra':429,2331,2396,2605 'extract':206,2243 'fail':706,813,924,1091,1485,1532,1717,1781,1838,2919 'fallback':1214 'fals':1173,1268 'fcm/apns':1368 'featur':3014,3015 'fetch':663,779,889,1055,1117,1402,1450,1458,1484,1564,1681,1746,1802,2040,2113,2159,2199,2893,3057 'fetchcometchattoken':1572 'file':437,2614 'final':1226 'firebas':25,95,114,487,827,837,941,1936,1938,1952,1959,1967,2012,2029 'firebase-funct':836,1958,1966 'first':129 'flag':3016 'flow':333,1410,1619,2861 'forc':1595,1610,2887 'force-clos':2886 'forev':2687 'forget':2749 'fresh':600 'freshtoken':1570,1575,1579 'friend':2130 'full':467,1396 'function':26,96,829,838,936,946,1002,1180,1255,1278,1431,1670,1731,1795,1849,1868,1954,1960,1968,2070 'functions/src/cometchat-token.ts':831 'gdpr':2499 'generat':248,414,450,708,2525 'get':512,2010,2767 'getcometchattoken':842,948 'getcredenti':2190,2195 'getidtoken':2036 'getserversess':989,1018 'gettoken':2149,2155 'git':435 'git-commit':434 'gitignor':2613 'give':2569 'goe':2966 'grep':2419 'ground':151 'handl':1513,1533 'handler':1003,1862,2008 'happen':1649,2495,2896 'header':530,676,792,902,1068,1464,1691,1758,1814,2044,2117,2163,2203 'helper':1399 'hit':2531 'hold':251,560 'hono':24,94,723,731,733,737 'hook':2002 'hour':1525 'http':2477 'https':2474 'https-on':2473 'httpscallabl':947 'httpserror':834,860,920 'id':163,521,623,626,665,683,760,763,781,799,847,870,873,891,909,1033,1036,1057,1075,1501,1545,1551,1583,1683,1698,1748,1765,1804,1821,2013,2087,2092,2261,2334,2337,2365,2368 'idtoken':2032,2047 'imperson':2676 'import':607,611,730,832,935,982,988,994,1157,1167,1357,1413,1418,1422,1955,1962,2023,2101,2144,2182 'includ':2238 'independ':3008 'init':2939 'init/login/wrapper':130 'initi':1172,1282,1283 'insert':2083 'integr':50,113,1914 'interfac':1229 'intern':921 'io':198 'ipa':199 'isauthent':1435,1446,1487,1491 'isn':963 'isreadi':1265,1312 'issu':1939,2057,2927 'json':704,1028,1087 'json.stringify':689,805,915,1081,1704,1771,1827 'jwt':1489,1585,2153,2166,2219,2231 'jwts':322 'key':56,81,85,187,255,258,263,278,340,344,347,361,372,381,383,404,407,445,454,460,463,465,545,567,634,638,687,771,775,803,853,881,885,913,1044,1048,1079,1661,1702,1769,1825,2280,2298,2350,2357,2375,2379,2390,2411,2423,2588,2631,2721,2818,2843 'land':1319 'layer':1921 'leak':304 'let':1171,1174,2641 'level':2001 'lifecycl':147,2138,2177 'like':2535 'limit':123,2466,2504,2512,2532,3068 'listen':1544,1548,1550,1582 'live':87,284,364,379,1850,2412 'locat':2257 'log':222,483,1219,1337,1605,2850 'logged-in':1336 'login':266,332,389,1343,1568,1598,2712,2738,2774,2894,2940,3063 'logininflight':1175,1195,1197,1204,1210,1225,1227 'make':2823 'malform':2819 'manag':17,1623 'match':1637,2763 'matter':173 'messag':233,419,2882 'metadata':1741,3039 'method':674,790,900,1066,1462,1689,1756,1812,2042,2115,2161,2201 'middlewar':748 'middleware-resolv':747 'might':1559 'mint':13,63,280,812,923,1090,1538,2245,2434,2460,2536,2541,2546,2648,2693,2779 'mistak':2814 'mode':54,176,393,2717 'model':320 'mount':2545 'move':45 'must':242,2654,2784 'name':1674,1706,1737,1885,1897,2599 'name/avatar':1644 'nativ':3,10,48,127,193,940,1133,1391,2028,2143,2187,2923,2937,2947,2960,2973,2990,3000,3004,3013,3027,3042,3052 'need':448,582,1380,1633 'neither':2731 'network':2867 'never':398,423,540,548,2288,2305,2583,2640 'new':736,859,919,1552,1712,1776,1833,2096 'newuser.avatarurl':1882 'newuser.id':1880 'newuser.name':1881 'next':987,992 'next-auth':991 'next.js':577,955,965 'nextapirequest':984,1005 'nextapirespons':985,1007 'nextauth':998 'node':727 'node.js':603 'normal':2860 'noth':1217,2953 'null':1177,1178,1228,1274,1275,1314,1442,1443,1448 'often':972,2538 'ok':2098,2265,2274 'omit':1239 'onaccountdelet':1989 'oncal':833,843 'ondisconnect':1554 'ondocumentcr':1956 'one':138,309,588,1925,2540 'onsignup':1972 'onusercr':1963,1973 'onuserdelet':1964,1990 'option':2902 'otherwis':2457 'pages/api/cometchat-token.ts':979 'pair':1400 'partial':1736 'pass':182 'password':551 'path':2914 'pattern':478,2233,2581,2745,2975,2992 'per':1942,2513,2522,2542,2548 'perman':1828 'photourl':1979,1986 'pick':466,586 'place':367 'placement':2961 'platform':158 'polici':2560 'poll':3017 'post':503,519,675,791,901,1010,1067,1463,1690,2043,2116,2162,2202,2662,2870 'prefer':1200 'prefix':2601 'prerequisit':150,2944 'prevent':2471 'privaci':2500 'privat':232 'privileg':371 'process.env.cometchat':624,629,635,871,876,882,1034,1039,1045 'prod':656,3024 'product':4,6,59,78,135,171,241,400,1154,1199,1241,1244,1511,2286,2292,2325,2352,2386,2401,2479,2715,2727,2796,2828,2835,2955,2970,3043 'production-awar':1153 'production-configur':2834 'production-readi':5,58 'production-specif':2954 'profil':1642,1727,1892,1996,2006 'profile-upd':2005 'project':440,573 'promis':1176,1186 'prop':139,1139,1143,1230,1263,1509,2405,2952 'provid':112,142,146,1147,1913,2402,2941 'public':431,2399,2609,2987 'purpos':40,376 'push':1317,1332,1355,1392 'put':1757 'r':661,777,887,1053,1473,1679,1744,1800,2038,2111,2157,2197,2420 'r.json':714,818,928,1095,1474,1721,1785,2051 'r.ok':691,807,917,1083,1710,1774,1831 'r.status':703 'r.text':695,1719,1783,1840 'radius':307 'rate':122,2465,2503,2511,3067 're':598,1537,1563,1567,1597,2530,2534,2692 're-fetch':1562 're-login':1566,1596 're-mint':1536,2691 'react':9,47,192,939,1158,1166,1417,2027,2142,2186,2999 'react-native-auth0':2185 'react-native-config':2998 'react-native-firebas':938,2026 'reactnod':1164,1253 'read':124,231 'readi':7,60 'real':2854 'receiv':293,1354 'recip':22,92,571,1915 'refer':2930 'refresh':105,1516,2705,2913 'region':165,523,628,630,667,765,767,783,849,875,877,893,1038,1040,1059,1233,1258,1288,1307,1502,1504,1685,1750,1806,2270,2339,2370,2846 'regist':1341,1367 'registr':1318 'releas':2384 'remov':2284,2391 'reopen':2891 'req':643,1004,1019,2076 'req.json':2080 'req.method':1009 'req.user.id':659 'request':653,855,2455 'request.auth':857 'request.auth.uid':867 'requests/minute':2521 'requir':864,2718 'requireauth':612,641 'res':644,1006,1020 'res.json':716,1097 'res.status':702,1012,1026,1085 'resolv':749,1324,2878 'respons':2097 'rest':83,159,253,272,276,325,342,402,405,452,461,510,543,632,636,685,769,773,801,851,879,883,911,1042,1046,1077,1655,1659,1700,1767,1823,2296,2355,2373,2377,2409,2421,2508,2586,2629,2816 'result':944 'result.data.authtoken':951 'retri':2505,2559,2562,2574,2805,2822 'return':701,715,753,808,819,929,1011,1025,1084,1096,1193,1198,1216,1313,1315,1449,1492,1495,1496,1580,1720,1784,2095,2872 'reus':974,2556 'revers':211 'reverse-engin':210 'reverseapk':217 'right':1321 'rn':29,426,538,572,961,1114,1917,2293,2326,2543,2591,2865,2994 'rout':33,580,957,2929,2934 'router':608,619,620,721 'router.post':639 'rule':2212 'safe':2321 'scope':1331 'screen':2544 'sdk':388,559,1330,1379,1529,1604 'sdks':1928 'secret':844 'secur':120,2381,2794 'see':541,1130,1388,2622,2920 'send':234,420,2015,2880 'separ':584 'serv':1110 'server':12,62,90,245,250,268,410,412,421,471,547,569,1108,2241,2255,2264,2273,2299,2359,2361,2653,3034,3046 'server-mint':11,61 'server-on':2358 'server-sid':244,470,3033 'server-to-serv':409 'server/cometchat-token.ts':729 'server/routes/cometchat-token.ts':606 'serverless':28,954 'session':311,649,1016,1023,2107,2120,2451,2660 'session.user.id':1051 'set':2725,2730 'setcometchattoken':1439,1447,1477,1574 'seterror':1271,1302 'setisreadi':1266,1298,1326,1372 'setup':1279,1305 'ship':76,549,2289,2306,2584 'short':283,2702 'short-liv':282 'side':101,246,387,472,1103,2268,2277,2555,2636,3035 'sign':197,862,1617,1627,1870,2550,2755 'sign-in':1616,2549 'signatur':2490 'signup':1667,2073 'simpler':1587 'simplest':595 'skill':134,2928,2931,3045 'skill-cometchat-native-production' 'skip':2792 'someon':1626 'source-cometchat' 'specif':289,2956,2978 'specifi':2644 'split':2252 'src':2424 'standard':209 'start':599,1531,2697,2900 'static':566 'step':480,2918 'still':3021 'stop':2638 'store':2699 'string':215,1183,1185,1232,1234,1236,1243,1249,1273,1303,1441,1673,1675,1677,1734,1738,1740,1798 'style':578 'subscriptiontyp':1289 'succeed':1878,1894 'supabas':116,489,2053,2055,2068,2102 'supabase.auth.getsession':2109 'support':1149 'surfac':2575 'swap':137,1136 'tab':2868 'tabl':384,408 'tag':3038 'teach':41 'theme':3005,3006 'threat':319 'throw':858,918,1711,1775,1832 'token':15,65,104,247,286,296,303,413,476,502,515,563,698,710,811,922,1089,1111,1119,1333,1346,1369,1387,1404,1454,1483,1514,1518,1527,1546,1558,2014,2020,2122,2248,2311,2342,2425,2436,2445,2462,2469,2517,2524,2650,2684,2777,2780,2809,2847,2892,2906,2926,3047,3056,3065 'token-endpoint':2808,3064 'toler':1593 'tool':213 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'tri':1223,1280 'trigger':2071,2173,2214 'troubleshoot':2924,3053 'true':1284,1299,1327,1373,1829 'trust':2666 'truth':152 'ts':605,728,830,978,1668,1729,1793,1950,2066 'tsx':934,1151,1356,1411,1541,2022,2100,2140,2181 'ttl':1522,2703,2907 'two':345 'type':679,795,905,983,1071,1163,1467,1694,1761,1817 'typic':1408,1922 'ui':2965 'uid':178,290,390,505,518,527,646,658,672,866,898,1050,1064,1138,1184,1209,1212,1248,1261,1297,1310,1364,1672,1705,1733,1755,1797,1811,1941,1947,1977,1983,2059,2065,2244,2440,2441,2646,2656,2664,2713,2716,2739,2820 'uikit':2724 'uikitset':2735 'unauthent':861,2430 'unauthor':756,1030 'understand':144 'unregist':1375 'unregisterpushtokenonlogout':1398 'unzip':214 'updat':1112,1145,1643,1723,1735,1772,1780,1884,1893,1997,2007 'updatecometchatus':1732,1895 'url':2316 'us':2340,2371 'use':179,219,243,473,1121,1653,1944,2060,2168,2208,2403,2711 'useauth':2145,2151 'useauth0':2183,2191 'usecontext':1160 'useeffect':1161,1276,1415,1444,1542 'usemyappauth':1423,1436 'user':16,66,107,226,237,265,310,415,447,590,743,745,750,752,967,1024,1291,1339,1382,1434,1488,1584,1607,1622,1639,1665,1715,1725,1779,1789,1836,1869,1876,1883,1899,1906,1943,1985,2085,2137,2176,2224,2431,2480,2493,2616,2750,2753,2765,2768,2783,2855,3037,3048,3058 'user.id':788,1896,1909 'user.jwt':1471,1573 'userid':2150 'usest':1162,1267,1272,1414,1440 'valid':2242 'var':432,2400,2598,2627,2980,2996 'variabl':2251,2256 've':329 'vercel':27,97,953 'verif':2491 'verifi':2387,2827,2883,2910 'version':1156,2837 'visibl':2258 'vs':82,341,2985 'wait':2903 'web':970 'webhook':2135,2171,2216,2488 'whole':314 'wire':1844,1931,2981,2997 'won':1352 'work':2830,2915,3022 'worker':725 'wors':2825 'wrapper':2942 'write':2624 'wrong':2815 'yet':1222","prices":[{"id":"4d21fc28-5f64-4bcb-aa57-25cfd943c5d0","listingId":"ce32cdc3-9705-49dc-885a-87a2346ed11a","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-07T13:05:15.080Z"}],"sources":[{"listingId":"ce32cdc3-9705-49dc-885a-87a2346ed11a","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-native-production","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-native-production","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:15.080Z","lastSeenAt":"2026-05-18T19:04:54.742Z"}],"details":{"listingId":"ce32cdc3-9705-49dc-885a-87a2346ed11a","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-native-production","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"3de5b25c861ae36e8403760224dc05104a73004d","skill_md_path":"skills/cometchat-native-production/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-native-production"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-native-production","license":"MIT","description":"Production-readiness for React Native — server-minted auth tokens, user management CRUD, external-backend recipes (Express / Hono / Firebase Functions / Vercel Serverless). RN has no API routes, so the backend is always external.","compatibility":"Node.js >=18; React Native >=0.70; @cometchat/chat-uikit-react-native ^5"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-native-production"},"updatedAt":"2026-05-18T19:04:54.742Z"}}