> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 用于 PlayFab API 的 Microsoft Entra ID 身份验证

> 使用 Microsoft Entra ID 委托令牌通过 OAuth 2.0 流程（如 PKCE、设备代码或隐式授权）向 PlayFab Admin、Server 和实体 API 进行身份验证。

PlayFab 支持使用 Microsoft Entra ID（以前称为 Azure Active Directory）身份验证来调用 Admin、Server 和实体 API。使用 Entra ID 身份验证时，你使用 Microsoft 身份登录，并使用委托的访问令牌而不是开发者密钥。你的应用程序使用标准 OAuth 2.0 公共客户端流程（例如带 PKCE 的授权代码流、设备代码流或隐式授权）处理登录体验。

<Note>
  Entra ID 身份验证是开发者密钥的替代方案，用于 Admin、Server 和某些实体 API 调用。面向玩家的 Client 和实体 API 继续使用通过玩家登录获得的会话票据和实体令牌。只有可以使用 *title* 实体令牌调用的实体 API 才能与 Entra ID 一起使用。有关密钥的更多信息，请参阅[密钥管理](/services/playfab/live-service-management/gamemanager/secret-key-management)。
</Note>

## 为何使用 Entra ID 身份验证

开发者密钥使用简单，但 Entra ID 身份验证提供了几个优势：

* **无共享密钥**——公共客户端流程不需要客户端密钥。令牌是短期的并作用于登录的用户。
* **个人责任**——每个 API 调用都绑定到特定用户身份，便于审计谁做了什么。
* **条件访问**——你的组织可以应用 Entra ID 策略，如 IP 限制、多重身份验证 (MFA) 和设备合规性检查。

## Entra ID 身份验证的工作原理

如果你以前从未使用过 Microsoft Entra ID，它是 Microsoft 的云身份服务——为 Microsoft 365、XBOX 开发者帐户和 Azure 提供支持的相同身份系统。你的工具会请求 Entra ID 让开发者登录并返回一个短期**访问令牌**，而不是由 PlayFab 向你的工作室颁发长期密钥。PlayFab 信任 Entra ID 为你工作室成员用户颁发的令牌。

涉及三方：

* \*\*开发者。\*\*你，使用 Microsoft 帐户（个人）或工作或学校帐户（Entra ID）登录。
* \*\*Entra ID 中的应用注册。\*\*这代表调用 PlayFab 的工具——一个 CLI、构建脚本、内部仪表板等。它告诉 Entra ID“允许此应用代表已登录的用户请求 PlayFab 访问令牌”。
* \*\*PlayFab 服务。\*\*PlayFab 在 Entra ID 中以应用程序 ID `448adbda-b8d8-4f33-a1b0-ac58cf44d4c1` 注册，并公开一个名为 `plugin` 的委托权限。当你的代码请求作用域 `448adbda-b8d8-4f33-a1b0-ac58cf44d4c1/plugin` 时，Entra ID 会颁发一个 PlayFab 接受的令牌。

典型的调用如下所示：

1. 你的代码使用 Microsoft 身份验证库 (MSAL) 启动登录——通常是浏览器弹出窗口或设备代码提示。
2. 用户使用其 Microsoft 身份登录并同意你的应用代表他们调用 PlayFab。
3. MSAL 返回一个访问令牌（一个 JWT）并缓存一个刷新令牌，以便将来的调用可以跳过提示。
4. 你的代码在每个 PlayFab 请求上以 `Authorization: Bearer <token>` 标头发送该令牌。
5. PlayFab 验证令牌，将其映射到匹配的工作室用户，并根据该用户的角色授权调用。

