Welcome to the new WEEX API documentation. Looking for the previous version?Switch to the legacy docs
WEEX
Broker

Request signing

Overview

On the OAuth authorization platform, the access_token endpoint is a sensitive API that is called by third-party servers to obtain tokens. In addition to validating standard OAuth parameters, the server must also verify that the request is initiated by a registered client, ensure the request has not been tampered with during transmission, and prevent replay attacks. To achieve this, a client-side request signing mechanism based on HMAC-SHA256 is introduced, providing:

  • Client authentication
  • Request integrity protection
  • Replay attack prevention

Request headers

Header nameRequiredDescription
Client-IdYesIdentifier for the client, corresponding to the registered client_id
SignYesThe request signature
TimestampYesThe request initiation timestamp in milliseconds
NonceYesA unique, single-use random string to prevent replay attacks

Signing rules

Field description

  • timestamp: The request timestamp (in milliseconds) must match the Timestamp header
  • nonce: A one-time random string. Must match the Nonce header and is used to prevent replay attacks.
  • method: The HTTP request method (such as POST, GET), in uppercase.
  • requestPath: The API endpoint path, such as /v1/oauth/token.
  • queryString: The query string after ? in the request URL. Leave empty if not present.
  • body: The request payload as a string. If there is no request body (typically for GET requests), use an empty string.

Signature format rules if queryString is empty

timestamp + nonce + method.toUpperCase() + requestPath + body

Signature format rules if queryString is not empty

timestamp + nonce + method.toUpperCase() + requestPath + "?" + queryString + body

Example

Example 1: Without query string (token request)

  • timestamp = 1710000000000
  • nonce = abc123xyz
  • method = "POST"
  • requestPath = "/v1/oauth/token"
  • body = {"grantType":"authorization_code","code":"code123","redirectUri":"https://client.example.com/callback","codeVerifier":"verifier123"}

Generate the string to be signed:

1710000000000abc123xyzPOST/v1/oauth/token{"grantType":"authorization_code","code":"code123","redirectUri":"https://client.example.com/callback","codeVerifier":"verifier123"}

Steps to generate the final signature

  1. Use the clientSecret to compute an HMAC-SHA256 hash of the baseString and encode the result using Base64.
    • Sign = Base64(HMAC-SHA256(clientSecret, baseString))

Signature example

Java

JavaCode
package com.weex.utils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.UUID; public class OAuthApiClient { /** * Replace with your actual clientId / clientSecret */ private static final String CLIENT_ID = "your-client-id"; private static final String CLIENT_SECRET = "your-client-secret"; /** * Replace with your OAuth authorization server base URL */ private static final String BASE_URL = "https://auth.example.com"; /** * HMAC-SHA256 algorithm */ private static final String HMAC_SHA256 = "HmacSHA256"; /** * Generate signature * * Signing rules: * 1. Without queryString: * timestamp + nonce + method.toUpperCase() + requestPath + body * *2. With queryString: * timestamp + nonce + method.toUpperCase() + requestPath + queryString + body * * Notes: * - Use "" if queryString is empty * - If not empty, queryString should include "?a=1&b=2" * - Use "" if body is empty */ public static String generateSignature(String clientSecret, String timestamp, String nonce, String method, String requestPath, String queryString, String body) throws Exception { String safeQueryString = queryString == null ? "" : queryString; String safeBody = body == null ? "" : body; String message = timestamp + nonce + method.toUpperCase() + requestPath + safeQueryString + safeBody; return hmacSha256Base64(clientSecret, message); } /** * Computes HMAC-SHA256 and encodes the result in Base64. */ private static String hmacSha256Base64(String secretKey, String message) throws Exception { SecretKeySpec secretKeySpec = new SecretKeySpec( secretKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA256 ); Mac mac = Mac.getInstance(HMAC_SHA256); mac.init(secretKeySpec); byte[] signatureBytes = mac.doFinal(message.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(signatureBytes); } /** * Generates a timestamp in milliseconds */ private static String generateTimestamp() { return String.valueOf(System.currentTimeMillis()); } /** * Generates a random nonce */ private static String generateNonce() { return UUID.randomUUID().toString().replace("-", ""); } /** * Sends a POST request * * Notes: * - The body must be the exact JSON string sent in the request * - The body used for signing must match the request body exactly */ public static String sendPost(String clientId, String clientSecret, String requestPath, String queryString, String body) throws Exception { String timestamp = generateTimestamp(); String nonce = generateNonce(); String signature = generateSignature( clientSecret, timestamp, nonce, "POST", requestPath, queryString, body ); String url = BASE_URL + requestPath + (queryString == null ? "" : queryString); HttpPost postRequest = new HttpPost(url); postRequest.setHeader("Client-Id", clientId); postRequest.setHeader("Sign", signature); postRequest.setHeader("Timestamp", timestamp); postRequest.setHeader("Nonce", nonce); postRequest.setHeader("Content-Type", "application/json"); StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8); postRequest.setEntity(entity); try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = httpClient.execute(postRequest)) { int statusCode = response.getStatusLine().getStatusCode(); String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); System.out.println("HTTP Status: " + statusCode); return responseBody; } } /** * Example: Exchange authorization_code for token */ public static void main(String[] args) { try { String requestPath = "/v1/oauth/token"; /** * Note: * This must be the exact JSON string sent in the request. * The same body must be used for both signing and sending. */ String body = "{" + "\"grantType\":\"authorization_code\"," + "\"code\":\"auth_code_xxx\"," + "\"redirectUri\":\"https://client.example.com/callback\"," + "\"codeVerifier\":\"code_verifier_xxx\"" + "}"; String response = sendPost( CLIENT_ID, CLIENT_SECRET, requestPath, "", body ); System.out.println("Response: " + response); } catch (Exception e) { e.printStackTrace(); } } }