Add CloudBase Authentication to WeChat Mini Programs
In one sentence: Use
@cloudbase/js-sdk@2.27.3+@cloudbase/adapter-wx_mp@1.3.1to let a WeChat Mini Program log in to a standalone CloudBase Environment using a ticket issued by a Cloud Function. After login, the frontend can directly access that Environment's database, Cloud Storage, and Cloud Functions.Estimated time: 25 minutes | Difficulty: Advanced
Applicable Scenarios
This recipe covers the scenario where you already have a standalone CloudBase Environment (visible at tcb.cloud.tencent.com/dev with its env-id) and want to connect a WeChat Mini Program as the frontend.
- Applicable: Standalone CloudBase Environment + WeChat Mini Program frontend
- Not applicable: Using the "Cloud Development" (
wx.cloud) built into WeChat DevTools. That is WeChat-native Cloud Development — a separate system. Follow the WeChat official docs directly; this recipe does not apply. - Not applicable: Web, UniApp, Taro, etc. — each has its own recipe.
The two systems share the same name but are not the same thing. This distinction is easy to miss at first, so it's stated upfront.
Prerequisites
| Dependency | Version |
|---|---|
| Node.js (local dev + Cloud Function runtime) | ≥ 16.13 |
@cloudbase/js-sdk | 2.27.3 |
@cloudbase/adapter-wx_mp | 1.3.1 |
@cloudbase/node-sdk | 3.18.1 |
@cloudbase/cli | latest (for deploying Cloud Functions) |
| WeChat DevTools | ≥ 1.06.x (must support "Build npm") |
Additionally:
- A provisioned CloudBase Environment ID
- WeChat Mini Program AppID + AppSecret from WeChat Open Platform or Official Accounts Platform
- Custom Login enabled in the CloudBase Console under Authentication → Login Methods, with the private key file
tcb_custom_login.jsondownloaded
Step 1: Install Dependencies
Mini Program frontend — navigate to the directory containing app.js (typically miniprogram/):
cd miniprogram
npm init -y
npm install --save @cloudbase/js-sdk@2.27.3 @cloudbase/adapter-wx_mp@1.3.1
After installation, click Tools → Build npm in WeChat DevTools. This step is required — without it, the Mini Program runtime cannot find the packages and will throw require is not defined.
Cloud Function directory — set up separately:
mkdir -p cloudfunctions/getLoginTicket
cd cloudfunctions/getLoginTicket
npm init -y
npm install --save @cloudbase/node-sdk@3.18.1
Place the tcb_custom_login.json downloaded from the Console into the cloudfunctions/getLoginTicket/ directory. This file contains an admin-level signing private key — do not commit it to a public repository. Add a line to .gitignore.
Step 2: Write the Cloud Function to Issue a Ticket from wx.login Code
cloudfunctions/getLoginTicket/index.js:
const cloudbase = require('@cloudbase/node-sdk');
const https = require('https');
const WX_APPID = process.env.WX_APPID;
const WX_SECRET = process.env.WX_SECRET;
const TCB_ENV = process.env.TCB_ENV;
const app = cloudbase.init({
env: TCB_ENV,
credentials: require('./tcb_custom_login.json'),
});
exports.main = async (event, context) => {
const { code } = event;
if (!code) {
return { error: 'MISSING_CODE', message: 'event.code is required' };
}
let session;
try {
session = await jscode2session(code);
} catch (e) {
return { error: 'WX_API_UNREACHABLE', message: e.message };
}
if (session.errcode) {
return {
error: 'WX_API_ERROR',
errcode: session.errcode,
message: session.errmsg,
};
}
const { openid, unionid } = session;
const ticket = app.auth().createTicket(openid, {
refresh: 3600 * 1000, // access_token refresh interval: 1 hour (milliseconds)
});
return { ticket, openid, unionid };
};
function jscode2session(code) {
return new Promise((resolve, reject) => {
const url =
`https://api.weixin.qq.com/sns/jscode2session` +
`?appid=${WX_APPID}` +
`&secret=${WX_SECRET}` +
`&js_code=${code}` +
`&grant_type=authorization_code`;
https
.get(url, (res) => {
let raw = '';
res.on('data', (chunk) => (raw += chunk));
res.on('end', () => {
try {
resolve(JSON.parse(raw));
} catch (err) {
reject(err);
}
});
})
.on('error', reject);
});
}
Two points that are easy to overlook:
WX_APPIDandWX_SECRETmust be injected via environment variables. Once AppSecret is committed to a repository, rotating it is far more painful than setting up env vars — don't hardcode it.- The first argument of
createTicketis used ascustomUserId. A real WeChat openid is 28 alphanumeric characters, satisfying the "4–32 characters, letters/digits/select symbols" rule, so it can be passed directly.
Step 3: Deploy the Cloud Function and Enable HTTP Access
Deploy with the CloudBase CLI:
tcb login
tcb fn deploy getLoginTicket --dir ./cloudfunctions/getLoginTicket -e your-env-id
After deployment, complete three tasks in the Console:
- Cloud Functions → getLoginTicket → Trigger Methods — add an "HTTP Access Service" trigger, allow
POST. Copy the generated access URL, e.g.https://your-env.service.tcloudbase.com/getLoginTicket - Cloud Functions → getLoginTicket → Environment Variables — add
WX_APPID,WX_SECRET,TCB_ENV - Authentication → Login Methods — confirm Custom Login is "Enabled" and that the
tcb_custom_login.jsoncurrently deployed is the most recently generated one (each time you regenerate, the old private key expires in 2 hours — this is mentioned again later)
Step 4: Initialize the SDK on the Mini Program Side and Complete Login
miniprogram/libs/cloudbase.js:
import cloudbase from '@cloudbase/js-sdk';
import adapter from '@cloudbase/adapter-wx_mp';
// adapter must be registered before init
cloudbase.useAdapters(adapter);
const app = cloudbase.init({
env: 'your-env-id',
});
export const auth = app.auth();
export default app;
miniprogram/libs/login.js:
import { auth } from './cloudbase';
const TICKET_URL = 'https://your-env.service.tcloudbase.com/getLoginTicket';
export async function ensureLogin() {
if (auth.hasLoginState()) {
return auth.currentUser;
}
// 1. Get code from wx.login
const { code } = await new Promise((resolve, reject) => {
wx.login({ success: resolve, fail: reject });
});
// 2. Exchange code for ticket via the Cloud Function HTTP endpoint
const ticket = await new Promise((resolve, reject) => {
wx.request({
url: TICKET_URL,
method: 'POST',
data: { code },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.ticket) {
resolve(res.data.ticket);
} else {
reject(new Error('Failed to get ticket: ' + JSON.stringify(res.data)));
}
},
fail: reject,
});
});
// 3. Inject ticket and sign in
await auth.setCustomSignFunc(() => Promise.resolve(ticket));
await auth.signInWithCustomTicket();
return auth.currentUser;
}
Call this in miniprogram/app.js inside onLaunch:
import { ensureLogin } from './libs/login';
App({
async onLaunch() {
try {
const user = await ensureLogin();
console.log('[cloudbase] logged in as', user.uid);
this.globalData.user = user;
} catch (err) {
console.error('[cloudbase] login failed', err);
}
},
globalData: {
user: null,
},
});
Verification
- WeChat DevTools → Tools → Build npm
- Click Compile in the top-right, open the Console panel
- Expected output:
[cloudbase] logged in as 138f4c8c-xxxx-xxxx-xxxx-xxxxxxxxxxxx
The uid is a globally unique ID assigned by CloudBase — it is not the same as openid. The openid is WeChat's user identifier; the uid is the primary key for the CloudBase account, used in the owner field for database and Cloud Storage records.
- Go to the CloudBase Console → Authentication → User Management — you should see a new record with a
customUserIdfield equal to the openid from this session.
Common Errors
| Error code / message | Cause | Fix |
|---|---|---|
40029 | The code returned by wx.login has already been used or has expired (a code can only be exchanged once; valid for 5 minutes) | Call wx.login fresh every time you log in — do not cache old codes |
45011 | jscode2session called too frequently (same IP hit rate limit) | Add a ticket cache layer in the Cloud Function, keyed by openid, TTL 5–10 minutes |
INVALID_TICKET | Mini Program side signInWithCustomTicket fails | Usually tcb_custom_login.json is not the latest copy. Re-download from the Console and redeploy the Cloud Function |
customUserId format error | createTicket received an empty string or invalid characters | Check whether the openid field exists in the jscode2session response — if missing, there is already an errcode upstream |
require is not defined (Mini Program side) | "Build npm" was not executed | WeChat DevTools → Tools → Build npm, then recompile |
Error code reference: docs.cloudbase.net/error-code. For WeChat error codes see WeChat Open Docs / Error Codes.
Related Documentation
- SDK Initialization —
@cloudbase/js-sdk@2.xinitialization - Custom Login — full login flow explained
- WeChat Mini Program Adapter —
@cloudbase/adapter-wx_mp - Node SDK / auth.createTicket — server-side ticket issuance
- Cloud Function HTTP Access Service — exposing a Cloud Function as an HTTP endpoint
@next (v3) Changes
If you want to use @cloudbase/js-sdk@3.3.2 (published under the npm @next tag), there are two differences from the code above:
- The Mini Program adapter is now bundled into the main package — no separate install or
cloudbase.useAdapters(adapter)call needed. - All other code (
cloudbase.init,auth.signInWithCustomTicket, etc.) remains the same.
v3 has not yet been promoted from @next to latest. It is less stable than 2.27.3. For production projects, stay on 2.x and migrate once v3 stabilizes.
Next Steps
- Read/write the cloud database after login:
add-database-wechat-miniprogram(coming soon) - Troubleshoot login errors:
fix-auth-wechat-miniprogram - Migrate from Firebase Auth:
migrate-firebase-auth