asa(苹果Apple Search Ads平台)授权调用接口
一、OAuth 认证
1. 认证流程
Implementing OAuth for the Apple Search Ads API
This implementation process guides you through the following steps:
- Invite users with API permissions.
- Generate a private-public key pair.
- Extract a public key from your persisted private key.
- Upload a public key.
- Create a client secret.
- Request an access token.
- 邀请拥有api权限的用户
- 生成专用公钥对
- 从持久化私钥中提取公钥
- 上传公钥
- 创建客户端密钥
- 请求访问令牌
2. 邀请用户
Invite Users
Account administrators invite users with API permissions using the following process:
- From the Apple Search Ads UI, choose Sign In > Advanced and log in as an account administrator.
- From the Users menu in the top-right corner, select the account to invite users to.
- Choose Account Settings > User Management.
- Click Invite Users to invite users to your Apple Search Ads organization.
- In the User Details section, enter the user’s first name, last name, and Apple ID.
- In the User Access and Role section, select an API user role. For non-API roles, see Invite users to your account.
- Click Send Invite. The invited user receives an email with a secure code. The user signs into the secure Apple URL in the email and inputs the provided code, which activates the user’s account.
管理员账号通过以下过程邀请用户具有api权限:
- 从Apple Search Ads UI中,选择“登录”>“高级(Advanced)”,然后以帐户管理员身份登录
登陆网址
图2-1:登陆界面
- 从右上角的“用户”菜单中,选择要邀请用户加入的帐户。
- 选择“帐户设置”>“用户管理”。
- 单击邀请用户以邀请用户加入您的Apple Search Ads组织。
- 在“用户详细信息”部分,输入用户的名字、姓氏和Apple ID。
- 在“用户访问权限和角色”部分中,选择一个API用户角色(Read Only、API Read Only)。有关非API角色,请参阅邀请用户加入您的帐户。
- 单击发送邀请。受邀用户收到一封带有激活码的电子邮件。用户登录电子邮件中的安全Apple URL并输入提供的激活码,从而激活用户的帐户:
整个过程开发者需要提供 Apple ID,具有苹果广告账户管理员权限的账号授权对应子账户的【最低 API 只读】权限;用户前往授权 Apple ID 邮箱查收邀请邮件发送对应的【激活码】,点击接受邀请,此时跳转至苹果广告登录后台,输入邀请【激活码】,完成苹果广告后台登录流程;
图2-2:Apple ID 邮箱查收邀请邮件并跳转激活账号
3. 密钥管理
需要下载openssl
- openssl生成私钥文件:openssl ecparam -genkey -name prime256v1 -noout -out private-key.pem
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. 文件名 |
- openssl通过私钥文件抽取公钥文件:openssl ec -in private-key.pem -pubout -out public-key.pem
Parameter | Description |
-in | The private key filename: private-key.pem 输入文件名 |
-out | The ec256-public-key file where you generate and store the public key. 输出文件名 |
- openssl通过私钥文件生成 pkcs8 编码文件: openssl pkcs8 -topk8 -nocrypt -in private-key.pem -out pkcs8_rsa_private_key.pem
- 将公钥粘贴到 ASA 后台(设置 > API),生成 clientId, teamId, keyId
图3-1:通过公钥生成clientId,teamId,keyId
4. 代码生成服务端token
将asa后台生成的 clientId, teamId, keyId以及私钥生成的 pkcs8 编码字符串拿出来备用
- java代码
依赖
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency>
代码
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); // 构建载荷 Map<String, Object> headers = new HashMap<>(); headers.put("alg", alg); headers.put("kid", keyId); long iat = DateUtil.currentSeconds(); long exp = iat + 86400 * 180; Map<String, Object> 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(); }
- python代码
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" }
5. 请求获取accesstoken
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" }
6. 调用接口携带accesstoken和orgId
- 获取 orgid:
(1)通过 Apple 页面拿到 orgId;
(2)通过 API 的方式拿到 orgId;
curl "https://api.searchads.apple.com/api/v3/acls
" \-H "Authorization: Bearer {access_token}" \
- 通过accesstoken和orgId访问所有接口
curl "https://api.searchads.apple.com/api/v4/campaigns" \ -H "Authorization: Bearer {access_token}" \ -H "X-AP-Context: orgId={orgId}"