tencent cloud

Mini Program/Mini Game Login Practical Tutorial
Last updated: 2025-10-15 15:39:11
Mini Program/Mini Game Login Practical Tutorial
Last updated: 2025-10-15 15:39:11

About this document

As SuperApp's business ecosystem develops to a certain stage, more and more third-party businesses are providing rich services to SuperApp users in the form of mini-programs within SuperApp. Each third-party mini-program may have its own user system. How can SuperApp simplify user login and ensure the security of user data? We have developed a login solution for mini-programs (or mini-games) for the superapp. This solution aims to establish an efficient and secure login system, allowing Superapp users to quickly and seamlessly access various mini-programs within the Superapp ecosystem. Generally, fields such as OpenID or UnionID are generated as unique identifiers, which are then linked to the mini program's own account system to complete the construction and design of the user account system.

Directions

1. The mini program initiates a wx.login request.
2. The SDK obtains the authorization code through superapp getAuthCode.
3. The mini program frontend obtains the tcsas code which is then replaced by the SDK with a new tcsas_code for security reason and transmits it to the mini program backend service.
4. The mini program backend obtains OpenID and session_key through the jscode2session interface and maintains the relationship between the user and OpenID. During this process, OpenServer calls superapp through /access_token/b2b2c to obtain access_token and user ID. OpenServer generates session_key and openid and returns them to the mini program backend, maintaining the relationship between code and user information.


Login solution

1. Superapp server transformation

The login process requires the superapp backend service to provide the following interfaces to support login capabilities.

1.1 Obtaining the Authorization Code

JSAPI Name: wx.login()
API description:
The SDK calls superapp getAuthCode to obtain temporary authorization credentials.

Request parameters

Name
Type
Required
Default value
Note
scopes
array of string
True
-
Temporary authorization code scope list, please refer to the scope definition list, usually only USER_LOGIN_ID/USER_AVATAR/USER_NICKNAME/USER_CONTACTINFO_EMAIL is used
The scope is defined as follows:
Scope definition
Description
SCOPE_PHONE_NUMBER
Authorize to obtain the user's phone number
SCOPE_AVATAR
Authorize to obtain user avatar
SCOPE_NICKNAME
Authorize to obtain user nickname
SCOPE_EMAIL
Authorize access to user's email information

Response parameters

Name
Type
Required
Default value
Note
authCode
String
True
-
Temporary authorization code
authErrorScopes
Key-value
True on failure
-
The permission scope for which authorization failed. The key is the permission scope and the value is the error message.
authSuccessScopes
Array
True on success
-
The scope of authority that is successfully authorized

Mini Program/Mini Game Usage Examples

wx.login({
success: (res) => {
console.log(res.code);
},
fail: (err) => {
console.log(err);
},
});

1.2 Obtaining an access token

Path: /v1.0/access-token/b2b2c.htm
Method: POST
API description:
It is used to complete the account binding process by exchanging the authorization code (authCode) for an access token (accessToken). The access token can be used as a user authorization credential.

Request parameters

Headers
Parameter
Parameter value
Required
Example
Note
Content-Type
application/json
True
-
Content-type, the value is always application/json
X-TIMESTAMP
Request time
True
-
Transaction date and time, in the format of YYYY-MM-DDTHH:mm:ss+07:00. For example, the Jakarta area time must use GMT+7 (Jakarta time)
X-CLIENT-KEY
Unique identifier
True
-
A unique identifier generated by the partner, also known as clientId
X-SIGNATURE
Request signature
True
-
Signatures can only be generated using the asymmetricSignature method.

Body
Name
Type
Required
Default value
Note
grantType
String
True
AUTHORIZATION_CODE
The request type for requesting a token. The value is AUTHORIZATION_CODE
authCode
String
True
-
The authorization code is used to redeem the access token, provided by superapp

Response parameters

