14 KiB
Mini Program WeChat Auth Design
Problem
The mini program currently supports phone number plus SMS code login only. The new requirement is to add full WeChat authorization login while keeping the existing phone login available. WeChat login must use the free openid-based login flow as the primary path, and phone number authorization should be integrated when available so the backend can identify and bind existing phone users automatically.
Goals
- Add WeChat mini program login with
wx.login/uni.loginand backendcode2Sessionexchange. - Keep the existing phone plus SMS code login flow unchanged as a supported fallback and alternate login method.
- Use WeChat openid as the free, reliable identity for mini program login.
- If WeChat phone authorization succeeds, use the returned phone number to identify an existing user and automatically bind the WeChat openid to that account.
- Save nickname and avatar into existing
t_user.nicknameandt_user.avatarfields when the user provides them. - Reuse the current JWT response, token storage, session restore, onboarding routing, and profile flow.
- Match the current mini program dark glass/cosmic visual style.
Non-Goals
- Do not remove, hide permanently, or degrade phone SMS login.
- Do not introduce paid third-party login or SMS providers.
- Do not require phone number authorization before a user can log in with WeChat openid.
- Do not redesign unrelated onboarding, main tabs, or profile features.
- Do not store WeChat
session_keyon the client.
Current Findings
mini-program/src/pages/login/index.vuecontains the existing phone/SMS login UI and routes to main or onboarding based on profile presence.mini-program/src/stores/app.jsalready centralizes login, logout, token restore, and profile fetch behavior.mini-program/src/services/auth.jsstoresaccess_tokenandrefresh_tokenfromAuthResponse.server/src/main/java/com/emotion/controller/AuthController.javaexposes/auth/login,/auth/refreshToken, and token validation endpoints.t_useralready hasphone,avatar,nickname,third_party_id, andthird_party_type, which can support WeChat binding without a required schema change.- The current backend issues JWT plus Redis-backed access/refresh tokens through
AuthServiceImpl. - The mini program manifest already has a WeChat appid:
wxaf2eaba72d28f0e4. application.ymlsecurity ignore URLs must include the new public WeChat login endpoint, while the phone binding endpoint must remain protected by JWT.UserInfoResponseexposes phone, nickname, and avatar, but it does not expose whether the current account has a WeChat binding. The frontend needs a non-sensitivewechatBoundboolean instead of receiving openid.- Binding an openid from a temporary WeChat user to an existing phone user must also transfer or preserve data created under the temporary user; otherwise early actions before phone binding can be stranded on the temporary account.
Recommended Approach
Use a two-track login page:
- Primary action: WeChat login. The mini program calls
uni.login({ provider: 'weixin' }), sends the returned code to the backend, and receives the sameAuthResponseshape used by existing login. - Secondary action: phone SMS login. The current phone/code form remains available and continues to call
/auth/login.
Phone authorization is a post-login enhancement:
- After WeChat login succeeds, show or surface a "bind phone" action using WeChat
open-type="getPhoneNumber"when the platform supports it. - The mini program sends the phone authorization
codeto the backend. - The backend calls WeChat's phone-number API.
- If the phone already belongs to an existing user, bind the current openid to that existing user and return a fresh
AuthResponsefor that existing user. - If the phone does not exist, write it to the current WeChat user and return updated user info.
This keeps WeChat openid login free and resilient while letting phone number authorization unify identities when available.
Backend Design
Configuration
Add configuration under emotion.wechat.mini-program:
app-idapp-secretbase-url, defaulthttps://api.weixin.qq.com
Secrets must come from environment/profile configuration in deploy environments. app-secret must not be sent to the client or logged.
Add a database migration for WeChat identity integrity:
- Add a unique composite index on
t_user(third_party_type, third_party_id). - MySQL allows multiple
NULLvalues in a unique index, so ordinary phone users without WeChat binding are unaffected. - Before transferring an openid from a temporary WeChat user to an existing phone user, clear the temporary user's third-party fields so the unique index remains valid.
DTOs
Add request/response DTOs:
-
WechatLoginRequestcode: required login code fromuni.login.nickname: optional user-provided nickname.avatar: optional user-provided avatar URL.
-
WechatPhoneBindRequestcode: required phone authorization code fromgetPhoneNumber.
-
The response can reuse
AuthResponsefor login and phone binding so token handling stays consistent. -
Extend
UserInfoResponsewithwechatBound: boolean. It must be computed from the server-side third-party fields and must not expose openid or session key.
Service Boundaries
Add WechatMiniProgramService for WeChat API calls:
code2Session(code)returns openid/session metadata.getStableAccessToken()obtains and caches the mini program interface token in Redis.getPhoneNumber(phoneCode)returns the verified phone number.- Translate WeChat API error codes into clear
BusinessExceptionmessages.
Add UserIdentityMergeService for account convergence:
mergeTemporaryWechatUserIntoPhoneUser(String temporaryWechatUserId, String phoneUserId)transfers user-owned rows from the temporary WeChat user to the canonical phone user.- The service updates the core mini-program tables that store
user_id:t_user_profile,t_life_event,t_epic_script,t_life_path,t_life_path_step,t_social_content_item,t_social_profile_insight,t_user_consent_log,t_tts_task, andt_analytics_event. - It also updates user-facing shared data where appropriate:
t_conversation,t_message,t_coze_api_call,t_emotion_analysis,t_emotion_record,t_diary_post,t_diary_comment,t_community_post,t_comment,t_user_stats, andt_ai_call_log. - If both users have a singleton row such as
t_user_profileort_user_stats, keep the existing phone user's row and only transfer rows that do not conflict. This avoids overwriting a mature phone account with sparse temporary data.
Extend AuthService with:
wechatLogin(WechatLoginRequest request)bindWechatPhone(String currentUserId, WechatPhoneBindRequest request)
Extend UserService with focused lookup helpers:
getByThirdParty(String thirdPartyType, String thirdPartyId)getByPhone(String phone)already exists and should be reused for phone identity matching.
The controller stays thin and delegates all identity decisions to the service layer.
Identity Matching
For WeChat login:
- Exchange login code for openid.
- Query user by
third_party_type = "wechat-mp"andthird_party_id = openid. - If found and enabled, issue tokens for that user.
- If not found, create a user:
account = "wx_" + openidusername =generated usernamenickname =request nickname or generated usernameavatar =request avatar if presentthird_party_type = "wechat-mp"third_party_id = openidmember_level = "free"status = 1
For phone binding:
- Require a valid current JWT.
- Resolve the current WeChat user's openid from
third_party_id. - Exchange the phone authorization code for phone number.
- Query existing user by phone.
- If no phone user exists, set current user's
phoneand mark verified. - If the phone user is the current user, ensure openid and phone are both present.
- If another enabled user owns the phone, treat that phone user as canonical.
- If the phone user already has a different WeChat openid, reject with a clear "phone already bound to another WeChat account" message.
- If the phone user can accept the binding, transfer user-owned rows from the temporary WeChat user to the phone user, clear the temporary user's third-party fields, bind the openid to the phone user, and issue new tokens for the phone user.
When the phone belongs to an existing account, the existing account is the canonical identity. The response must return tokens for that account so future openid login lands on the same user as phone SMS login.
Frontend Design
Login Page
Update mini-program/src/pages/login/index.vue while preserving the current visual language:
- Keep the dark background, glass card, purple accents, and safe-area handling.
- Add a primary WeChat login button at the top of the card.
- Keep the existing phone number and SMS code form as an alternate method below a subtle divider.
- Use one
loadingstate for the active login method and prevent duplicate taps. - On successful login, reuse current routing:
- has profile ->
/pages/main/index?tab=script - no profile ->
/pages/onboarding/index
- has profile ->
Frontend Services And Store
Add to mini-program/src/services/auth.js:
wechatLogin({ code, nickname, avatar })bindWechatPhone({ code })
Both methods store returned tokens exactly like existing login() and refreshToken().
Add to mini-program/src/stores/app.js:
loginWithWechat(profilePayload)bindWechatPhone(phoneCode)fetchCurrentUserInfo(), called after login, token restore, and phone binding.- Keep
login(phone, smsCode)unchanged. - After phone binding returns new tokens, refresh current profile/user state.
Phone Authorization UI
Add a small phone-bind entry in the logged-in experience, preferably in MineView.vue or onboarding completion area:
- Show only when logged in,
userInfo.phoneis empty, anduserInfo.wechatBoundis true. - Use WeChat
button open-type="getPhoneNumber"on mp-weixin. - On non-WeChat/H5 builds, show the existing phone SMS login/bind fallback.
- If authorization succeeds and backend returns tokens for an existing phone user, overwrite local tokens and refresh profile.
- If the user denies authorization, keep them logged in and show a non-blocking message.
Nickname And Avatar
Use WeChat's current nickname/avatar collection pattern in profile or onboarding:
- Nickname input can populate existing
registrationData.nickname. - Avatar selection can populate existing avatar fields.
- Save through existing user/profile update endpoints where possible.
- Do not block WeChat openid login on nickname or avatar.
Error Handling
- Missing backend WeChat appid/secret: backend returns configuration error; frontend keeps phone login available.
uni.loginfailure: show a concise "WeChat login failed, please try again later" message and keep phone login visible.code2Sessionfailure or invalid code: show a concise login failure message.- Disabled user: reject login consistently with existing auth behavior.
- Phone authorization denied: do not log out; show a non-blocking message that the user can bind a phone later in the profile center.
- Phone API unavailable or permission not enabled: keep openid login active and route user normally.
- Existing phone is bound to another openid: reject binding and ask user to use phone login or contact support.
- Existing phone account has data while temporary WeChat account also has data: transfer non-conflicting temporary rows to the phone account before switching tokens.
- Temporary WeChat account after successful merge: clear its
third_party_typeandthird_party_idand disable it from future WeChat login matching. - Network failures: preserve current session restore behavior and avoid clearing tokens unless backend explicitly rejects auth.
Data Flow
WeChat Login
- User taps WeChat login.
- Mini program calls
uni.login. - Mini program posts
{ code }to/auth/wechat/login. - Backend exchanges code for openid through WeChat.
- Backend finds or creates user by openid.
- Backend returns
AuthResponse. - Mini program stores tokens, fetches profile, and routes.
Phone Auto-Binding
- Logged-in user taps authorize phone.
- WeChat returns a phone authorization code.
- Mini program posts
{ code }to/auth/wechat/phone. - Backend exchanges code for phone number.
- Backend finds existing user by phone.
- Backend binds openid to the phone user or writes phone to current user.
- If an existing phone user is canonical, backend transfers non-conflicting temporary user data and clears the temporary WeChat identity.
- Backend returns
AuthResponse. - Mini program replaces tokens and refreshes user/profile state.
Testing
Backend:
- WeChat login creates a new user for a new openid.
- WeChat login returns the same user for a known openid.
- Phone binding writes phone to current WeChat user when no existing phone user exists.
- Phone binding binds openid to an existing phone user and returns that user's tokens.
- Phone binding transfers temporary WeChat user data to the existing phone user before returning phone-user tokens.
- Temporary WeChat user no longer matches future openid login after a successful transfer.
- Phone binding rejects a phone user already bound to a different openid.
- Existing phone SMS login still works.
- Token validation and refresh still work after WeChat login.
/api/auth/wechat/loginis public, and/api/auth/wechat/phonerejects unauthenticated requests.UserInfoResponsereturnswechatBoundwithout returning openid.
Mini program:
- Build
npm run build:mp-weixin. - Login page supports both WeChat and phone SMS login.
- WeChat login success routes through the current profile/onboarding rules.
- Phone login success routes through the current profile/onboarding rules.
- Denying phone authorization does not break logged-in state.
- Successful phone authorization refreshes tokens and profile.
- Personal center reflects nickname/avatar/phone state where available.
- Restored sessions fetch current user info so phone binding visibility is correct after app restart.
References
- WeChat Mini Program
code2Session: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html - WeChat Mini Program phone number API: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html
- WeChat Mini Program avatar/nickname capability: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html