有关本文使用的 Entra ID 术语定义，请参阅末尾的[术语表](#glossary)。

## 先决条件

* 由 Entra ID 支持的 Microsoft 帐户（工作或学校帐户）或个人 Microsoft 帐户 (MSA)。
* 一个 PlayFab title。有关更多信息，请参阅[创建 PlayFab 账户](/services/playfab/identity/dev-identity/pfab-account)。
* 在你的 Microsoft Entra ID 租户中注册应用程序的权限。
* 调用方用户必须已添加到 PlayFab 工作室并指定为 title 管理员。有关角色的更多信息，请参阅 [PlayFab 用户角色](/services/playfab/identity/dev-identity/permissions/playfab-user-roles)。

## 在 Entra ID 中注册公共客户端应用程序

你需要在 Entra ID 租户中注册一个应用程序，以便你的代码可以代表已登录的用户请求委托令牌。对于公共客户端流程（SPA、设备代码、隐式），不需要客户端密钥。

1. 登录 [Azure 门户](https://portal.azure.com)。

2. 搜索并选择 **App registrations**，然后选择 **New registration**。

3. 输入应用程序的名称（例如 `PlayFab API Client`）。

4. 在 **Supported account types** 下，选择与你组织要求匹配的选项。为获得最大灵活性，请选择 **Accounts in any organizational directory (Any Microsoft Entra ID tenant - Multitenant) and personal Microsoft accounts**。此选项允许任何 Microsoft 帐户进行身份验证。

5. 根据你的应用程序类型配置 **Redirect URI**：
   * 对于单页应用程序 (SPA)，选择 **Single-page application (SPA)** 并输入你的重定向 URI（例如 `http://localhost:3000`）。
   * 对于使用设备代码流的原生或控制台应用程序，选择 **Public client/native (mobile & desktop)** 并输入 `http://localhost`。

6. 选择 **Register**。在概览页面上，记下 **Application (client) ID**。请求令牌时需要此值。

7. 将 PlayFab API 权限添加到你的应用程序注册中：
   1. 在 Azure 门户中，导航到你的应用程序注册并选择 **API permissions**。
   2. 选择 **Add a permission** > **APIs my organization uses**。
   3. 搜索 PlayFab 应用程序 ID `448adbda-b8d8-4f33-a1b0-ac58cf44d4c1` 并选择它。
   4. 选择 **Delegated permissions**，选中 **plugin** 权限，然后选择 **Add permissions**。

<Note>
  如果你的场景需要机密（Web 应用）客户端，则还需要在 **Certificates & secrets** 下创建客户端密钥。本文重点介绍不需要密钥的公共客户端流程。有关机密客户端流程的更多信息，请参阅 [Microsoft 身份平台和 OAuth 2.0 授权代码流](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-auth-code-flow)。
</Note>

## 设置 PlayFab 工作室访问

PlayFab 根据工作室成员身份验证 Entra ID 令牌。调用方用户必须已添加到 PlayFab 工作室并被授予适当的角色。

1. 让一位工作室管理员登录 [Game Manager](https://developer.playfab.com)。

2. 导航到工作室的 **Users** 部分。

3. 选择 **Add User** 并输入需要 API 访问权限的开发者的 Microsoft 帐户电子邮件。

4. 选择 **Microsoft** 作为身份验证提供程序。

5. 为用户分配包括 Admin 或 Server API 访问权限的角色。至少，该用户必须是他们需要调用 API 的 title 的 **title admin**。

6. 选择 **Add user** 发送邀请。

有关添加用户和分配角色的更多信息，请参阅 [PlayFab Game Manager 的帐户身份验证](/services/playfab/identity/dev-identity/authentication/aad-authentication)。

## 获取访问令牌

你的应用程序负责让用户登录并从 Entra ID 获取委托的访问令牌。以下示例展示了常见的公共客户端流程。

### 交互式浏览器流程（推荐用于桌面和控制台应用）

交互式浏览器流程打开系统浏览器窗口进行登录。它是桌面应用程序和本地开发工具的推荐选项。

```csharp theme={null}
using Microsoft.Identity.Client;

// Build an MSAL public client app. The client ID identifies your Entra app
// registration; "common" lets users sign in from any Entra tenant or with a
// personal Microsoft account.
var app = PublicClientApplicationBuilder.Create("your-client-id")
    .WithAuthority("https://login.microsoftonline.com/common")
    .WithDefaultRedirectUri()
    .Build();

// The scope identifies the PlayFab API and the delegated permission ("plugin")
// your app is requesting on behalf of the signed-in user.
string[] scopes = ["448adbda-b8d8-4f33-a1b0-ac58cf44d4c1/plugin"];

AuthenticationResult result;
try
{
    // Try to get a token from MSAL's cache without prompting the user.
    // This succeeds on subsequent runs while the cached refresh token is valid.
    var accounts = await app.GetAccountsAsync();
    result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
        .ExecuteAsync();
}
catch (MsalUiRequiredException)
{
    // No cached token, or it expired and can't be refreshed silently.
    // Open the system browser so the user can sign in interactively.
    result = await app.AcquireTokenInteractive(scopes)
        .WithUseEmbeddedWebView(false)
        .ExecuteAsync();
}

// The access token is what you send to PlayFab in the Authorization header.
string accessToken = result.AccessToken;
```

### 带 PKCE 的授权代码流（推荐用于 SPA）

带 Proof Key for Code Exchange (PKCE) 的授权代码流是单页应用程序的推荐方法。以下 JavaScript 示例使用 Microsoft 身份验证库 (MSAL)：

```javascript theme={null}
import { PublicClientApplication } from "@azure/msal-browser";

// MSAL config. clientId is your Entra app registration's Application (client) ID.
// "common" lets users sign in from any tenant or with a personal account.
// redirectUri must match a Single-page application redirect URI on the app registration.
const msalConfig = {
    auth: {
        clientId: "your-client-id",
        authority: "https://login.microsoftonline.com/common",
        redirectUri: "http://localhost:3000"
    }
};

const msalInstance = new PublicClientApplication(msalConfig);
await msalInstance.initialize();

// loginPopup runs the auth code + PKCE flow in a pop-up window. The scope
// asks Entra ID for a delegated token that PlayFab will accept.
const loginResponse = await msalInstance.loginPopup({
    scopes: ["448adbda-b8d8-4f33-a1b0-ac58cf44d4c1/plugin"]
});

// Send this token to PlayFab in the Authorization header.
const accessToken = loginResponse.accessToken;
```

## 使用访问令牌调用 PlayFab API

在你的 PlayFab API 请求中将 Entra ID 访问令牌作为 Bearer 令牌包含在 `Authorization` 标头中，而不是使用 `X-SecretKey` 标头。

### 示例请求

```http theme={null}
POST https://{titleId}.playfabapi.com/Server/GetPlayerProfile
Content-Type: application/json
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi...

{
    "PlayFabId": "ABCDEF1234567890"
}
```

<Note>
  你不能在同一请求中同时使用 `X-SecretKey` 和 `Authorization: Bearer`。每次调用使用一种身份验证方法。
</Note>

### C# 示例

以下示例使用 Microsoft 身份验证库 (MSAL) 获取令牌并调用 `Server/GetTime` API。将 `your-client-id` 替换为你应用注册中的 **Application (client) ID**。

```csharp theme={null}
using Microsoft.Identity.Client;
using System.Net.Http.Headers;
using System.Text;

// PlayFab API endpoint. Every title gets its own subdomain on playfabapi.com.
string titleId = "YOUR_TITLE_ID";
string host = $"{titleId}.playfabapi.com";
string url = $"https://{host}/Server/GetTime";

// MSAL configuration.
// clientId   - your Entra app registration's Application (client) ID.
// authority  - "common" lets any Entra tenant or personal Microsoft account sign in.
// scopes     - the PlayFab application ID + the "plugin" delegated permission.
string clientId = "your-client-id";
string authority = "https://login.microsoftonline.com/common";
string[] scopes = ["448adbda-b8d8-4f33-a1b0-ac58cf44d4c1/plugin"];

// Build the MSAL public client. Public clients (desktop, CLI, mobile) don't use
// a client secret; tokens are bound to the signed-in user instead.
var app = PublicClientApplicationBuilder.Create(clientId)
    .WithAuthority(authority)
    .WithDefaultRedirectUri()
    .Build();

AuthenticationResult authResult;
try
{
    // Reuse a cached token if MSAL has one. Avoids prompting on every run.
    var accounts = await app.GetAccountsAsync();
    authResult = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
        .ExecuteAsync();
}
catch (MsalUiRequiredException)
{
    // First run, or the cached token expired. Open the system browser to sign in.
    authResult = await app.AcquireTokenInteractive(scopes)
        .WithUseEmbeddedWebView(false)
        .ExecuteAsync();
}

// Call PlayFab. Replace X-SecretKey with an Authorization: Bearer header
// containing the Entra ID access token.
using var httpClient = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");

var response = await httpClient.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();

Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Response: {body}");
```

### Node.js 示例

以下示例使用 `@azure/msal-node` 库获取令牌并调用 `Server/GetTime` API。将 `your-client-id` 替换为你应用注册中的 **Application (client) ID**。

```javascript theme={null}
import { PublicClientApplication } from "@azure/msal-node";
import open from "open";

// PlayFab API endpoint for your title.
const titleId = "YOUR_TITLE_ID";
const host = `${titleId}.playfabapi.com`;
const url = `https://${host}/Server/GetTime`;

// MSAL configuration.
// clientId  - your Entra app registration's Application (client) ID.
// authority - "common" accepts any Entra tenant or personal Microsoft account.
const msalConfig = {
    auth: {
        clientId: "your-client-id",
        authority: "https://login.microsoftonline.com/common",
    },
};

const pca = new PublicClientApplication(msalConfig);

// Token request:
// scopes      - PlayFab application ID + the "plugin" delegated permission.
// redirectUri - must match a Public client redirect URI on the app registration.
// openBrowser - MSAL calls this to launch the user's default browser for sign-in.
const tokenRequest = {
    scopes: ["448adbda-b8d8-4f33-a1b0-ac58cf44d4c1/plugin"],
    redirectUri: "http://localhost:3000",
    openBrowser: async (url) => { await open(url); },
    successTemplate: "<h1>Authentication complete. You can close this window.</h1>",
};

// Sign the user in and obtain a delegated access token.
const authResult = await pca.acquireTokenInteractive(tokenRequest);

// Call PlayFab with the token in the Authorization header instead of X-SecretKey.
const response = await fetch(url, {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${authResult.accessToken}`,
    },
    body: JSON.stringify({}),
});

const body = await response.json();
console.log(`Status: ${response.status}`);
console.log("Response:", JSON.stringify(body, null, 2));
```

## 令牌刷新

Entra ID 访问令牌是短期的（通常为 60–90 分钟）。如果你使用 MSAL，请在每个请求之前调用 `AcquireTokenSilent`（C#）或检查令牌缓存——当有缓存的刷新令牌可用时，MSAL 会自动处理刷新。

如果你手动管理令牌，请在当前令牌过期之前使用相同的登录流程请求一个新令牌。

<Info>
  如果令牌在使用过程中过期，PlayFab 将返回 `401 Unauthorized` 响应。你的应用程序应通过请求新令牌并重试调用来处理此错误。
</Info>

## 故障排除

| 问题                                             | 原因                       | 解决方案                                   |
| ---------------------------------------------- | ------------------------ | -------------------------------------- |
| `401 Unauthorized`                             | 令牌已过期或无效                 | 重新登录并请求新令牌                             |
| `403 Forbidden`                                | 用户在 title 上没有所需的角色       | 在 Game Manager 中验证工作室成员身份和 title 管理员角色 |
| `AADSTS50076: Need multifactor authentication` | 条件访问策略要求 MFA             | 完成 MFA 挑战并重试                           |
| `AADSTS700016: Application not found`          | Client ID 不正确或应用不在预期的租户中 | 验证 client ID 和 tenant ID               |

### 示例错误响应

当 Bearer 令牌缺失、格式错误或已过期时，PlayFab 返回的 `401 Unauthorized` 如下所示：

```json theme={null}
{
    "code": 401,
    "status": "Unauthorized",
    "error": "NotAuthenticated",
    "errorCode": 1074,
    "errorMessage": "Invalid \"Authorization\" header."
}
```

当登录用户已经过身份验证但在 title 上缺乏管理员权限时，PlayFab 返回的 `403 Forbidden` 如下所示：

```json theme={null}
{
    "code": 403,
    "status": "Forbidden",
    "error": "NotAuthorizedByTitle",
    "errorCode": 1191,
    "errorMessage": "Developer does not have admin permission for this title."
}
```

## 限制

PlayFab SDK 当前不支持 Entra ID 身份验证。要今天使用 Entra ID 令牌调用 PlayFab API，请使用直接 HTTP 调用，并自行设置 `Authorization: Bearer <token>` 标头，如 [C#](#c-example) 和 [Node.js](#nodejs-example) 示例所示。SDK 支持计划在未来版本中提供。

## 术语表

| 术语                       | 含义                                                                                               |
| ------------------------ | ------------------------------------------------------------------------------------------------ |
| **Tenant**               | Entra ID 中的一个独立目录。你的公司有一个；个人 Microsoft 帐户使用称为 *consumers* 的共享租户。示例中使用的 `common` 颁发机构允许用户从任何租户登录。 |
| **App registration**     | Entra ID 中描述你的客户端应用程序（其 client ID、允许的重定向 URI、请求的 API 权限）的记录。                                     |
| **Delegated permission** | 你的应用*代表*已登录的用户接收的权限，而不是自行操作。                                                                     |
| **Public client**        | 无法安全存储密钥的客户端——桌面或移动应用、CLI 或单页 Web 应用。公共客户端使用带 PKCE 的授权代码或设备代码等流程，而不是客户端密钥。                       |
| **Scope**                | 你的代码向 Entra ID 请求的 API 权限。对于 PlayFab，该作用域是 `448adbda-b8d8-4f33-a1b0-ac58cf44d4c1/plugin`。        |
| **MSAL**                 | Microsoft Authentication Library。官方客户端库，它实现了所有这些流程，因此你不必手动构造 OAuth 请求。                           |

## 另请参阅

* [密钥管理](/services/playfab/live-service-management/gamemanager/secret-key-management)
* [PlayFab Game Manager 的帐户身份验证](/services/playfab/identity/dev-identity/authentication/aad-authentication)
* [PlayFab 用户角色](/services/playfab/identity/dev-identity/permissions/playfab-user-roles)
* [API 访问策略](/services/playfab/api-references/api-access-policy)
* [Microsoft Entra ID 文档](https://learn.microsoft.com/en-us/entra/fundamentals/whatis)


## Related topics

- [PlayFab 的 Microsoft 帐户身份验证](/zh-CN/services/playfab/identity/dev-identity/authentication/aad-authentication.md)
- [将 Grafana 连接到 Insights](/zh-CN/services/playfab/data-analytics/legacy/connectivity/connecting-grafana-to-insights.md)
- [使用 Microsoft Store API 对你的服务进行身份验证](/zh-CN/publishing/xstore-commerce/xstore-authenticating-service.md)
- [为服务到服务身份验证请求 User Store ID](/zh-CN/publishing/xstore-commerce/xstore-requesting-userstoreid.md)
- [市场集成 - Microsoft Store](/zh-CN/services/playfab/economy-monetization/economy-v2/marketplace/marketplace-integrations/microsoft.md)