Headers
Parameter
Parameter value
Required
Example
Note
Content-Type
application/json
True
-
Content-Type, the value is always application/json
X-TIMESTAMP
Request time
True
-
Transaction date and time, in the format of YYYY-MM-DDTHH:mm:ss+07:00. For example, the Jakarta area time must use GMT+7 (Jakarta time)
Body
Name
Type
Required
Conditions
Note
responseCode
String
True
-
Reference Response Code List
responseMessage
String
True
-
Reference Response Code List
tokenType
String
Conditional
responseCode=0
Token Type
accessToken
String
Conditional

responseCode=0
This token is called a customer token and will be used in the header parameter "Authorization-Customer" of other APIs
accessTokenExpiryTime
String
Conditional
responseCode=0
The expiration time of the access token is provided to the user in the format of YYYY-MM-DDTHH:mm:ss+07:00. The time must be GMT+7 (Jakarta time)
refreshToken
String
Conditional
responseCode=0
This token is used to refresh the session when the existing token expires
refreshTokenExpiryTime
String
Conditional
responseCode=0
The expiration time of the refresh token is provided to the user in the format of YYYY-MM-DDTHH:mm:ss+07:00. The time must be GMT+7 (Jakarta time)
additionalInfo
JSON object
Optional
-
Additional information
additionalInfo.userInfo
JSON object
Optional
-
Additional information about the user. Contains user ID

Sample response

Content-Type: application/json
X-TIMESTAMP: 2020-12-18T15:06:00+07:00
{
"responseCode": "2007400",
"responseMessage": "Successful",
"accessToken": "SQoHkw1tSfWsULjf3qrWpPqimAQi6IxcgmvO4200",
"tokenType": "Bearer",
"accessTokenExpiryTime": "2031-11-02T11:31:19+07:00",
"refreshToken": "NEcnzX7Aq2vv5Ot08ZDSmCzfO4aEWhnWTpbf4200",
"refreshTokenExpiryTime": "2031-11-02T11:31:19+07:00",
"additionalInfo": {
"userInfo": {
"publicUserId": "21779009320193133"
}
}
}
Response Code List
Response code
Response message
Note
superapp operations
2007400
Successful
Processing succeeded.
Mark the token request process as successful
4007400
Bad Request
General request failure error
Mark the token request process as failed. Retry the request with the correct parameters
4007401
Invalid field format
The specific field format is invalid
Mark the token request process as failed. Retry the request with the correct parameters
4007402
Invalid required field
Required fields are missing or in invalid format
Mark the token request process as failed. Retry the request with the correct parameters
4017400
Not authorized. [reason]
General unauthorized error
Mark the token request process as failed. Retry the request with the correct parameters
4297400
Too many requests
Exceeding the maximum transaction limit
Mark the token request process as failed. Retry requests periodically
5007400
General error
General errors that are not retryable
Mark the token request process as failed. Retry requests periodically
5007401
Internal Server Error
Unknown internal server failure, please retry the process
Mark the token request process as failed. Retry requests periodically
Total timeout
-
Total timeout, the client did not get any response from the server (maybe due to network problems, server slowdown, etc.)
Retry up to 3 times, sending the same request payload. If it still fails, mark the token application process as pending.
Unexpected response (field is empty/field does not exist/undefined response code)
-
The server did not produce the expected response (possibly due to hardware failure, errors, etc.)
- If the response code prefix is 202 and 5XX, mark the token application process as pending
- If the field is empty/does not exist, mark the token request process as pending

1.3 Obtaining user information (including mobile number/email address/avatar/nickname)

Path: /dana/omniprod/oauth/userInfoInquiry/scopesToken.htm
Method: POST
API description:
Query user information based on permission scope.

Request parameters

