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
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;Send the parameters obtained from the mobile client to the game server, then the server will sign a MAC Token.
Request
https://openapi.tap.io/account/profile/v1
withmac 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_id | string | The 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 | string | mac_key id, The key identifier. |
access_token | string | This string currently has no use |
token_type | string | Token type (i.e. MAC) |
mac_key | string | MAC key |
mac_algorithm | string | MAC 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 | string | Error code used for assessing code logic |
error_description | string | Error 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_scope | The 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. |