How to Implement Seamless, Secure Login in WeChat Mini‑Programs
This article explains why user authentication is essential for mobile mini‑programs, outlines security measures using AccessToken and RefreshToken, and provides step‑by‑step techniques—including automatic login, token refresh, request queuing, and user‑state handling—to achieve a frictionless user experience.
Preface
In the mobile Internet era, mobile apps are crucial as a bridge between users and services. Mini‑programs, with their lightweight, convenient, and fast‑startup characteristics, are gradually replacing traditional apps. User authentication is the foundation for all subsequent operations.
User Login Necessity
Security: only authenticated users can access their data.
Personalized customization: push content tailored to specific user behavior.
Cross‑device sync: identify users across devices via unique identifiers such as phone number, WeChat OpenId, etc.
Data analysis: collect behavior data to evaluate product impact and guide improvements.
Social interaction: enable user‑to‑user interaction and increase product reach.
How to Ensure Security
We use AccessToken and RefreshToken for API authentication. AccessToken has a short lifespan to prevent theft, but short life leads to frequent expiration, so RefreshToken is used to obtain a new AccessToken with a longer validity.
Authentication Flow
User Experience Focus
In mini‑programs the only identifier is WeChat, which yields UnionId and OpenId. The optimal experience is “no‑feeling” login. If the product needs a phone number, the user must authorize the binding because platforms tightly control phone number access.
How to Improve User Experience
Now we discuss how to achieve seamless login.
Problems to Solve
Automatic login
Refresh token when authentication fails
Continue user actions after token refresh
Idea
Obtain code via platform API, exchange for UnionId/OpenId on server, then register/login the user.
If API authentication fails, call refreshToken or trigger automatic login again.
Intercept 401 errors with the request library, wait for token reset, then retry the intercepted request.
Solution
Examples are based on WeChat; other platforms are similar.
Automatic Login
wx.login(Object object) | WeChat documentation
const login = async () => {
const { code, errMsg } = await wx.login();
// Call backend to exchange code for user identifier and obtain user info
const user = await loginFunc(code);
}Automatic Token Refresh
Determine when token refresh is not needed in request error interceptor
Non‑401 interface errors
User has logged out voluntarily
Special business‑defined scenarios
Reset User Information
refreshToken
Silent login
Retry Interface
Update token
Retry the 401‑blocked request
/*** Match status code */
const matchStatusCode = (response, statusCode) => {
return [response.status, response.data.code].includes(statusCode);
};
/*** Determine whether to retry request */
const whetherToRetry = async (error, updateConfig) => {
const { user } = rootStore;
if (
!matchStatusCode(error?.response, 401) ||
user.isLogout ||
error?.config?.meta?.reTry === false
)
return false;
await resetUser();
if (!user.token) return false;
updateConfig({
headers: {
Authorization: `Bearer ${user.token}`,
},
});
return true;
};
/*** Reset user */
const resetUser = async () => {
const { user, common } = rootStore;
if (user.isLogin) {
await user.refreshToken();
} else {
await silentLogin();
}
};Continue User Actions
To achieve a seamless experience, we need to preserve the user’s previous operations.
Use userInited to record user reset state.
Use requestQueue to store pending API requests.
After user reset, execute all queued requests.
/*** Reset user */
const resetUser = async () => {
const { user, common } = rootStore;
if (!user.userInited)
return new Promise(resolve => {
common.setRequestQueue([...common.requestQueue, resolve]);
});
user.setUserInited(false);
if (user.isLogin) {
await user.refreshToken();
} else {
await silentLogin();
}
user.setUserInited(true);
}; constructor() {
reaction(
() => this.userInited,
(arg) => {
if (arg) {
const { common } = rootStore;
common.requestQueue.forEach(item => item());
common.setRequestQueue([]);
}
}
);
}Pre‑wait for User Info Completion
If user information is not yet initialized, place authenticated requests into a waiting queue to avoid unnecessary calls.
/*** Wait for user initialization */
const waitForUserInited = async () => {
const { user, common } = rootStore;
if (user.userInited) return Promise.resolve();
// Not initialized, block request in queue
return new Promise(resolve => {
common.setRequestQueue([...common.requestQueue, resolve]);
});
};
http.tap('request', async (config) => {
const { user } = rootStore;
// Authenticated interface
if (!NO_AUTH_URL.includes(config.rawURL)) {
await waitForUserInited();
config.headers.Authorization = `Bearer ${rootStore.user.token}`;
}
});Summary
Security is the foundation of user login; optimizing experience on this basis is essential.
Reducing unnecessary user actions improves perception and product conversion.
When both AccessToken and RefreshToken expire, we perform silent login to obtain a new token; in mini‑programs we use code to get OpenId and link it to private‑domain member data.
Goodme Frontend Team
Regularly sharing the team's insights and expertise in the frontend field
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