Headers
Parameter
Required
Example
Note
version
True
-
API version. According to the corresponding API reference
Function
True
-
According to the specifications defined by each business area
Value: dana.omniprod.openapi.oauth.userInfoInquiry.scopes
clientId
True
-
Client identifier provided by superapp to identify partners and application systems
clientSecret
False
-
Serves as the client's key. The client key assigned during registration
reqTime
True
-
Request time, in the format of YYYY-MM-DDTHH:mm:ss+07:00. For example, the Jakarta area time must use GMT+7 (Jakarta time)
reqMsgId
True
-
Identifies a unique system request. Each request will be assigned a unique identifier (UUID)
accessToken
True
-
Contains the client token, which has been obtained from the binding process. Refer to 1.2
reserve
False
-
Reserved for future implementation (key/value)
Body
Name
Type
Required
Default value
Note
scopes
array of string
True
-
The scope of permissions for user information. For more information, see Scope definition in 1.1

Example

{
"request": {
"head": {
"version": "1.0",
"function": "dana.omniprod.openapi.oauth.userInfoInquiry.scopes",
"clientId": "2022083116021575310778",
"clientSecret": "c1643a8a0eb1a19cbe817a5766de7a5f27db1f3e5e14c6c432e4e5fc8cbdcd51",
"accessToken": "pZHmQBs0B2SCrY8Q7rO0bvaztwe73Tmw9QcU6500",
"reqTime": "2020-06-09T23:24:26+07:00",
"reqMsgId": "8939eu9821u3891",
"reserve": "{}"
},
"body": {
"scopes": ["USER_LOGIN_ID", "KYC_INFO"]
}
},
"signature": "signature string"
}

Response parameters

Headers
Name
Type
Required
Default value
Note
version
String
True
-
API version. According to the corresponding API reference
Function
String
True
-
According to the specifications defined by each business area
Value: dana.omniprod.openapi.oauth.userInfoInquiry.scopes
clientId
String
True
-
Client identifier provided by superapp to identify partners and application systems
clientSecret
String
False
-
Serves as the client's key. The client key assigned during registration
respTime
String
True
-
Response time, in the format of YYYY-MM-DDTHH:mm:ss+07:00. For example, Jakarta time must use GMT+7 (Jakarta time)
reqMsgId
String
True
-
Identifies a unique system request. Each request will be assigned a unique identifier (UUID)
accessToken
String
True
-
Contains the customer token, which has been obtained from the binding process. Please refer to the account binding and unbinding document
reserve
String
False
-
Reserved for future implementation (key/value)
Body
Name
Type
Required
Default value
Note
resultInfo
JSON object
True
-
Define the details of the result information
userInfo
String
True
-
User information is encrypted using AES-128

Sample response

Encrypted response data
The following is an example response to encrypted response data:
{
"response": {
"head": {
"version": "1.0",
"function": "dana.omniprod.openapi.oauth.userInfoInquiry.scopes",
"clientId": "2022083116021575310778",
"clientSecret": "c1643a8a0eb1a19cbe817a5766de7a5f27db1f3e5e14c6c432e4e5fc8cbdcd51",
"respTime": "2020-06-09T23:24:26+07:00",
"reqMsgId": "8939eu9821u3891",
"reserve": "{}"
},
"body": {
"resultInfo": {
"resultStatus": "S",
"resultCodeId": "00000000",
"resultCode": "SUCCESS",
"resultMsg": "success"
},
"userInfo": "/g99a8dkY989hABjn3A=="
},
"signature": "signature string"
}
}
Plaintext response data
The following is an example response for plaintext response data:
{
"response": {
"head": {
"version": "1.0",
"function": "dana.omniprod.openapi.oauth.userInfoInquiry.scopes",
"clientId": "2022083116021575310778",
"clientSecret": "c1643a8a0eb1a19cbe817a5766de7a5f27db1f3e5e14c6c432e4e5fc8cbdcd51",
"respTime": "2020-06-09T23:24:26+07:00",
"reqMsgId": "8939eu9821u3891",
"reserve": "{}"
},
"body": {
"resultInfo": {
"resultStatus": "S",
"resultCodeId": "00000000",
"resultCode": "SUCCESS",
"resultMsg": "success"
},
"userInfo": {
"USER_LOGIN_ID": "62-85707121623",
"USER_KTP_NAME": "ahmad irfaan",
"USER_GENDER": "1",
"USER_KTP_ID": "124337489",
"USER_BIRTH_OF_PLACE": "Madrid",
"USER_BIRTHDAY": "2000-01-01",
"USER_ADDRESS_KYC": {
"ADDRESS": "A",
"PROVINCE": "B",
"CITY": "C",
"DISTRICT": "D",
"SUB_DISTRICT": "E",
"POSTAL_CODE": "1"
},
"USER_MARITAL_STATUS": "10",
"USER_OCCUPATION": ""

}
}
},
"signature": "signature string"
}

