How to Build a Robust and Efficient Login System for WeChat Mini‑Programs

This article presents a comprehensive, step‑by‑step approach to designing a robust, high‑performance login flow for WeChat mini‑programs, covering basic and advanced scenarios such as authorization rejection, session expiration, concurrency control, scenario‑specific optimizations, custom UI, and cross‑program reuse.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
How to Build a Robust and Efficient Login System for WeChat Mini‑Programs

Introduction

Login is a core basic function that uniquely identifies users, enabling tracking services such as favorites, orders, comments, messages, publishing, and personalized recommendations. Because most mini‑program features involve login, the robustness and efficiency of the login process deserve careful attention.

The login process touches many aspects: it can be triggered from various pages and interactions; it must handle user identity verification and backend session maintenance while ensuring security; it must be reusable across multiple scenarios and mini‑programs, yet also customizable for specific pages.

This article explores the main requirements and challenges of mini‑program login, and proposes a progressive, iterative solution that results in a robust and efficient login scheme.

Basic Flow

The basic login flow consists of:

Call the WeChat login interface wx.login to obtain a WeChat login state.

Call the WeChat user‑info interface wx.getUserInfo to obtain user information.

Call the backend login interface, using the WeChat identifier to record and maintain the own user system.

This flow is based on the following considerations:

From the user perspective, only a confirmation in the WeChat authorization dialog is required, without entering account passwords.

The experience is smoother because the user's WeChat nickname and avatar can be used as initial user information.

Development is simplified by directly using the WeChat user identifier without generating and verifying a separate identifier.

Security is relatively guaranteed because WeChat handles information acquisition, transmission, and decryption.

Robust Flow

Authorization Rejection

Problem: When obtaining user information, an authorization dialog appears. If the user clicks “Reject”, the login fails and subsequent attempts also fail because the dialog is not shown again for a period of time.

Solution: Add a flow to handle rejection: detect recent rejection, prompt the user to open the permission panel, allow re‑authorization, and only affect the current login attempt.

When obtaining user info fails, check if it was caused by recent rejection.

If so, show a tip and open the permission panel for the user.

If the user still refuses, the login fails; otherwise, retry obtaining user info.

Login State Expiration

Problem: The validity period of the WeChat login state is uncontrollable; the backend session_key may expire at any time.

Developers must check the session_key before each request using wx.checkSession or re‑login, but this adds overhead (≈200 ms per check).

Solution: Implement a strategy that checks login state only when necessary, automatically re‑login when the backend reports an expired state, and transparently retry the original request.

Concurrency Issue

Problem: Multiple components may trigger login simultaneously, leading to extra performance overhead, repeated login dialogs, and logical conflicts.

Solution: Use a singleton login promise: if a login is already in progress, other calls wait for the same promise instead of starting a new login.

Process Implementation

Sequence Control

The login process involves many asynchronous steps. Using Promise with async/await allows linear, readable sequencing instead of nested callbacks.

class Login {
  static async _login() {
    // each step is wrapped in a Promise
    // e.g., wx.login, wx.getUserInfo, backend request
  }
}

Expiration Handling

The implementation checks the backend response for specific error codes that indicate an expired session. If detected, the module clears the local login state, re‑executes the login flow, and retries the original request.

static async requestWithLogin(options) {
  // check if login is needed
  // if login fails due to expiration, call Login.login() again
  // then repeat the request
}

Scenario Optimization

Repeated Authorization Issue

Problem: After a user authorizes once, the mini‑program can access user info for a period. If the user does not use the mini‑program for a long time or deletes it and reinstalls, the authorization dialog appears again, which can annoy users.

Solution: After a successful wx.login, attempt a silent login using only the openid. Only if this fails does the module request user authorization. This way, new users see the dialog, while returning users log in silently.

Scenario Adaptation Issue

Different scenarios have different expectations for login behavior, such as:

Only trigger login when the user clicks a button that requires authentication (e.g., commenting, favoriting).

Attempt silent login on the homepage to provide personalized recommendations without interrupting the user.

Ensure front‑end and back‑end login states are consistent for data decryption.

The module supports three login modes that can be specified per request:

Common (default): Automatically trigger login and complete the request.

Silent: Attempt silent login only; do not show the authorization dialog, and the result does not affect page functionality.

Force: Re‑login regardless of existing state to guarantee consistency.

Implementation

The login flow checks the mode option and executes the appropriate steps. For silent mode, it calls the silent login function; for force mode, it forces a full login.

static async login(options) {
  if (options.mode === 'silent') {
    // silent login logic
  } else if (options.mode === 'force') {
    // force re‑login
  } else {
    // common flow
  }
}

