Sibling skills (local only)
Sibling CloudBase skills ship beside this skill. Use local relative paths such as ../auth-tool-cloudbase/SKILL.md.
If a referenced sibling skill file is missing from this environment, ask the user to install the full CloudBase plugin (or the missing skill). Do not HTTP-fetch remote skill or protocol markdown into the agent context.
Activation Contract
Use this first when
- The task is a CloudBase Web login, registration, session, or user profile flow built with
@cloudbase/js-sdkand the auth provider setup has already been checked.
Read before writing code if
- The user needs a login page, auth modal, session handling, or protected Web route. Read
auth-tool-cloudbasefirst to ensure providers are enabled, then return here for frontend integration.
Then also read
../auth-tool-cloudbase/SKILL.mdfor provider setup../web-development/SKILL.mdfor Web project structure and deployment
Do not start here first when
- The request is a Web auth flow but provider configuration has not been verified yet.
- In that case, activate
auth-tool-cloudbasebeforeauth-web-cloudbase.
Do NOT use for
- Mini program auth, native App auth, or server-side auth setup.
Common mistakes / gotchas
Skipping publishable key and provider checks.
Replacing built-in Web auth with cloud function login logic.
Reusing this flow in Flutter, React Native, or native iOS/Android code.
Creating a detached helper file with
auth.signUp/verifyOtpbut never wiring it into the existing form handlers, so the actual button clicks still do nothing.Using
signInWithEmailAndPasswordorsignUpWithEmailAndPasswordfor username-style accounts such asadminandeditor.Keeping the login or register account input as
type="email"when the task explicitly says the account identifier is a plain username string.Starting implementation before calling
queryAppAuth(action="getLoginConfig")and enablingusernamePasswordwhen it is still off.Writing
auth.signInWithPassword(...)orauth.signUp(...)code without first confirming the provider is enabled via MCP. Before writing any sign-in or sign-up code in the browser, callqueryAppAuth(action="listProviders")to verify the target provider (e.g.email,phone,usernamePassword) hasOn: "TRUE". For email-based sign-up (auth.signUp({ email, password })), additionally confirm SMTP is configured — otherwise the provider may throw"provider email not found"or similar errors. For username/password login, useauth.signInWithPassword({ username, password }); registration is best done through the management API (manageAppAuth(action="createUser")) or by confirming email provider readiness first.Treating
auth.getUser()or deprecatedauth.getLoginState()as proof of real login. When the SDK is initialized withaccessKey, the deprecatedgetLoginState()may still return an object with a validuideven without any login — causing route guards that check!!loginStateor!!uidto incorrectly pass. That misleadinguidis not a gateway-authenticated session. Useauth.getSession()instead: it returnsdata.session === undefinedwhen no real login has occurred. Only!!data.sessionfromgetSession()is a reliable authentication check.Assuming publishable
accessKeyalone is enough for NoSQL CRUD. NoSQLapp.database()get/add/update/watchrequires a gateway-authenticated session: use a real login (password / OTP / OAuth). Anonymous login is a demo-only escape hatch for explicitly-public, non-user data — it is disabled by default, denied AI model permissions, and must never stand in for real auth in user-scoped apps. Skipping any login yields gateway 401.checkLogin()/getSession()alone do not create a usable write session.Copying old CloudBase auth snippets from training data. Do not use
auth.getLoginState(),auth.hasLoginState(),auth.getCurrentUser(), orauth.toDefaultLoginPage()as the default Web flow. Use the Web SDK v3 auth methods in this file and provider readiness fromauth-tool-cloudbase.Calling a standalone
auth.verifyOtp({ token })for OTP login. CloudBase Web SDK v3 returnsverifyOtpas a callback on thesignInWithOtp/signUpresult: send the code first, keep the returneddata, then calldata.verifyOtp({ token }). A standaloneauth.verifyOtp({ token })withoutmessageIdfails with"messageId is required"— seeing that error means the callback form was skipped. Seereferences/extended-guide.mdfor the full send → save callback → verify flow.Note: anonymous login is disabled by default for new environments and inactive existing environments. Do not enable it to work around permission errors — enable via
auth-tool-cloudbaseonly when the app explicitly serves public non-user data (e.g. NoSQL read-only demos). Always useauth.getSession()for auth guards.
Overview
Prerequisites: CloudBase environment ID (env)
Prerequisites: CloudBase environment Region (region)
Core Capabilities
Use Case: Web frontend projects using @cloudbase/js-sdk@latest for user authentication
Key Benefits: Supabase-compatible Auth API — all methods return { data, error }, supports phone, email, anonymous (disabled by default), username/password, OAuth, and third-party login methods
📌 Supabase API Compatibility: CloudBase Web SDK v3 auth module is designed with Supabase-like API ergonomics. If you are familiar with
supabase-jsauth patterns, the same mental model applies:
- All methods return
Promise<{ data, error }>— always checkerrorfirstsignInWithPassword,signInWithOtp,signUp,signOut,getSession,getUserfollow the same naming as SupabaseonAuthStateChange(callback)provides reactive auth state observation (events:INITIAL_SESSION,SIGNED_IN,SIGNED_OUT,TOKEN_REFRESHED,USER_UPDATED,PASSWORD_RECOVERY,BIND_IDENTITY)- Session management via
getSession()/refreshSession()/setSession()mirrors Supabase patternsKey differences from Supabase:
- OTP verification: Supabase uses a standalone
auth.verifyOtp({ phone, token, type })call; CloudBase returnsverifyOtpas a callback ondata— calldata.verifyOtp({ token })from thesignInWithOtp/signUpresultaccessKeyreplaces Supabase'sanonKey; environment usesenv+regioninstead of Supabase'surlsignInWithIdTokenfor direct third-party token login (similar to Supabase's same-named method)
Use npm installation for modern Web projects. In React, Vue, Vite, and other bundler-based apps, install and import @cloudbase/js-sdk from the project dependencies instead of using a CDN script.
Prerequisites
- Automatically use
auth-tool-cloudbaseto check app-side auth readiness viaqueryAppAuth/manageAppAuth, then get thepublishable keyand configure login methods. - Publishable key readiness (do not skip): call
queryAppAuth(action="getPublishableKey"). If it is empty, callmanageAppAuth(action="ensurePublishableKey")first — new environments may not have one provisioned, and skipping this step leaves the frontend without a data-plane credential, surfacing later as gateway auth failures instead of an obvious missing-key error. - Persist the key, don't hoard it in conversation: after retrieval, write the publishable key to
.env.localasVITE_PUBLISHABLE_KEY(create the file if missing) and read it in client code viaimport.meta.env.VITE_PUBLISHABLE_KEY. Never hardcode the key into source files, and never ask the user to fetch it from the console — fall back to the console link below only if both MCP calls fail. - If
auth-tool-cloudbasefailed, let user go tohttps://tcb.cloud.tencent.com/dev?envId={env}#/env/apikeyto getpublishable keyandhttps://tcb.cloud.tencent.com/dev?envId={env}#/identity/login-manageto set up login methods
Parameter map
- For username-style identifiers, the required precondition is
loginMethods.usernamePassword === truefromqueryAppAuth(action="getLoginConfig"). If it is false, enable it withmanageAppAuth(action="patchLoginStrategy", patch={ usernamePassword: true })before wiring frontend auth code. - If the conversation only provides an environment alias, nickname, or other shorthand, resolve it with
envQuery(action="list", alias=..., aliasExact=true)first and use the returned canonical fullEnvIdfor SDK init, console links, and generated config. Do not pass alias-like short forms directly intocloudbase.init({ env }). - Treat CloudBase Web Auth as Supabase-like, not “every
supabase-jsauth example is valid unchanged” - When
queryAppAuth/manageAppAuthreturnssdkStyle: "supabase-like"andsdkHints, follow those method and parameter hints first auth.signInWithOtp({ phone })andauth.signUp({ phone })use the phone number in aphonefield, notphone_numberauth.signInWithOtp({ email })andauth.signUp({ email })useemailauth.signInWithPassword({ username, password })is the canonical Web login path for username/password accounts- Treat direct Web
auth.signUp({ username, password })as conditional. VerifysdkHintsand the installed SDK first; some versions only supportsignUpfor OTP/provider-token flows and will not create username/password users. - If the task gives accounts like
admin,editor, or another plain string without@, treat it as a username-style identifier rather than an email address data.verifyOtp({ token })— theverifyOtpcallback on thesignInWithOtp/signUpresultdata— expects the SMS or email code intoken; do not invent a standaloneauth.verifyOtp({ token })call, which additionally requiresmessageIdaccessKeyis the publishable key fromqueryAppAuth/manageAppAuthviaauth-tool-cloudbase, not a secret keyaccessKeyalone does not create a gateway-authenticated session. PublishableaccessKeyinitializes the SDK; it does not replace a login for NoSQL CRUD. Anyapp.database()get/add/update/watchneeds a session — prefer a real login (password / OTP / OAuth);signInAnonymously()only for explicitly-public demo data (disabled by default, denied AI model permissions). Otherwise the gateway returns 401. Separately: the deprecatedauth.getLoginState()may still return a misleadinguidwithout login; useauth.getSession()for route guards (data.session === undefinedwhen not logged in).checkLogin()/getSession()alone do not create a usable write session.- Never set
accessKeytoenvId, a username, or any placeholder string. If you do not have a real Publishable Key yet, do not fabricate one. - If the task mentions provider setup, stop and read
auth-tool-cloudbasebefore writing frontend code
Quick Start
SDK init reference: docs.cloudbase.net/api-reference/webv3/initialization.md(URL 加 .md 可取 raw markdown 原文)
// npm install @cloudbase/js-sdk
import cloudbase from '@cloudbase/js-sdk'
const app = cloudbase.init({
env: 'your-full-env-id', // Canonical full CloudBase environment ID resolved from envQuery or the console, not an alias or shorthand
region: 'ap-shanghai', // CloudBase environment Region, default 'ap-shanghai'
accessKey: 'publishable key', // required, get from auth-tool-cloudbase
// ⚠️ accessKey alone ≠ a login session. NoSQL CRUD needs a session —
// real login preferred; signInAnonymously() only for public demo data.
// Use auth.getSession() for route guards; deprecated getLoginState()
// may return a misleading uid without a real session.
auth: { detectSessionInUrl: true }, // required
})
const auth = app.auth
// NoSQL app.database() CRUD requires a session (js-sdk 3.x + publishable key).
// Real login (see cookbook). Anonymous, only for public non-user demos:
// const { error } = await auth.signInAnonymously()
// if (error) throw error
If the current task has not retrieved a real Publishable Key, omit accessKey instead of inventing one. A wrong accessKey can break auth-state checks and protected-route behavior.
Auth code cookbook (official v3 API — copy these, do not re-derive from .d.ts)
Every method returns the unified shape { data, error } — branch on error first and surface error.message. The auth API is identical in traditional and PG environments. Source: official auth docs(raw markdown, cross-check snippets there when in doubt).
Default auth UI contract: when the user asks for 登录/注册/账号体系/user system without restricting the method, the login page must make ALL of these reachable (tabs or separate forms): password sign-in, OTP sign-in, verified sign-up (code + password), and forgot-password (whenever password sign-in exists). Never ship OTP-only or password-only UI unless explicitly asked. Never reveal whether an identifier is already registered in user-facing copy — route existing users to login with neutral wording.
Password sign-in (username-style or email identifiers both go here):
const { data, error } = await auth.signInWithPassword({ username, password })
// email accounts: auth.signInWithPassword({ email, password })
if (error) { /* show error.message */ } else { /* data.user */ }
Anonymous sign-in — demo-only, not a default. NoSQL app.database() CRUD needs some session (PG anon reads work with accessKey alone). Prefer a real login; reach for anonymous ONLY when the app explicitly serves public non-user data and the user accepts the trade-off — it is disabled by default, denied AI model permissions, and its uid must never own user-scoped rows:
const { error } = await auth.signInAnonymously()
Registration — verification code is MANDATORY. There is no password-only signup: signUp itself sends a code, and data.verifyOtp must complete it. Smart flow: existing identifier → plain login; new identifier → register + auto-login. Phone/SMS is 上海地域 only — prefer email:
const { data, error } = await auth.signUp({ email, password }) // or { phone, password }
if (error) throw error
// user types the code from their inbox...
const { data: login, error: verifyErr } = await data.verifyOtp({ token: code })
// login.user / login.session — signed in on both paths
OTP sign-in (no password) — same shape as signUp, auto-creates the user by default (shouldCreateUser: false to refuse unknown users). Requires 邮箱/短信验证码登录 enabled in console → 身份认证/登录方式:
const { data, error } = await auth.signInWithOtp({ email }) // or { phone }
const { data: login, error: verifyErr } = await data.verifyOtp({ token: code })
Forgot password — email code → set new password → auto sign-in (emits PASSWORD_RECOVERY):
const { data, error } = await auth.resetPasswordForEmail(email)
if (error) throw error
const { data: login, error: resetErr } = await data.updateUser({ nonce: code, password: newPassword })
OTP closure vs standalone verifyOtp — do not mix. The data.verifyOtp returned by signUp / signInWithOtp / resetPasswordForEmail has the message ID bound (pass only { token }). The standalone auth.verifyOtp(...) requires messageId and only logs in — it never registers. Always use the returned closure.
Session check / route guard — always getSession(), never the deprecated getLoginState():
const { data } = await auth.getSession()
const session = data?.session // undefined === not logged in
Auth state listener (wire this once at app bootstrap):
auth.onAuthStateChange((event, session) => {
// event: INITIAL_SESSION | SIGNED_IN | SIGNED_OUT | PASSWORD_RECOVERY
// | TOKEN_REFRESHED | USER_UPDATED | BIND_IDENTITY
})
Sign out:
const { error } = await auth.signOut()
Mandatory auth gate before user-scoped data. Before reading/writing user-owned PG rows or Storage objects, check the session and show login when absent — never "fix" data errors by silently calling signInAnonymously:
const { data } = await auth.getSession()
if (!data?.session) { navigate('/login'); return }
Completion Bar — before calling the auth task done, the generated source must have ALL of:
-
signInWithPassword(when password login is part of the UI) - a verification-code path:
signUp+data.verifyOtpand/orsignInWithOtp - auth gate before user-scoped DB/Storage calls (rule above)
-
onAuthStateChangewired at bootstrap (route guard reacts toSIGNED_OUT) - errors surfaced from
error.message, no invented error text - NO
signInAnonymouslyas a fallback for permission errors, no mock/localStorage sessions
Extended guide
For detailed scenarios, examples, and patterns, read extended-guide.md.
Reference index
All packaged reference files (required for skill lint reachability):
- extended-guide.md