2. SDK version update

The mini program login function is supported in version 2.2.11. If your SDK version is lower than this version, please upgrade it first.
Superapp developers need to implement the getAuthCode interface in the proxy interface.
iOS:
You need to implement the getAuthCode:params:completionHandler method in the TMFMiniAppSDKDelegate proxy: Get the corresponding authcode from the superapp backend and return it to the SDK.
Code example:
- (void)getAuthCode:(TMFMiniAppInfo *)app params:(NSDictionary *)params completionHandler:(MACommonCallback)completionHandler {
NSString *token = [DemoUserInfo sharedInstance].token;
if(token.length <= 0) {
if(completionHandler) {
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"app not login"};
completionHandler(nil,[NSError errorWithDomain:@"TCMPPLoginErrorDomain" code:-1001 userInfo:userInfo]);
}
return;
}

NSArray *scopeList = params[@"scopes"];
NSString *appId = [[TMFMiniAppSDKManager sharedInstance] getConfigAppKey];

NSArray *selectedScopes = [[[DemoUserInfo sharedInstance] scopeList] allKeys];
[[LoginManager sharedInstance] getAuthCode:token appId:appId scopes:selectedScopes completionHandler:^(NSError * _Nullable err, NSDictionary* _Nullable datas) {
if(err) {
if(completionHandler) completionHandler(nil,err);
} else {
if(datas) {
NSString *authCode = datas[@"authCode"];
NSArray *authSuccessScopes = datas[@"authSuccessScopes"];
NSString *code = datas[@"code"];
if(completionHandler) {
if(authCode.length <=0 && code.length <= 0) {
completionHandler(@{@"authCode":authCode,@"authSuccessScopes":authSuccessScopes?:@[]},nil);
} else {
if(authCode.length >0) {
completionHandler(@{@"authCode":authCode,@"authSuccessScopes":authSuccessScopes?:@[]},nil);
} else {
completionHandler(@{@"code":code,@"authSuccessScopes":authSuccessScopes?:@[]},nil);
}
}
}
} else {
if(completionHandler) {
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"authCode is null"};
completionHandler(nil,[NSError errorWithDomain:@"TCMPPLoginErrorDomain" code:-1002 userInfo:userInfo]);
}
}
}
}];
}
Android:
You need to inherit the MiniOpenApiProxy proxy, implement the MiniOpenApiProxy.getAuthCode method, obtain the corresponding authcode from the superapp backend and return it to the SDK.
Example:
@ProxyService(proxy = MiniOpenApiProxy.class)
public class MiniOpenApiProxyImpl extends MiniOpenApiProxy {
private static final String TAG = "MiniOpenApiProxyImpl";
@Override
public void getAuthCode(IMiniAppContext miniAppContext, JSONObject params, AsyncResult result) {
QMLog.d(TAG, "getAuthCode:" + params);
JSONObject ret = new JSONObject();
try {
if (params == null) {
ret.put("errno", -1);
ret.put("errMsg", "invalid params");
result.onReceiveResult(false, ret);
return;
}

LoginApi.UserInfo userInfo = Login.g(miniAppContext.getContext()).getUserInfo();
if (userInfo == null || TextUtils.isEmpty(userInfo.token)) {
ret.put("errno", -2);
ret.put("errMsg", "invalid token, please login app first");
result.onReceiveResult(false, ret);
return;
}

JSONArray scopes = params.optJSONArray("scopes");
// Convert scopes to superapp's own scope
// String[] scopeArr = scopeList.toArray(new String[0]);
AuthApi.INSTANCE.getAuthCode(Login.g(miniAppContext.getContext()).getAppId(),
userInfo.token, scopeArr, new MiniCallback<AuthApi.AuthCodeInfo>() {
@Override
public void value(int code, String msg, AuthApi.AuthCodeInfo data) {
try {
if (code == 0) {
if (data != null) {
if (data.authSuccessScopes != null &amp;&amp; data.authSuccessScopes.length > 0) {
JSONObject ret = new JSONObject();
try {
ret.put("authCode", data.authCode);
ret.put("code", data.code);
ret.put("authSuccessScopes", new JSONArray(data.authSuccessScopes));
result.onReceiveResult(true, ret);
} catch (JSONException e) {
result.onReceiveResult(false, ret);
}
} else {
ret.put("authCode", data.authCode);
ret.put("code", data.code);
result.onReceiveResult(true, ret);
}
} else {
ret.put("errno", -3);
ret.put("errMsg", "invalid authCode");
result.onReceiveResult(false, ret);
}
} else {
ret.put("errno", code);
ret.put("errMsg", msg);
result.onReceiveResult(false, ret);
}
} catch (JSONException e) {
result.onReceiveResult(false, ret);
}
}
});
} catch (JSONException e) {
result.onReceiveResult(false, ret);
}
}
}

