# Khóa API App Store Connect (.p8): cách tạo và sử dụng

> Khóa API App Store Connect (.p8) chỉ tải về được một lần. Vai trò, Issuer ID, Key ID, thu hồi khóa và cách dùng với Expo, fastlane hoặc script.

Trang gốc: [Khóa API App Store Connect (.p8): cách tạo và sử dụng](https://devoria.fr/vi/blog/khoa-api-app-store-connect-p8)
Tác giả: [Mehdi Azizi](https://devoria.fr/vi/mehdi-azizi)
Ngày đăng: 2026-11-04
Lần sửa nội dung gần nhất: 2026-11-04


Khóa API App Store Connect cho phép một công cụ như EAS, fastlane, RevenueCat hay script của riêng bạn thao tác trên tài khoản mà không cần mật khẩu hay xác thực hai yếu tố. **Apple giữ nửa công khai của khóa, còn bạn tải về nửa riêng tư, một file `.p8`, và chỉ tải được một lần.** Với file này, Issuer ID và Key ID, bạn ký các token có hiệu lực tối đa 20 phút cho phần lớn các request.

Mình phát hành các app iOS của mình, trong đó có TradaZen và Nolera, bằng Expo. Một khóa nhóm với vai trò App Manager được dùng cho RevenueCat, cho các bản build Expo và cho một công cụ dòng lệnh chỉnh sửa trang app của mình. Hướng dẫn này dựa trên các trang của Apple, Expo và fastlane mà mình đọc lại ngày 3 tháng 11 năm 2026. Đừng nhầm nó với khóa của các dịch vụ được nhét vào code của app: loại đó được nói trong [khóa API bí mật trong app Expo](https://devoria.fr/vi/blog/khoa-api-bi-mat-app-expo).

## Khóa API App Store Connect dùng để làm gì

Theo [tài liệu của Apple](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api), API App Store Connect yêu cầu một token JWT cho mỗi request. Bạn tự tạo và ký token bằng khóa riêng tư. Khóa này chỉ dùng cho API đó: bạn không dùng được nó cho các dịch vụ khác của Apple.

Lợi ích của nó gói gọn trong một câu: **không cần ai xác nhận đăng nhập.** Apple ID của bạn vướng xác thực hai yếu tố, và điều đó chặn mọi kiểu tự động hóa. Hướng dẫn [EAS Submit iOS](https://devoria.fr/vi/blog/eas-submit-ios-dua-app-expo-len-app-store) cho thấy nó thay đổi gì khi gửi một app Expo.

## Trước hết: xin quyền truy cập API

Bạn không tạo được khóa đầu tiên ngay. [Trang trợ giúp App Store Connect](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api) nói rõ: **Account Holder phải xin quyền truy cập API trước.** Người đó mở Users and Access, bấm Integrations, rồi Request Access. Apple xét từng yêu cầu một.

Account Holder là người đã đăng ký tài khoản. Nếu bạn mới bắt đầu, đó chính là bạn: hướng dẫn [tài khoản nhà phát triển Apple](https://devoria.fr/vi/blog/tai-khoan-apple-developer-gia) nói chi tiết về việc đăng ký.

## Khóa nhóm hay khóa cá nhân

Apple có hai loại khóa. Đây là điểm khác nhau, lấy từ [tài liệu](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api) và [trang trợ giúp App Store Connect](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api):

| Tiêu chí                        | Khóa nhóm                 | Khóa cá nhân                  |
| ------------------------------- | ------------------------- | ----------------------------- |
| Ai tạo được                     | Account Holder hoặc Admin | Gần như mọi người dùng        |
| App truy cập được               | Tất cả, không ngoại lệ    | App của người dùng đó         |
| Quyền                           | Vai trò được chọn         | Quyền của người dùng đó       |
| Số lượng khóa                   | Nhiều                     | Một khóa đang hoạt động/người |
| Chứng chỉ và profile            | Có, tùy vai trò           | Không                         |
| Doanh số, tài chính, notaryTool | Tùy vai trò               | Không                         |
| Trường trong token              | `iss` = Issuer ID         | `sub` = `user`                |

"Gần như mọi người dùng" theo Apple nghĩa là: Account Holder, Admin, App Manager, Customer Support, Developer và Marketing, trừ khi một Admin hoặc Account Holder đã gỡ quyền tạo khóa cá nhân của họ.

**Với Expo hoặc fastlane, hãy chọn khóa nhóm.** Khóa cá nhân không truy cập được các endpoint provisioning, tức các endpoint tạo chứng chỉ và profile. [Tài liệu của fastlane](https://docs.fastlane.tools/app-store-connect-api/) xác nhận: mọi truy cập liên quan đến provisioning đều cần khóa nhóm.

## Nên gán vai trò nào cho khóa

Khóa nhóm truy cập mọi app trong tài khoản, bất kể vai trò. Vai trò quyết định nó làm được gì. **Apple cảnh báo rằng khóa Admin có thể tạo và xóa người dùng**: hãy chọn vai trò nhỏ nhất đủ cho nhu cầu của bạn. Các quyền dưới đây lấy từ [bảng vai trò của Apple](https://developer.apple.com/help/account/access/roles/).

| Mục đích dùng khóa                | Vai trò cho phép              |
| --------------------------------- | ----------------------------- |
| Gửi bản build                     | Admin, App Manager, Developer |
| Quản lý bản build TestFlight      | Admin, App Manager            |
| Tạo chứng chỉ và profile          | Admin, App Manager            |
| Gửi app đi duyệt                  | Admin, App Manager            |
| Nhập sản phẩm và giá (RevenueCat) | Tối thiểu App Manager         |
| Tạo các khóa nhóm khác            | Admin                         |

Dòng RevenueCat lấy từ [tài liệu của họ](https://www.revenuecat.com/docs/service-credentials/itunesconnect-app-specific-shared-secret/app-store-connect-api-key-configuration). Còn hướng dẫn của Expo thì [gán vai trò Admin](https://github.com/expo/fyi/blob/main/creating-asc-api-key.md) cho khóa. Với các app của mình, App Manager là đủ: EAS tạo được distribution profile với nó, và RevenueCat nhập được sản phẩm.

Khi khóa đã được tạo, [bạn không sửa được tên lẫn vai trò của nó nữa](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api). Muốn đổi thì phải thu hồi và tạo khóa khác.

## Tạo khóa nhóm từng bước

Bạn cần tài khoản Admin hoặc Account Holder. Theo [Apple](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api):

1. **Mở Users and Access**, rồi tab Integrations.
2. **Chọn App Store Connect API** ở cột bên trái, rồi tab Team Keys.
3. **Bấm Generate API Key**, hoặc nút +.
4. **Đặt tên cho khóa.** Tên chỉ để bạn nhận biết, nó không thuộc về khóa. Hãy đặt theo công cụ sẽ dùng khóa.
5. **Chọn vai trò** ở mục Access, rồi bấm Generate.
6. **Tải khóa riêng tư** qua liên kết Download API Key.

> Liên kết tải về biến mất ngay khi bạn tải file, và Apple không giữ bản sao nào. Hãy cất file `.p8` trước khi đóng trang.

Sau đó ghi lại hai mã định danh. Theo [trang của Apple về token](https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests), **Issuer ID hiển thị ở đầu trang Integrations**, kèm nút Copy. Key ID nằm trong danh sách khóa đang hoạt động: rê chuột lên để hiện Copy Key ID. Issuer ID giống nhau cho mọi khóa nhóm của tài khoản. Key ID là riêng của từng khóa.

Với khóa cá nhân thì ngắn hơn: mở hồ sơ người dùng của bạn, kéo xuống Individual API Key và bấm Generate API Key. Ở đây cũng chỉ tải về được một lần.

## Cất giữ file .p8

Apple yêu cầu xem khóa như mật khẩu: **đừng chia sẻ, đừng để trong kho code, tuyệt đối không nhúng vào code của app.** Thói quen của mình:

- **Một thư mục nằm ngoài dự án**, hoặc trình quản lý secret của công cụ CI.
- **Thêm thư mục chứa khóa vào `.gitignore`**, nếu file phải nằm trong dự án trong lúc gửi app.
- **Quyền truy cập chặt.** Một công cụ dòng lệnh cho App Store Connect đã từ chối file `.p8` của mình chừng nào tài khoản khác còn đọc được nó. Một lệnh `chmod 600` đã giải quyết.
- **Mỗi công cụ một khóa.** Apple cho phép [tạo nhiều khóa](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api), với vai trò tùy bạn chọn.

Điểm cuối này lúc đầu mình không làm. Khóa App Manager của mình dùng chung cho RevenueCat, cho các bản build và cho công cụ chỉnh trang app. Nếu phải thu hồi nó, cả ba công cụ cùng sập. **Nếu làm lại, mình sẽ tạo mỗi công cụ một khóa**, để có thể ngắt một khóa mà không ảnh hưởng các khóa khác.

## Dùng với Expo

Có ba cách đưa khóa cho EAS:

- **Để EAS quản lý.** `eas credentials --platform ios` có thể tạo khóa và lưu trên máy chủ của Expo. Đây là cách đơn giản nhất.
- **Khai báo trong `eas.json`.** Profile `submit` nhận `ascApiKeyPath`, `ascApiKeyIssuerId` và `ascApiKeyId`. Theo [tài liệu tham chiếu eas.json](https://docs.expo.dev/eas/json/), các trường này nhận biến môi trường, ví dụ `$ASC_API_KEY_PATH`.
- **Truyền qua biến môi trường.** [Repo của EAS CLI](https://github.com/expo/eas-cli/blob/main/packages/eas-cli/README.md) ghi rõ `EXPO_ASC_API_KEY_PATH`, `EXPO_ASC_KEY_ID` và `EXPO_ASC_ISSUER_ID`. Với khóa cá nhân thì bỏ Issuer ID.

Một giới hạn cần biết khi build: [Expo cho biết](https://docs.expo.dev/build-reference/ios-capabilities/) API chính thức không gắn được App Group, Merchant ID hay CloudKit container. Khi đó bạn cần đăng nhập bằng Apple ID, trên máy local. Mình đã gặp chuyện này với Nolera, ở app group của widget: khóa bật được capability nhưng không gắn được group. Mình đã gắn nó trong cổng nhà phát triển của Apple. **Nếu việc đồng bộ capability bị kẹt, `EXPO_NO_CAPABILITY_SYNC=1` sẽ tắt nó**, và bạn tự bật capability trên App ID. Việc ký bản build được nói chi tiết trong [chứng chỉ và provisioning profile iOS](https://devoria.fr/vi/blog/ios-developer-certificate-provisioning-profile-expo).

## Dùng với fastlane

[fastlane](https://docs.fastlane.tools/app-store-connect-api/) đọc khóa từ một file JSON. Các trường `key_id`, `issuer_id` và `key` (nội dung file `.p8`) là bắt buộc. `duration` và `in_house` là tùy chọn. Giá trị giả:

```json
{
  "key_id": "XXXXXXXXXX",
  "issuer_id": "your-issuer-id",
  "key": "-----BEGIN PRIVATE KEY-----\nMIGT...\n-----END PRIVATE KEY-----",
  "duration": 1200,
  "in_house": false
}
```

Sau đó bạn truyền file này cho một action, ví dụ `fastlane pilot distribute --api_key_path fastlane/cle.json`. Trong Fastfile, action `app_store_connect_api_key` nhận `key_id`, `issuer_id` và `key_filepath`. Thời lượng phiên không vượt quá 1.200 giây, tức 20 phút. Không phải action nào cũng hỗ trợ: fastlane cho biết ví dụ `produce` và `pem` không chạy được với khóa API.

## Dùng trong script của riêng bạn

Không có công cụ thì bạn tự tạo token. [Apple quy định công thức](https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests):

- **Header**: `alg` là `ES256`, `kid` là Key ID của bạn, `typ` là `JWT`.
- **Payload, khóa nhóm**: `iss`, Issuer ID của bạn; `iat`, thời điểm tạo; `exp`, thời điểm hết hạn; `aud`, có giá trị `appstoreconnect-v1`.
- **Payload, khóa cá nhân**: `sub` là `user` thay cho `iss`.
- **Thời hạn**: quá 20 phút, Apple từ chối token với phần lớn request.

Bằng Python, dùng thư viện PyJWT và phần phụ thuộc mã hóa của nó, cài bằng `pip install pyjwt[crypto]` theo [tài liệu của nó](https://pyjwt.readthedocs.io/en/stable/installation.html). Trong terminal của Mac, hãy thêm dấu nháy: `pip install "pyjwt[crypto]"`, nếu không zsh sẽ không chấp nhận dấu ngoặc vuông.

```python
import time
import jwt

KEY_ID = "XXXXXXXXXX"
ISSUER_ID = "your-issuer-id"

with open("AuthKey_XXXXXXXXXX.p8") as f:
    private_key = f.read()

now = int(time.time())
token = jwt.encode(
    {"iss": ISSUER_ID, "iat": now, "exp": now + 20 * 60, "aud": "appstoreconnect-v1"},
    private_key,
    algorithm="ES256",
    headers={"kid": KEY_ID, "typ": "JWT"},
)
print(token)
```

Rồi bạn gửi nó trong header `Authorization`, như ví dụ của Apple:

```bash
curl -H "Authorization: Bearer $TOKEN" "https://api.appstoreconnect.apple.com/v1/apps"
```

Mình đã chạy script này ngày 3 tháng 11 năm 2026 với một khóa dùng một lần tạo trên máy mình: header và payload của token khớp với trang của Apple, và chữ ký ES256 xác minh được. Mình không gửi nó lên API của Apple với khóa thử này, vì Apple không có khóa đó. **Hãy dùng lại cùng một token chừng nào nó còn hiệu lực**: Apple khuyên vậy để có hiệu năng tốt hơn.

## Thu hồi khóa

Hãy thu hồi khóa ngay khi bị mất, bị lộ, hoặc khi một công cụ không còn dùng đến. Theo [trang trợ giúp App Store Connect](https://developer.apple.com/help/app-store-connect/get-started/app-store-connect-api), với tài khoản Account Holder hoặc Admin:

1. Mở Users and Access, rồi Integrations.
2. Chọn Team Keys hoặc Individual Keys, rồi bấm Edit.
3. Đánh dấu các khóa cần thu hồi, rồi Revoke Key.
4. Xác nhận bằng Revoke.

**Khóa đã thu hồi không bao giờ kích hoạt lại được.** [Apple hiển thị nó](https://developer.apple.com/documentation/appstoreconnectapi/revoking-api-keys) 30 ngày ở mục Revoked. Mọi công cụ từng dùng nó sẽ xác thực thất bại. Với Expo, thông báo là "[App Store Connect rejected the API authentication credentials](https://github.com/expo/eas-cli/blob/main/packages/build-tools/src/steps/utils/ios/AscApiClient.ts)". Thông báo này và các lỗi gửi app khác được giải thích trong [các lỗi gửi app với EAS và cách sửa](https://devoria.fr/vi/blog/loi-eas-submit-ios-cach-sua). Nếu khóa chưa bị lộ, hãy chuẩn bị khóa mới trước khi ngắt khóa cũ. Nếu đã bị lộ, Apple yêu cầu thu hồi ngay lập tức.

Với khóa cá nhân của bạn: vào hồ sơ, Individual API Key, rồi Revoke.

## Checklist khóa .p8 của mình

- Account Holder đã xin quyền truy cập API.
- Khóa là khóa nhóm nếu dùng cho build hoặc profile.
- Vai trò là vai trò nhỏ nhất đủ dùng, và tên khóa cho biết công cụ nào dùng nó.
- File `.p8` được cất ngoài repo, với quyền truy cập chặt và một bản sao lưu đã mã hóa.
- Issuer ID và Key ID được ghi lại riêng, không kèm file.
- Mỗi công cụ có khóa riêng, để có thể thu hồi từng khóa một.

## Tự động hóa việc phát hành mà không để lộ khóa

Khóa chỉ là một mảnh trong cả hành trình. [iOS & Android Kit](https://devoria.fr/vi/kits/ios) xuất phát từ code nền của các app của mình, có cả gói đăng ký, và đi qua bốn bước đến App Store, theo nhịp của bạn. Nếu bạn kết nối RevenueCat bằng khóa này, cách thiết lập đầy đủ nằm trong [RevenueCat và gói đăng ký in-app iOS](https://devoria.fr/vi/blog/revenuecat-goi-dang-ky-in-app-ios).

Nếu bạn đang chuẩn bị ra mắt app đầu tiên, [hãy làm bài chẩn đoán miễn phí](https://devoria.fr/vi/diagnostic): nó cho bạn biết điều gì thật sự đang cản bạn và gửi bạn checklist chống bị Apple từ chối của mình. Trang [phát hành ứng dụng lên App Store](https://devoria.fr/vi/expertise/dang-ung-dung-len-app-store) đi lại từng bước, từ trang app đến lúc được duyệt.

## Câu hỏi thường gặp

### Làm sao tạo khóa API App Store Connect?

Trước tiên Account Holder xin quyền truy cập API. Sau đó một Admin mở Users and Access, tab Integrations, App Store Connect API, Team Keys. Người đó bấm Generate API Key, đặt tên và chọn vai trò, tạo khóa, rồi tải file `.p8`.

### Có tải lại được file .p8 bị mất không?

Không. File chỉ tải được một lần, và Apple không giữ bản sao nào. Hãy thu hồi khóa bị mất, tạo khóa mới và cập nhật mọi công cụ từng dùng nó.

### Tìm Issuer ID và Key ID ở đâu?

Trong App Store Connect, menu Users and Access, tab Integrations. Issuer ID hiển thị ở đầu trang. Key ID nằm trong danh sách khóa đang hoạt động. Khóa cá nhân không dùng Issuer ID.

### Nên gán vai trò nào cho khóa dùng với Expo?

Hướng dẫn của Expo gán vai trò Admin. Theo bảng vai trò của Apple, App Manager là đủ để gửi bản build và tạo chứng chỉ, profile. Đó là vai trò khóa của mình, dùng cho các bản build Expo.

### Có đổi được vai trò của khóa API không?

Không. Apple không cho sửa tên lẫn vai trò của khóa sau khi tạo. Hãy thu hồi nó, rồi tạo khóa mới với vai trò đúng.

### Khóa API App Store Connect có hết hạn không?

Các trang của Apple không nhắc đến việc khóa hết hạn, chỉ nói về thu hồi. Thứ hết hạn là các token được ký bằng khóa, tối đa sau 20 phút với phần lớn request.


## Dự án của bạn

[Review Kit](https://devoria.fr/vi/kits/app-review): Checklist duyệt App Store của mình, các lần Apple từ chối đã sửa, checklist Google Play và ba skill Claude Code. 1.399.000 ₫.

[iOS & Android Kit](https://devoria.fr/vi/kits/ios): Tạo app iPhone và Android với Claude Code: code khởi đầu từ các app của mình, lộ trình đến tận store và tất cả các kit. 2.799.000 ₫.

[Chẩn đoán: điều gì ngăn bạn có app iPhone](https://devoria.fr/vi/diagnostic): 7 câu hỏi, 60 giây: bạn biết điều gì thật sự cản bạn đăng app iPhone, lộ trình 4 bước và kit phù hợp với bạn.
