Skip to main content

Add CloudBase Authentication to WeChat Mini Programs

In one sentence: Use @cloudbase/js-sdk@2.27.3 + @cloudbase/adapter-wx_mp@1.3.1 to 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

DependencyVersion
Node.js (local dev + Cloud Function runtime)≥ 16.13
@cloudbase/js-sdk2.27.3
@cloudbase/adapter-wx_mp1.3.1
@cloudbase/node-sdk3.18.1
@cloudbase/clilatest (for deploying Cloud Functions)
WeChat DevTools1.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.json downloaded

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_APPID and WX_SECRET must 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 createTicket is used as customUserId. 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:

  1. 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
  2. Cloud Functions → getLoginTicket → Environment Variables — add WX_APPID, WX_SECRET, TCB_ENV
  3. Authentication → Login Methods — confirm Custom Login is "Enabled" and that the tcb_custom_login.json currently 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

  1. WeChat DevTools → Tools → Build npm
  2. Click Compile in the top-right, open the Console panel
  3. 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.

  1. Go to the CloudBase Console → Authentication → User Management — you should see a new record with a customUserId field equal to the openid from this session.

Common Errors

Error code / messageCauseFix
40029The 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
45011jscode2session 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_TICKETMini Program side signInWithCustomTicket failsUsually tcb_custom_login.json is not the latest copy. Re-download from the Console and redeploy the Cloud Function
customUserId format errorcreateTicket received an empty string or invalid charactersCheck 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 executedWeChat DevTools → Tools → Build npm, then recompile

Error code reference: docs.cloudbase.net/error-code. For WeChat error codes see WeChat Open Docs / Error Codes.

@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:

  1. The Mini Program adapter is now bundled into the main package — no separate install or cloudbase.useAdapters(adapter) call needed.
  2. 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