3. Configure the service domain in the console - Application management

To implement the unified login function for mini-programs in your superapp, you need to first Console - Superapp management - Configuration management to configure the service domain name of superapp. For more details, please see Configuration Management.

4. Integrate the login feature for the mini program

If your mini program is a purely tool-based application and does not need to store any user information on the server, there is no need to integrate the login feature.
However, in most business scenarios, the mini program needs to obtain the user's unique user ID to record various user behavior data in the server-side database. This ensures that when users switch devices or log in again, they can still retrieve their previous data from the server. Typical use cases include: Orders, shopping carts, points, browsing history, etc.
For the above business scenarios, we recommend that mini program developers follow the process below to integrate and implement the feature:

4.1 Console-Mini Program Management Generate Key

Each mini program needs to generate a key in Mini Program Management - Development management - Key management for identity verification when integrating the login feature. For more details, please see Mini Program Management - Development management - Key management.

4.2 Mini Program Frontend

Log in to the mini program
Mini programs can easily obtain user identities provided by the superapp through standardized login capabilities and quickly establish a user system within the mini program.
Login process chart

Call wx.login() in the mini program to obtain the temporary login credential code and return data to the mini program backend service.
The mini program backend service calls the jscode2Session API in exchange for the user’s unique identifier OpenID and the session key session_key.
The mini program backend service can generate a custom login state according to the user identifier, which will be used to identify the user's identity and login state during subsequent interactions between the frontend and backend.
Note:
1. The session key session_key is a key for encrypting and signing user data. For data security of the mini program, the mini program backend service should not deliver the session key to the mini program, nor should it provide this key to external parties.
2. The temporary login credential code can be used only once.
Mini Program Sample Code
WXML (WX Markup Language)
JAVASCRIPT
<button bindtap='login' loading="{{isLoading}}">Authorized login</button>
import Config from './utils/configData';
const app = getsuperapp();
Page({
<i>data</i>: {
isLoading: false,
},
login: function () {
this.setData({
isLoading: true
})
wx.login({
success: (res) => {
console.log('wx.login success===', res)
if (res.code) {
wx.request({
url: `${Config.BASEURL}/getUserInfo`,
method: "POST",
<i>data</i>: {
appid: Config.APPID,
code: res.code
},
success: (res) => {
this.setData({
isLoading: false
})
console.log('wx.request success===', res)
const { code = -1, data = {} } = res?.data || {};
if (code === 200) {
wx.showToast({
title: ?Logged in successfully',
icon: 'success',
duration: 500
})
// Or store via wx.setStorage
app.globalData.userInfo = {
avatarUrl: data.avatarUrl,
account: data.account,
nickName: data.userName,
id: data.id,
token: data.token,
phoneNumber: data.phone,
emailAddress: data.email
}
setTimeout(() => {
wx.navigateBack({
delta: 1
})
}, 500)
} else {
const msg = res?.data?.data?.msg || '/getUserInfo request fail'
const errcode = res?.data?.data?.errcode || code
console.log('/getUserInfo request fail', res)
wx.showModal({
title: ?Login failed',
confirmText: ?Confirm',
content: `/getUserInfo fail:${msg}[code:${errcode}]`,
showCancel: false
})
}
},
fail: (err) => {
this.setData({
isLoading: false
})
console.log('wx.request fail', err)
wx.showModal({
title: ?Login failed',
confirmText: ?Confirm',
content: err.errMsg,
showCancel: false
})
},
})
} else {
this.setData({
isLoading: false
})
console.log('wx.login does not return code', res)
wx.showModal({
title: ?Login failed',
confirmText: ?Confirm',
content: res.errMsg,
showCancel: false
})
}
},
fail: (err) => {
this.setData({
isLoading: false
})
console.log('wx.login fail===', err)
wx.showModal({
title: ?Login failed',
confirmText: ?Confirm',
content: err.errMsg,
showCancel: false
})
}
})
}
})

