Information Security 32 min read

Design and Implementation of Secure Third‑Party API Authentication Using AK/SK, Signatures, and Tokens

This article presents a comprehensive design for securing third‑party APIs, covering permission segmentation, AK/SK generation, signature workflow and rules, anti‑replay mechanisms, token handling, detailed API endpoint specifications, and best‑practice security measures with illustrative Java code examples.

Top Architect
Top Architect
Top Architect
Design and Implementation of Secure Third‑Party API Authentication Using AK/SK, Signatures, and Tokens

When exposing APIs to third‑party systems, data integrity, authenticity, and replay protection are essential. The article outlines a full design that uses Access Key/Secret Key (AK/SK) for identity verification, timestamps, nonces, and digital signatures to safeguard requests.

Design Overview

1. API Key Generation: Each third‑party application receives a unique AK (public identifier) and SK (private secret) pair. AK identifies the client, while SK signs request parameters.

AK: Access Key Id, used to label the user. SK: Secret Access Key, used to generate and verify signatures and must be kept confidential.

2. Interface Authentication: Clients compute a signature from request parameters, AK, timestamp, and nonce, then place it in the request header or query string.

3. Callback URL: Third‑party services provide a callback URL to receive asynchronous notifications.

4. API Design: Define URL, HTTP method, request parameters, and response format for each endpoint.

Permission Division

appID: Unique application identifier, often stored with an index for quick lookup.

appKey (public key): Acts like an account name; used in service calls.

appSecret (private key): Paired with appKey, functions like a password for signing.

token: Time‑limited credential generated after successful authentication.

When requesting authorization, the client sends appKey and appSecret.

The server verifies their existence in the database or cache.

If valid, a unique token is generated and returned to the client.

Subsequent requests must include this token.

Signature Process

The client includes timestamp , nonce , and signature in each request. The server validates the timestamp (e.g., within 60 seconds) and checks that the nonce has not been used before (stored in Redis with a short TTL). If validation passes, the request is processed.

public class SignAuthInterceptor implements HandlerInterceptor {
    private RedisTemplate
redisTemplate;
    private String key;
    public SignAuthInterceptor(RedisTemplate
redisTemplate, String key) {
        this.redisTemplate = redisTemplate;
        this.key = key;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String timestamp = request.getHeader("timestamp");
        String nonceStr = request.getHeader("nonceStr");
        String signature = request.getHeader("signature");
        long NONCE_STR_TIMEOUT_SECONDS = 60L;
        if (StrUtil.isEmpty(timestamp) || DateUtil.between(DateUtil.date(Long.parseLong(timestamp) * 1000), DateUtil.date(), DateUnit.SECOND) > NONCE_STR_TIMEOUT_SECONDS) {
            throw new BusinessException("invalid timestamp");
        }
        Boolean haveNonceStr = redisTemplate.hasKey(nonceStr);
        if (StrUtil.isEmpty(nonceStr) || Objects.isNull(haveNonceStr) || haveNonceStr) {
            throw new BusinessException("invalid nonceStr");
        }
        if (StrUtil.isEmpty(signature) || !Objects.equals(signature, this.signature(timestamp, nonceStr, request))) {
            throw new BusinessException("invalid signature");
        }
        redisTemplate.opsForValue().set(nonceStr, nonceStr, NONCE_STR_TIMEOUT_SECONDS, TimeUnit.SECONDS);
        return true;
    }
    private String signature(String timestamp, String nonceStr, HttpServletRequest request) throws UnsupportedEncodingException {
        Map
params = new HashMap<>(16);
        Enumeration
enumeration = request.getParameterNames();
        if (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String value = request.getParameter(name);
            params.put(name, URLEncoder.encode(value, CommonConstants.UTF_8));
        }
        String qs = String.format("%s×tamp=%s&nonceStr=%s&key=%s", this.sortQueryParamString(params), timestamp, nonceStr, key);
        log.info("qs:{}", qs);
        String sign = SecureUtil.md5(qs).toLowerCase();
        log.info("sign:{}", sign);
        return sign;
    }
    private String sortQueryParamString(Map
params) {
        List
listKeys = Lists.newArrayList(params.keySet());
        Collections.sort(listKeys);
        StrBuilder content = StrBuilder.create();
        for (String param : listKeys) {
            content.append(param).append("=").append(params.get(param).toString()).append("&");
        }
        if (content.length() > 0) {
            return content.subString(0, content.length() - 1);
        }
        return content.toString();
    }
}

Anti‑Replay Measures

Combining timestamp (valid for a short window) with a unique nonce prevents replay attacks. The nonce is stored in Redis; duplicate usage within the TTL triggers rejection.

Token Generation and Usage

After successful signature verification, the server issues a token (e.g., UUID) stored in Redis alongside associated user data. Subsequent requests include this token for stateless authentication.

API Endpoint Examples

GET /api/resources – List resources (optional page , limit ).

POST /api/resources – Create a resource (requires name , optional description ).

PUT /api/resources/{resourceId} – Update a resource.

DELETE /api/resources/{resourceId} – Delete a resource.

Response Format

All responses follow a unified structure containing code , message , and data fields, enabling clients to quickly assess success or error conditions.

public class Result implements Serializable {
    private int code;
    private String message;
    private Object data = null;
    // getters, setters, and helper methods omitted for brevity
}

Database Schema for AK/SK Management

CREATE TABLE api_credentials (
    id INT AUTO_INCREMENT PRIMARY KEY,
    app_id VARCHAR(255) NOT NULL,
    access_key VARCHAR(255) NOT NULL,
    secret_key VARCHAR(255) NOT NULL,
    valid_from DATETIME NOT NULL,
    valid_to DATETIME NOT NULL,
    enabled TINYINT(1) NOT NULL DEFAULT 1,
    allowed_endpoints VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Overall, the design emphasizes HTTPS transport, signature‑based authentication, nonce/timestamp replay protection, token‑based session handling, fine‑grained permission control via appKey/appSecret pairs, and clear API documentation.

backendAuthenticationtokenAPI securitySignatureAK/SK
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.