Skip to main content
Version: v3

TapTap OAuth Interface

Summary

An OAuth Mac Token is a string that the TapTap OpenAPI uses to validate the request.

When using the Basic TapTap User Verification Interface, an access token bundle is generated after the user grants permission to the current application. It can be transformed into a MAC Token string after encryption. This access token bundle remains valid until the user updates their account's security information or denies authorization. Developers are advised to manage MAC Tokens on their server as a means of subsequent communication with the TapTap server.

For details on the MAC Token algorithm, see the MAC Token Algorithm section of this document.

Process

  1. When the client uses SDK's TapTap Login, it can Obtain AccessTokens, including:

    public String kid;
    public String access_token;
    public String token_type;
    public String mac_key;
    public String mac_algorithm;
    public String expire_in;
    private String json = null;
  2. Send the parameters obtained from the mobile client to the game server, then the server will sign a MAC Token.

  3. Request https://openapi.tap.io/account/profile/v1 with mac token included in the header.

Note: The returned kid and access_token values are equal. We recommend using kid.

API

Obtain Current Account Details

GET https://openapi.tap.io/account/profile/v1?client_id=xxx
Authorization mac token

Request Parameters

String      Type   Description   
client_idstringThe app's Client ID must be the same as the contract

Response Parameters

String             Type           Description
name            string        User name
avatar          string        User avatar address
gender          string       "female", "male" or empty string
openid          string        The unique ID of the authorized user. The openid of each player for each game is different. The openid of players obtained by the same game is always the same
unionid         string        Unique identifier of authorized users. Unionid is the same for a player in all games from a manufacturer. Therefore, manufactures have different unionids.

Request Example

Replace the MAC ID and Client ID with your own signed MAC Token and console's Client ID.

curl -s -H 'Authorization:MAC id="1/hC0vtMo7ke0Hkd-iI8-zcAwy7vKds9si93l7qBmNFxJkylWEOYEzGqa7k_9iw_bb3vizf-3CHc6U8hs-5a74bMFzkkz7qC2HdifBEHsW9wxOBn4OsF9vz4Cc6CWijkomnOHdwt8Km6TywOX5cxyQv0fnQQ9fEHbptkIJa
gCd33eBXg76grKmKsIR-YUZd1oVHu0aZ6BR7tpYYsCLl-LM6ilf8LZpahxQ28n2c-y33d-20YRY5NW1SnR7BorFbd00ZP97N9kwDncoM1GvSZ7n90_0ZWj4a12x1rfAWLuKEimw1oMGl574L0wE5mGoshPa-CYASaQmBDo3Q69XbjTs
KQ",ts="1618221750",nonce="adssd",mac="XWTPmq6A6LzgK8BbNDwj+kE4gzs="' "https://openapi.tap.io/account/profile/v1?client_id=<Client ID>"

Other

MAC Token Algorithm

MAC Token contains the following strings:

String          Type   Description                            
kid           stringmac_key id, The key identifier.
access_token  stringThis string currently has no use                    
token_type    stringToken type (i.e. MAC)               
mac_key       stringMAC key                         
mac_algorithmstringMAC algorithm computation is called hmac-sha-1      

Use the MAC Token to sign an interface:

Script Request Example

Use this script to verify the direct replacement parameters to ensure the MAC Token signed by your server is correct.

Replace the CLIENT_ID with the Client ID obtained by the console. Replace ACCESS_TOKEN and MAC_KEY with the  access_token and mac_key obtained after logging into the client:

#!/usr/bin/env bash

# Client ID
CLIENT_ID="Change to the `Client ID` obtained from the console"
# Aaccess_token obtained by SDK
ACCESS_TOKEN="1/hC0vtMo7ke0Hkd-iI8-zcAwy7vKds9si93l7qBmNFxJkylWEOYEzGqa7k_9iw_bb3vizf-3CHc6U8hs-5a74bMFzkkz7qC2HdifBEHsW9wxOBn4OsF9vz4Cc6CWijkomnOHdwt8Km6TywOX5cxyQv0fnQQ9fEHbptkIJagCd33eBXg76grKmKsIR-YUZd1oVHu0aZ6BR7tpYYsCLl-LM6ilf8LZpahxQ28n2c-y33d-20YRY5NW1SnR7BorFbd00ZP97N9kwDncoM1GvSZ7n90_0ZWj4a12x1rfAWLuKEimw1oMGl574L0wE5mGoshPa-CYASaQmBDo3Q69XbjTsKQ"
# mac_key obtained by SDK
MAC_KEY="mSUQNYUGRBPXyRyW"