4.3 Mini Program Server

The mini program backend needs to implement the `${Config.BASEURL}/getUserInfo` API, which is called by the mini program. The purpose of this API is to exchange a code for a unique user identifier and establish the login state of the mini program.
As the mini program backend, you need to call the OpenServer provided jscode2session Method to obtain a unique openid, which can be used to confirm the unique identity, and each user will not change for a certain mini program.

After obtaining the OpenID, the backend should return the necessary user information and logged-in session (the expiration time of the session can be customized by the mini program backend). Normally, the OpenID should not be returned to the front-end mini program.
The following is an example of calling jscode2session (example only)
Note: Please obtain the calling address from the console. For details, see OpenServer, secret
GET https://xxx.xxx.com/sns/jscode2session?appid=APPID&amp;secret=SECRET&amp;js_code=JSCODE&amp;grant_type=authorization_code
In addition, the mini program backend also needs to implement the getAccessToken API, the token is valid for 7200 seconds, developers need to save it properly, access_token is usually saved in the backend.
The validity period of the access_token is currently 2 hours, and it needs to be refreshed periodically. Repeatedly obtaining the access_token will cause the previously obtained one to become invalid. It is recommended that developers use a central control server to uniformly obtain and refresh the access_token. All other business logic servers should obtain the access_token from this central control server and should not refresh it individually, as this could lead to conflicts and cause the access_token to be overwritten, potentially affecting business operations.
The validity period of the access_token is conveyed through the returned expires_in value, which is currently set to 7,200 seconds. The central control server needs to refresh the token in advance based on this validity period. During the refresh process, the central control server can continue to output the old access_token, and the backend will ensure that both the old and new access_token are valid within 5 minutes, ensuring a smooth transition for third-party services.
The validity period of the access_token may be adjusted in the future. Therefore, the central control server not only needs to proactively refresh the access_token internally at regular intervals but also needs to provide an API for passive refreshing. This allows business servers to trigger the refresh process when they detect that the access_token has expired during API calls.

Benefits

Quick integration:The superapp backend is connected to the product backend, so there is no need to develop the login authentication function of the mini program yourself, and only a small amount of adaptation work is required.
Unified user data: All mini-programs published on the superapp use the same login mechanism, ensuring the consistency and accuracy of user data between the superapp and the mini-program.
Improved user experience: Superapp users can use the same set of credentials to log in to different mini-programs, improving user experience and satisfaction.

Was this page helpful?
You can also Contact Sales or Submit a Ticket for help.
Yes
No

Feedback