Effect Example

Using the wepy framework, a page can request data with login handling:

import Login from '../../lib/Login';
export default class extends wepy.page {
  async onLoad() {
    const dataRes = await Login.requestWithLogin({
      url: 'xxx/xxx',
      loginOpts: { mode: 'silent' }
    });
    // use dataRes
  }
  methods = {
    async onComment() {
      const addRes = await Login.requestWithLogin({
        url: 'xxx/addComment',
        data: { comment: 'xxx' },
        loginOpts: { mode: 'common' }
      });
    }
  }
}

UI Optimization

Solution 1: Login Modal

Place a login modal on every page. When login is required, the modal is shown, and the user completes authorization via a button.

<template>
  <view class="modal" wx:if="{{show}}">
    <button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">登录</button>
  </view>
</template>
<script>
export default class extends wepy.component {
  data = { show: false, listener: null };
  methods = {
    onGetUserInfo(ev) {
      this.listener && this.listener({
        succeeded: ev.detail.errMsg.includes('ok'),
        data: ev.detail
      });
      this.show = false;
      this.$apply();
    },
    open() {
      return new Promise((resolve, reject) => {
        this.listener = resolve;
        this.show = true;
        this.$apply();
      });
    }
  }
}
</script>

Solution 2: Separate Login Page

Navigate to a dedicated login page that contains the authorization button. The page notifies the previous page via a global hub.

// userAuthHub.js
export default {
  _listeners: [],
  subscribe(listener) { this._listeners.push(listener); },
  notify(res) { this._listeners.forEach(l => l(res)); this._listeners = []; }
};

// login.js (requestUserInfo)
await new Promise(resolve => userAuthHub.subscribe(resolve));
wx.navigateTo({ url: '/pages/login/login' });

// login.wpy (page)
<button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">登录</button>
<script>
export default class extends wepy.page {
  data = { userInfoRes: { succeeded: false, data: null } };
  methods = {
    onGetUserInfo(ev) {
      this.userInfoRes = {
        succeeded: ev.detail.errMsg.includes('ok'),
        data: ev.detail
      };
      wx.navigateBack();
    },
    onUnload() {
      userAuthHub.notify(this.userInfoRes);
    }
  }
}
</script>

Reuse Optimization

Cross‑Mini‑Program Reuse & Customization

Provide a unified login flow with a configurable source identifier for each mini‑program and an optional custom user‑auth handler for UI customization.

class Login {
  static _config = { source: '' , userAuthHandler: null };
  static async login(options) { /* ... */ }
  static async requestWithLogin(options) { /* ... */ }
}
export default Login;

The module includes a decorator that ensures configuration is set before any public API is called, preventing timing errors.

function requireConfig(target) {
  for (let prop of Object.getOwnPropertyNames(target)) {
    if (typeof target[prop] !== 'function') continue;
    const ori = target[prop];
    target[prop] = function(...args) {
      if (!target.checkConfig()) {
        console.error('[Login] Please call Login.config first');
        return;
      }
      return ori.apply(this, args);
    };
  }
}
@requireConfig
class Login { /* ... */ }

Cross‑Page Reuse & Customization

Pages can provide their own authorization tip function to override the default UI text.

export default class extends wepy.page {
  $loginUserAuthTips() {
    return { title: 'Authorize to', content: 'Get personalized offers', confirmTxt: 'OK' };
  }
}

The global login configuration merges the default tips, the page‑level tips, and any custom handler supplied by the mini‑program.

Login.config({
  async userAuthHandler() {
    const page = getCurrentWepyPage();
    const defaultTips = { title: '', content: 'Mini‑program needs your authorization', confirmTxt: 'Got it' };
    const tips = Object.assign({}, defaultTips, page.$loginUserAuthTips && page.$loginUserAuthTips());
    const res = await page.$invoke('AuthModal', 'open', tips);
    return res;
  }
});

Conclusion

Features

One‑click login via WeChat authorization.

Silent login after the first authorization.

Multiple login scenarios: common, silent, force.

Customizable authorization UI.

Robustness

Handles rejection by prompting the permission panel.

Automatically re‑login when the session expires.

Login flow and retry logic are transparent to callers.

Performance

Lazy backend session checks reduce per‑request overhead.

Singleton steps avoid duplicate work.

Seamless continuation after login reduces user friction.

Reusability & Extensibility

Unified core flow shared across mini‑programs; each program can supply its own source and UI customizations.

Page‑level overrides allow different texts or behaviors without affecting other pages.

The open‑source library fancy-mini contains the full implementation. Feel free to explore, discuss improvements, and share feedback.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.