# Random number. Please replace for official launch.
NONCE="8IBTHwOdqNKAWeKl7plt8g=="
# Current timestamp
TS=$(date +%s)

# Request method
METHOD="GET"
# Request addres (contains query string)
REQUEST_URI="/account/profile/v1?client_id=${CLIENT_ID}"
# Request domain name
REQUEST_HOST="openapi.tap.io"

MAC=$(printf "%s\n%s\n%s\n%s\n%s\n443\n\n" "${TS}" "${NONCE}" "${METHOD}" "${REQUEST_URI}" "${REQUEST_HOST}" | openssl dgst -binary -sha1 -hmac ${MAC_KEY} | base64)

AUTHORIZATION=$(printf 'MAC id="%s",ts="%s",nonce="%s",mac="%s"' "${ACCESS_TOKEN}" "${TS}" "${NONCE}" "${MAC}")

curl -s -H"Authorization:${AUTHORIZATION}" "https://${REQUEST_HOST}${REQUEST_URI}"

nodejs Request Example

const http = require('http');
const https = require('https');
const crypto = require('crypto');

function getAuthorization(requestUrl, method, keyId, macKey) {
const url = new URL(requestUrl);
const time = Math.floor(Date.now() / 1000).toString().padStart(10, '0');
const randomStr = getRandomString(16);
const host = url.hostname;
const uri = url.pathname + url.search;
const port = url.port || (url.protocol === 'https:' ? '443' : '80');
const other = '';
const sign = signData(mergeData(time, randomStr, method, uri, host, port, other), macKey);

return `MAC id="${keyId}", ts="${time}", nonce="${randomStr}", mac="${sign}"`;
}

function getRandomString(length) {
return crypto.randomBytes(length).toString('base64');
}

function mergeData(time, randomCode, httpType, uri, domain, port, other) {
let prefix =
`${time}\n${randomCode}\n${httpType}\n${uri}\n${domain}\n${port}\n`;

if (!other) {
prefix += '\n';
} else {
prefix += `${other}\n`;
}

return prefix;
}

function signData(signatureBaseString, key) {
const hmac = crypto.createHmac('sha1', key);
hmac.update(signatureBaseString);
return hmac.digest('base64');
}

const client_id = "5enu******wfy";
const keyId = "1/JFZi8****IiumsGZI31iJH1q*****UKZ-eKA";
const macKey = 'LMbNcKox*******kfmk7oWXbuRz';
const requestUrl = 'https://openapi.tap.io/account/profile/v1?client_id='+ client_id ;
const method = 'GET';

const authorization = getAuthorization(requestUrl, method, keyId, macKey);
console.log(authorization);

const options = new URL(requestUrl);
const client = options.protocol === 'https:' ? https : http;

const req = client.request({
hostname: options.hostname,
port: options.port,
path: options.pathname + options.search,
method: 'GET',
headers: {
'Authorization': authorization
}
}, (res) => {
let data = '';

res.on('data', (chunk) => {
data += chunk;
});

res.on('end', () => {
console.log(data);
});
});

req.end();


java Request Example

