Implementing OAuth for the Apple Search Ads API
This implementation process guides you through the following steps:
Invite Users
Account administrators invite users with API permissions using the following process:
管理员账号通过以下过程邀请用户具有api权限:
登陆网址
图2-1:登陆界面
整个过程开发者需要提供 Apple ID,具有苹果广告账户管理员权限的账号授权对应子账户的【最低 API 只读】权限;用户前往授权 Apple ID 邮箱查收邀请邮件发送对应的【激活码】,点击接受邀请,此时跳转至苹果广告登录后台,输入邀请【激活码】,完成苹果广告后台登录流程;
图2-2:Apple ID 邮箱查收邀请邮件并跳转激活账号
需要下载openssl
Parameter |
Description |
-name |
prime256v1 — the Elliptic Curve Digital Signature Algorithm (ECDSA) filename. prime256v1:椭圆曲线数字签名算法(ECDSA),key算法对应EC |
-out |
The .pem filename where you generate and store the key pair. 文件名 |
Parameter |
Description |
-in |
The private key filename: private-key.pem 输入文件名 |
-out |
The ec256-public-key file where you generate and store the public key. 输出文件名 |
图3-1:通过公钥生成clientId,teamId,keyId
将asa后台生成的 clientId, teamId, keyId以及私钥生成的 pkcs8 编码字符串拿出来备用
依赖
com.auth0 java-jwt 3.10.3
代码
public static void main(String[] args) throws Exception { // 需要保存的密钥信息 String pkcs8PrivateKey = "MIxxxxxx"; String keyId = "8xxx8"; String teamId = "Sxxxb"; String clientId = "Sxxxb"; String audience = "https://appleid.apple.com"; String alg = "ES256"; // 获取私钥key byte[] decodeBase64Key = Base64.decodeBase64(pkcs8PrivateKey); // ec Keys for the Elliptic Curve algorithm. 椭圆螺线加密算法 KeyFactory keyFactory = KeyFactory.getInstance("EC"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodeBase64Key); PrivateKey privateKey = keyFactory.generatePrivate(keySpec); // 构建载荷 Mapheaders = new HashMap<>(); headers.put("alg", alg); headers.put("kid", keyId); long iat = DateUtil.currentSeconds(); long exp = iat + 86400 * 180; Map payload = new HashMap<>(); payload.put("sub", clientId); payload.put("aud", audience); payload.put("iat", iat); payload.put("exp", exp); payload.put("iss", teamId); // 生成token String jwtToken = Jwts.builder().setClaims(payload).setHeaderParams(headers).signWith(SignatureAlgorithm.ES256, privateKey).compact(); }
import os import datetime as dt from authlib.jose import jwt from Crypto.PublicKey import ECC private_key_file = "private-key.pem" public_key_file = "public-key.pem" client_id = "SEARCHADS.9703f56c-10ce-4876-8f59-e78e5e23a152" team_id = "SEARCHADS.9703f56c-10ce-4876-8f59-e78e5e23a152" key_id = "d136aa66-0c3b-4bd4-9892-c20e8db024ab" audience = "https://appleid.apple.com" alg = "ES256" # Create the private key if it doesn't already exist. if os.path.isfile(private_key_file): with open(private_key_file, "rt") as file: private_key = ECC.import_key(file.read()) else: private_key = ECC.generate(curve='P-256') with open(private_key_file, 'wt') as file: file.write(private_key.export_key(format='PEM')) # Extract and save the public key. public_key = private_key.public_key() if not os.path.isfile(public_key_file): with open(public_key_file, 'wt') as file: file.write(public_key.export_key(format='PEM')) # Define the issue timestamp. issued_at_timestamp = int(dt.datetime.utcnow().timestamp()) # Define the expiration timestamp, which may not exceed 180 days from the issue timestamp. expiration_timestamp = issued_at_timestamp + 86400*180 # Define the JWT headers. headers = dict() headers['alg'] = alg headers['kid'] = key_id # Define the JWT payload. payload = dict() payload['sub'] = client_id payload['aud'] = audience payload['iat'] = issued_at_timestamp payload['exp'] = expiration_timestamp payload['iss'] = team_id # Open the private key. with open(private_key_file, 'rt') as file: private_key = ECC.import_key(file.read()) # Encode the JWT and sign it with the private key. client_secret = jwt.encode( header=headers, payload=payload, key=private_key.export_key(format='PEM') ).decode('UTF-8') # Save the client secret to a file. with open('client_secret.txt', 'w') as output: output.write(client_secret)
载荷示例
// Header { "alg": "ES256", "kid": "d136aa66-0c3b-4bd4-9892-c20e8db024ab" } // Payload { "iss": "SEARCHADS.9703f56c-10ce-4876-8f59-e78e5e23a152", "iat": 2234567891, "exp": 2234567900, "aud": "https://appleid.apple.com", "sub": "SEARCHADS.9703f56c-10ce-4876-8f59-e78e5e23a152" }
curl -X POST \ -H 'Host: appleid.apple.com' \ -H 'Content-Type: application/x-www-form-urlencoded' \ 'https://appleid.apple.com/auth/oauth2/token?grant_type=client_credentials& client_id=SEARCHADS.27478e71-3bb0-4588-998c-182e2b405577&client_secret=eyJ0 eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.zI1NiIsImprdSI6Imh0dHBzOi8vYXV0aC5kZXYuYXB pLnJpY29oL3YxL2Rpc2NvdmVyeS9rZXlzIiwia2lkIjoiMmIyZTgyMTA2NzkxZGM4ZmFkNzgxNW Q3ZmI1NDRhNjJmNzJjMTZmYSJ9.eyJpc3MiOiJodHRwczovL2F1dGguZGV2LmFwaS5yaWNvaC8iL CJhdWQiOiJodHRwczovL2lwcy5kZXYuYXBpLnJpY29oLyIsImlhdCI6MTQ5MDg1Mjc0MSwiZXhwI joxNDkwODU2MzQxLCJjbGllbnRfaWQiOiI4ODQwMWU1MS05MzliLTQ3NzktYjdmNy03YzlmNGIzZj kyYzAiLCJzY29wZSI6Imh0dHBzOi8vaXBzLmRldi5hcGkucmljb2gvdjEiLCJyaWNvaF9tc3Mi OnsibWVkaWEiOnsicXVvdGEiOjEwLCJ0aHJvdHRsZSI6eyJ2YWx1ZSI6MCwid2luZG93IjowfX1 9fQ.jVq_c_cTzgsLipkJKBjAHzm8KDehW4rFA1Yg0EQRmqWmBDlEKtpRpDHZeF6ZSQfNH2OlrBW FBiVDV9Th091QFEYrZETZ1IE1koAO14oj4kf8TCmhiG_CtJagvctvloW1wAdgMB1_Eubz9a8oim cODqL7_uTmA5jKFx3ez9uoqQrEKZ51g665jSI6NlyeLtj4LrxpI9jZ4zTx1yqqjQx0doYQjBPhOB 06Z5bdiVyhJDRpE8ksRCC3kDPS2nsvDAal28sMgyeP8sPvfKvp5sa2UsH78WJmTzeZWcJfX2C2ba3 xwRMB5LaaVrQZlhj9xjum0MfDpIS1hJI6p5CHZ8w&scope=searchadsorg'
Request header |
Description |
Host |
Required. appleid.apple.com |
Content-Type |
Required. application/x-www-form-urlencoded |
Request parameter |
Type |
Description |
client_id |
String |
Required. You receive your clientId when you upload a public key. |
client_secret |
String |
Required. The client secret is a JWT that you create and sign with your private key. |
grant_type |
String |
Required. The method to request authorization and get an access token. The value is client_credentials. |
scope |
String |
Required. Defines the access permissions you request from the user, and limits the authorization of the access token you receive. The value is searchadsorg. |
返回值:
{ "access_token":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjpudWxsfQ ..lXm332TFi0u2E9YZ.bVVBvsjcavoQbBnQVeDiqEzmUIlaH9zLKY6rl36A_TD8wvgvWxp yBXMQuhs-qWG_dxQ5nfuJEIxOp8bIndfLE_4a3AiYtW0BsppO3vkWxMe0HWnzglkFbKUHU 3PaJbLHpimmnLvQr44wUAeNcv1LmUPaSWT4pfaBzv3dMe3PNHJJCLVLfzNlWTmPxViIivQ t3xyiQ9laBO6qIQiKs9zX7KE3holGpJ-Wvo39U6ZmGs7uK9BoNBPaFtd_q914mb9ChHAKc QaxF3Gadtu_Z5rYFg.vD0iQuRwHGYVnDy27qexCw", "token_type": "Bearer", "expires_in": 3600, "scope": "searchadsorg" }
(1)通过 Apple 页面拿到 orgId;
(2)通过 API 的方式拿到 orgId;
curl "https://api.searchads.apple.com/api/v3/acls
" \-H "Authorization: Bearer {access_token}" \
curl "https://api.searchads.apple.com/api/v4/campaigns" \ -H "Authorization: Bearer {access_token}" \ -H "X-AP-Context: orgId={orgId}"