package com.taptap;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class Authorization {
    public static void main(String[] args) throws IOException {
        String client_id = "0RiAlMny7jiz086FaU";
        String kid = "1/hC0vtMo7ke0Hkd-iI8-zcAwy7vKds9si93l7qBmNFxJkylWEOYEzGqa7k_9iw_bb3vizf-3CHc6U8hs-5a74bMFzkkz7qC2HdifBEHsW9wxOBn4OsF9vz4Cc6CWijkomnOHdwt8Km6TywOX5cxyQv0fnQQ9fEHbptkIJagCd33eBXg76grKmKsIR-YUZd1oVHu0aZ6BR7tpYYsCLl-LM6ilf8LZpahxQ28n2c-y33d-20YRY5NW1SnR7BorFbd00ZP97N9kwDncoM1GvSZ7n90_0ZWj4a12x1rfAWLuKEimw1oMGl574L0wE5mGoshPa-CYASaQmBDo3Q69XbjTsKQ"; // kid
        String mac_key = "mSUQNYUGRBPXyRyW"; // mac_key
        String method = "GET";
        String request_url = "https://openapi.tap.io/account/profile/v1?client_id=" + client_id; //
        String authorization = getAuthorization(request_url, method, kid, mac_key);
        System.out.println(authorization);
        URL url = new URL(request_url);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // Http
        conn.setRequestProperty("Authorization", authorization);
        conn.setRequestMethod("GET");
        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String line;
        StringBuilder result = new StringBuilder();
        while ((line = rd.readLine()) != null) {
            result.append(line);
        }
        rd.close();
        System.out.println(result.toString());
    }
    /**
     * @param request_url
     * @param method "GET" or "POST"
     * @param key_id key id by OAuth 2.0
     * @param mac_key mac key by OAuth 2.0
     * @return authorization string
     */
    public static String getAuthorization(String request_url, String method, String key_id, String
            mac_key) {
        try {
            URL url = new URL(request_url);
            String time = String.format(Locale.US, "%010d", System.currentTimeMillis() / 1000);
            String randomStr = getRandomString(16);
            String host = url.getHost();
            String uri = request_url.substring(request_url.lastIndexOf(host) + host.length());
            String port = "80";
            if (request_url.startsWith("https")) {
                port = "443";
            }
            String other = "";
            String sign = sign(mergeSign(time, randomStr, method, uri, host, port, other), mac_key);
            return "MAC " + getAuthorizationParam("id", key_id) + "," + getAuthorizationParam("ts", time)
                    + "," + getAuthorizationParam("nonce", randomStr) + "," + getAuthorizationParam("mac",
                    sign);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return null;
    }
    private static String getRandomString(int length) {
        byte[] bytes = new byte[length];
        new SecureRandom().nextBytes(bytes);
        String base64String = Base64.getEncoder().encodeToString(bytes);
        return base64String;
    }
    private static String mergeSign(String time, String randomCode, String httpType, String uri,
                                    String domain, String port, String other) {
        if (time.isEmpty() || randomCode.isEmpty() || httpType.isEmpty() || domain.isEmpty() || port.isEmpty())
        {
            return null;
        }
        String prefix =
                time + "\n" + randomCode + "\n" + httpType + "\n" + uri + "\n" + domain + "\n" + port
                        + "\n";
        if (other.isEmpty()) {
            prefix += "\n";
        } else {
            prefix += (other + "\n");
        }
        return prefix;
    }
    private static String sign(String signatureBaseString, String key) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            byte[] text = signatureBaseString.getBytes(StandardCharsets.UTF_8);
            byte[] signatureBytes = mac.doFinal(text);
            signatureBytes = Base64.getEncoder().encode(signatureBytes);
            return new String(signatureBytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new IllegalStateException(e);
        }
    }
    private static String getAuthorizationParam(String key, String value) {
        if (key.isEmpty() || value.isEmpty()) {
            return null;
        }
        return key + "=" + "\"" + value + "\"";
    }
}

General Interface Error Messages

Same format

String              Type   Description                                                 
code              int    Reserved field for tracking problems in the future                           
error             stringError code used for assessing code logic                           
error_descriptionstringError description info, which is used to help understand and solve errors during development

Error Response

Error code            Description                                                    
invalid_request       The request is missing a required parameter, contains an unsupported parameter or parameter value, or the format is incorrect
invalid_time          Invalid ts time in the MAC Token algorithm. Request to reconstruct the server time 
invalid_client        Invalid client_id parameters                             
access_denied         Authorized server rejects the request this occurs when requesting user resources with a token. If this occurs, the client should log the user out and request the user to log in again. 
forbidden       User does not have permission to perform this action. Reauthenticating permission will not provide any help. This request should be not repeated. 
not_found       Request failed. The requested resources were not found on the server. Requests should not be repeated with the same parameters 
server_error   The server error has occurred.  You may retry the request later, but there must be an upper limit (recommended: 3). If the first attempt fails, interrupt and inform the user 
insufficient_scopeThe permission used for TapTap authorisation on the mobile side does not match the OAuth interface called by the server side, e.g. if the mobile side uses the basic_info permission for authorisation and the server side calls the `Get Current Account Detailed Information' API, then this exception is returned.