# 授权 OAuth 应用

你可以允许其他用户授权 OAuth app。

> \[!NOTE]
> 请考虑构建GitHub App，而不是OAuth app。
>
> OAuth apps 和 GitHub Apps 均使用 OAuth 2.0。
>
> GitHub Apps 可以代表用户（类似于 OAuth app或自己）执行操作，这对于不需要用户输入的自动化有利。 此外， GitHub Apps 使用细粒度的权限，让用户可以更好地控制应用可以访问的存储库，并使用生存期较短的令牌。 有关详细信息，请参阅 [GitHub 应用和 OAuth 应用之间的差异](/zh/enterprise-server@3.22/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps) 和 [关于创建GitHub应用](/zh/enterprise-server@3.22/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps)。

GitHub 的 OAuth 实现支持标准的 [授权码授权类型](https://tools.ietf.org/html/rfc6749#section-4.1) 以及适用于无法访问 Web 浏览器的应用的 OAuth 2.0 [设备授权授予](https://tools.ietf.org/html/rfc8628)。

如果想跳过以标准方式授权应用（例如在测试应用时），可以使用[非 Web 应用程序流](#non-web-application-flow)。

要为您的 OAuth app 授权，请考虑哪种授权流程最适合您的应用。

* [Web 应用程序流](#web-application-flow)：用于授权用户在浏览器中运行的标准 OAuth apps 。 （不支持[隐式授权类型](https://tools.ietf.org/html/rfc6749#section-4.2)。）
* [设备流](#device-flow)：用于无外设应用，例如 CLI 工具。

## Web 应用程序流程

> \[!NOTE]
> 如果要生成GitHub应用，仍可使用 OAuth Web 应用程序流，但设置有一些重要差异。 有关详细信息，请参阅“[代表用户使用 GitHub 应用进行身份验证](/zh/enterprise-server@3.22/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-with-a-github-app-on-behalf-of-a-user)”。

为您的应用授权用户的 Web 应用流程是：

1. 用户将被重定向以请求其GitHub标识
2. 用户通过GitHub重定向回您的网站
3. 您的应用程序使用用户的访问令牌访问 API

### 1.请求用户的GitHub标识

```
GET http(s)://HOSTNAME/login/oauth/authorize
```

此终结点采用以下输入参数。

| 查询参数           | 类型       | 必需？   | 说明                                                                                                                                                                                                                                                                                                                                         |
| -------------- | -------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `client_id`    | `string` | 必需的   | 你在注册时从GitHub收到的客户端ID。                                                                                                                                                                                                                                                                                                                      |
| `redirect_uri` | `string` | 强烈建议  | 用户获得授权后被发送到的应用程序中的 URL。 请参阅以下有关[重定向 URL](#redirect-urls) 的详细信息。                                                                                                                                                                                                                                                                            |
| `login`        | `string` | 可选    | 提供用于登录和授权应用程序的特定账户。                                                                                                                                                                                                                                                                                                                        |
| `scope`        | `string` | 上下文相关 | 一个由空格分隔的[范围](/zh/enterprise-server@3.22/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)列表。 如果未提供，则 `scope` 对于未为应用程序授权任何范围的用户默认为空列表。 对于已向应用程序授权作用域的用户，不会显示含作用域列表的 OAuth 授权页面。 相反，通过用户向应用程序授权的作用域集，此流程步骤将自动完成。 例如，如果用户已经执行了两次 Web 流，并且已授权一个具有 `user` 范围的令牌和另一个具有 `repo` 范围的令牌，则不提供 `scope` 的第三个 Web 流将收到具有 `user` 和 `repo` 范围的令牌。 |
| `state`        | `string` | 强烈建议  | 不可猜测的随机字符串。 它用于防止跨站请求伪造攻击。                                                                                                                                                                                                                                                                                                                 |
|                |          |       |                                                                                                                                                                                                                                                                                                                                            |
| `allow_signup` | `string` | 可选    | 是否向未经身份验证的用户提供了在 OAuth 流期间注册GitHub的选项。 默认值为 `true`。 在策略禁止注册时使用 `false`。                                                                                                                                                                                                                                                                    |
| `prompt`       | `string` | 可选    | 强制帐户选取器在设置为 `select_account` 时显示。 如果应用程序具有非 HTTP 重定向 URI，或者用户登录了多个帐户，则帐户选取器也会显示。                                                                                                                                                                                                                                                           |

目前不支持 PKCE（代码交换证明密钥）参数 `code_challenge` 和 `code_challenge_method`。
目前不支持 CORS 预测试版请求（OPTIONS）。

### 2. 用户被GitHub重定向回您的网站

如果用户接受你的请求，GitHub 会重定向回你的网站，并在 code 参数中附带一个临时的 `code`，以及在 `state` 参数中附带你在上一步提供的 state。 临时代码将在 10 分钟后到期。 如果状态不匹配，然后第三方创建了请求，您应该中止此过程。

将此 `code` 交换为访问令牌：

```
POST http(s)://HOSTNAME/login/oauth/access_token
```

此终结点采用以下输入参数。

| 参数名称            | 类型       | 必需？  | 说明                                                                 |
| --------------- | -------- | ---- | ------------------------------------------------------------------ |
| `client_id`     | `string` | 必需的  | 从 GitHub 中 OAuth app收到的客户端 ID。                                     |
| `client_secret` | `string` | 必需的  | 你从 GitHub 收到的用于您的 OAuth app 的客户端密钥。                                |
| `code`          | `string` | 必需的  | 您收到的代码是作为对步骤 1 的响应。                                                |
| `redirect_uri`  | `string` | 强烈建议 | 用户获得授权后将被发送到的应用程序 URL。 我们可以使用此参数来匹配发放 `code` 时最初提供的 URI，以防止对服务的攻击。 |
|                 |          |      |                                                                    |

默认情况下，响应采用以下形式：

```shell
access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a&scope=repo%2Cgist&token_type=bearer
```

如果在 `Accept` 标头中提供格式，则还可以接收不同格式的响应。 例如 `Accept: application/json` 或 `Accept: application/xml`：

```json
Accept: application/json
{
  "access_token":"gho_16C7e42F292c6912E7710c838347Ae178B4a",
  "scope":"repo,gist",
  "token_type":"bearer"
}
```

```xml
Accept: application/xml
<OAuth>
  <token_type>bearer</token_type>
  <scope>repo,gist</scope>
  <access_token>gho_16C7e42F292c6912E7710c838347Ae178B4a</access_token>
</OAuth>
```

### 3. 使用访问令牌访问 API

访问令牌可用于代表用户向 API 提出请求。

```
Authorization: Bearer OAUTH-TOKEN
GET http(s)://HOSTNAME/api/v3/user
```

例如，您可以像以下这样在 curl 中设置“授权”标头：

```shell
curl -H "Authorization: Bearer OAUTH-TOKEN" http(s)://HOSTNAME/api/v3/user
```

每次收到访问令牌时，都应使用该令牌重新验证用户的标识。 当你向他们发送邮件授权应用时，用户可以更改他们登录的帐户，如果在每次登录后没有验证用户的标识，则可能会出现混合用户数据的风险。

## 设备流动

设备流允许你授权用户使用无头应用程序，例如 CLI 工具或 [Git 凭据管理器](https://github.com/git-ecosystem/git-credential-manager)。

在使用设备流识别和授权用户之前，必须先在应用的设置中启用它。 有关在应用中启用设备流的详细信息，请参阅针对 [](/zh/enterprise-server@3.22/apps/maintaining-github-apps/modifying-a-github-app-registration) 的 GitHub Apps 以及针对 [](/zh/enterprise-server@3.22/apps/oauth-apps/maintaining-oauth-apps/modifying-an-oauth-app) 的 OAuth apps。

### 设备流程概述

1. 您的应用程序会请求设备和用户验证码，并获取用户将在其中输入用户验证码的授权 URL。
2. 应用提示用户输入用户验证码  `http(s)://HOSTNAME/login/device`。
3. 应用程序轮询用户的身份验证状态。 用户授权设备后，应用程序将能够使用新的访问令牌进行 API 调用。

### 步骤 1：应用从GitHub请求设备和用户验证码

```
POST http(s)://HOSTNAME/login/device/code
```

您的应用程序必须请求用户验证码和验证 URL，因为应用程序在下一步中提示用户进行身份验证时将使用它们。 此请求还返回设备验证代码，应用程序必须使用它们来接收访问令牌和检查用户身份验证的状态。

终结点采用以下输入参数。

| 参数名称                              | 类型       | 说明                                                                                                                                    |
| --------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `client_id`                       | `string` |                                                                                                                                       |
| **必填。** 你为你的应用从 GitHub 收到的客户端 ID。 |          |                                                                                                                                       |
| `scope`                           | `string` | 应用请求访问的范围的列表（以空格分隔）。 有关详细信息，请参阅“[OAuth 应用的范围](/zh/enterprise-server@3.22/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)”。 |

默认情况下，响应采用以下形式：

```shell
device_code=3584d83530557fdd1f46af8289938c8ef79f9dc5&expires_in=900&interval=5&user_code=WDJB-MJHT&verification_uri=https%3A%2F%2FHOSTNAME%2Flogin%2Fdevice
```

| 参数名称                                                    | 类型        | 说明                                                                                                                                                              |
| ------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `device_code`                                           | `string`  | 设备验证码为 40 个字符，用于验证设备。                                                                                                                                           |
| `user_code`                                             | `string`  | 用户验证码显示在设备上，以便用户可以在浏览器中输入该代码。 此代码为 8 个字符，中间有连字符。                                                                                                                |
| `verification_uri`                                      | `string`  | 用户需要输入 `user_code` 的验证 URL： `http(s)://HOSTNAME/login/device`。                                                                                                  |
| `expires_in`                                            | `integer` |                                                                                                                                                                 |
| `device_code` 和 `user_code` 过期之前的秒数。 默认值为 900 秒或 15 分钟。 |           |                                                                                                                                                                 |
| `interval`                                              | `integer` | 在能够发出新的访问令牌请求 (`POST http(s)://HOSTNAME/login/oauth/access_token`) 以完成设备授权之前必须经过的最短秒数。 例如，如果间隔为 5，则只有经过 5 秒后才能发出新请求。 如果在 5 秒内发出多个请求，则将达到速率限制并收到 `slow_down` 错误。 |

如果在 `Accept` 标头中提供格式，则还可以接收不同格式的响应。 例如 `Accept: application/json` 或 `Accept: application/xml`：

```json
Accept: application/json
{
  "device_code": "3584d83530557fdd1f46af8289938c8ef79f9dc5",
  "user_code": "WDJB-MJHT",
  "verification_uri": "http(s)://HOSTNAME/login/device",
  "expires_in": 900,
  "interval": 5
}
```

```xml
Accept: application/xml
<OAuth>
  <device_code>3584d83530557fdd1f46af8289938c8ef79f9dc5</device_code>
  <user_code>WDJB-MJHT</user_code>
  <verification_uri>http(s)://HOSTNAME/login/device</verification_uri>
  <expires_in>900</expires_in>
  <interval>5</interval>
</OAuth>
```

### 第 2 步：提示用户在浏览器中输入用户代码

你的设备将显示用户验证码，并提示用户输入代码  `http(s)://HOSTNAME/login/device`。

### 步骤 3：应用程序通过轮询 GitHub 来检查用户是否已授权设备。

```
POST http(s)://HOSTNAME/login/oauth/access_token
```

应用将发出轮询 `POST http(s)://HOSTNAME/login/oauth/access_token` 的设备授权请求，直到设备和用户代码过期，或者用户已使用有效的用户代码成功授权应用。 应用必须使用在步骤 1 中检索到的最短轮询 `interval`，以免出现速率限制错误。 有关详细信息，请参阅[设备流的速率限制](#rate-limits-for-the-device-flow)。

用户必须在 15 分钟（或 900 秒内）内输入有效代码。 15 分钟后，需要使用 `POST http(s)://HOSTNAME/login/device/code` 请求新的设备授权代码。

一旦用户授权， 应用程序将收到一个访问令牌，该令牌可用于代表用户向 API 发出请求。

终结点采用以下输入参数。

| 参数名称                                                                         | 类型       | 说明 |
| ---------------------------------------------------------------------------- | -------- | -- |
| `client_id`                                                                  | `string` |    |
| **必填。** 从 GitHub 中 OAuth app收到的客户端 ID。                                       |          |    |
| `device_code`                                                                | `string` |    |
| **必填。** 你从 `device_code` 请求中收到的 `POST http(s)://HOSTNAME/login/device/code`。 |          |    |
| `grant_type`                                                                 | `string` |    |
| **必填。** 授权类型必须是 `urn:ietf:params:oauth:grant-type:device_code`。              |          |    |

默认情况下，响应采用以下形式：

```shell
access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a&token_type=bearer&scope=repo%2Cgist
```

如果在 `Accept` 标头中提供格式，则还可以接收不同格式的响应。 例如 `Accept: application/json` 或 `Accept: application/xml`：

```json
Accept: application/json
{
 "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
  "token_type": "bearer",
  "scope": "repo,gist"
}
```

```xml
Accept: application/xml
<OAuth>
  <access_token>gho_16C7e42F292c6912E7710c838347Ae178B4a</access_token>
  <token_type>bearer</token_type>
  <scope>gist,repo</scope>
</OAuth>
```

### 设备流的速率限制

当用户在浏览器上提交验证码时，每个应用程序在一个小时内的提交速率限制为 50 个。

如果在请求之间所需的最小时间范围（即 `POST http(s)://HOSTNAME/login/oauth/access_token`）内发出多个访问令牌请求 (`interval`)，你将达到速率限制，并收到 `slow_down` 错误响应。
`slow_down` 错误响应向上一个 `interval` 添加 5 秒钟的时间。 有关详细信息，请参阅[设备流的错误代码](#error-codes-for-the-device-flow)。

### 设备流的错误代码

| 错误代码                           | 说明                                                                                                                                                                                                                                |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `authorization_pending`        | 授权请求待处理并且用户尚未输入用户代码时，将发生此错误。 应用应在不超出 `POST http(s)://HOSTNAME/login/oauth/access_token` 的情况下继续轮询 `interval` 请求，这需要每个请求之间的最短秒数。                                                                                                    |
| `slow_down`                    | 收到 `slow_down` 错误时，会使用 `interval` 向请求之间所需的最短 `POST http(s)://HOSTNAME/login/oauth/access_token` 或时间范围添加 5 秒钟的额外时间。 例如，如果请求之间的启动间隔至少需要 5 秒，并且你收到了 `slow_down` 错误响应，那么现在必须等待至少 10 秒，然后才能发出新的 OAuth 访问令牌请求。 错误响应包括必须使用的新 `interval`。 |
| `expired_token`                | 如果设备代码已过期，则将看到 `token_expired` 错误。 您必须发出新的设备代码请求。                                                                                                                                                                                 |
| `unsupported_grant_type`       | 轮询 OAuth 令牌请求 `urn:ietf:params:oauth:grant-type:device_code` 时，授权类型必须为 `POST http(s)://HOSTNAME/login/oauth/access_token` 并且必须作为输入参数包含在内。                                                                                         |
| `incorrect_client_credentials` | 对于设备流，您必须传递应用程序的客户端 ID，该 ID 可以在应用程序的设置页面上找到。 设备流不需要 `client_secret`。                                                                                                                                                              |
| `incorrect_device_code`        | 提供的 device\_code 无效。                                                                                                                                                                                                              |
| `access_denied`                | 当用户在授权过程中单击取消时，你将收到 `access_denied` 错误，该用户将无法再次使用验证码。                                                                                                                                                                             |
| `device_flow_disabled`         | 尚未在应用的设置中启用设备流。 有关详细信息，请参阅[设备流](#device-flow)。                                                                                                                                                                                    |

有关详细信息，请参阅 [OAuth 2.0 设备授权](https://tools.ietf.org/html/rfc8628#section-3.5)。

## 非 Web 应用程序流程

非 web 身份验证适用于测试等有限的情况。 如果需要，您可以使用 [基本身份验证](/zh/enterprise-server@3.22/rest/authentication/authenticating-to-the-rest-api#using-basic-authentication)，通过您的 personal access token 创建 [](/zh/enterprise-server@3.22/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)。 此方法支持用户随时撤销访问权限。

## 重定向 URL

`redirect_uri` 参数是可选的。 如果省略，GitHub 会将用户重定向到在 OAuth app 中配置的回调 URL
设置。 如果提供该参数，重定向 URL 的主机（不含子域）和端口必须与回叫 URL 完全匹配。 重定向 URL 的路径必须指向回调 URL 的子目录。

```
CALLBACK: http://example.com/path

GOOD: http://example.com/path
GOOD: http://example.com/path/subdir/other
GOOD: http://oauth.example.com/path
GOOD: http://oauth.example.com/path/subdir/other
BAD:  http://example.com/bar
BAD:  http://example.com/
BAD:  http://example.com:8080/path
BAD:  http://oauth.example.com:8080/path
BAD:  http://example.org
```

### 环回重定向网址

可选的 `redirect_uri` 参数还可用于环回 URL，这对于在台式计算机上运行的本机应用程序非常实用。 如果应用程序指定了环回 URL 和端口，那么在授权应用程序后，用户将被重定向到提供的 URL 和端口。
`redirect_uri` 不需要与应用的回叫 URL 中指定的端口匹配。

对于 `http://127.0.0.1/path` 回调 URL，如果应用程序正在端口 `redirect_uri` 上进行侦听，则可以使用此 `1234`：

```http
http://127.0.0.1:1234/path
```

请注意，OAuth RFC [建议不要使用 `localhost`](https://datatracker.ietf.org/doc/html/rfc8252#section-7.3)，而是使用环回文本 `127.0.0.1` 或 IPv6 `::1`。

## 创建多个 OAuth apps 令牌

您可以为用户/应用程序/作用域组合创建多个令牌，以便为特定用例创建令牌。

如果你的OAuth app支持一种使用 GitHub 登录且只需要基本用户信息的工作流，这会很有用。 另一个工作流程可能需要访问用户的私有仓库。 通过使用多个令牌，您的 OAuth app 可以针对每个用例执行 Web 流程，并且只请求所需的作用域。 如果用户仅使用你的应用程序进行登录，就绝不会被要求授予你的 OAuth app 对其私有仓库的访问权限。

每个用户/应用程序/作用域组合签发的令牌数量有限，速率限制是每小时创建十个令牌。 如果应用程序为同一用户和同一作用域创建 10 个以上的令牌， GitHub 则撤消具有相同用户/应用程序/范围组合的现有令牌之一，顺序如下：

1. 从未使用过的最早令牌。
2. 如果使用了每个令牌，则表示最近使用最少的令牌。

达到每小时速率限制不会撤销最早的令牌。 相反，它会在浏览器中触发重新授权提示，要求用户仔细检查他们向你的应用授予的权限。 此提示旨在中断应用陷入的任何潜在的无限循环，因为应用几乎没有理由在一小时内向用户请求十个令牌。

> \[!WARNING]
> 从 OAuth app 撤销所有权限将会删除应用程序代表用户生成的所有 SSH 密钥，包括[部署密钥](/zh/enterprise-server@3.22/authentication/connecting-to-github-with-ssh/managing-deploy-keys#deploy-keys)。

## 指示用户审查其访问权限

可以链接到授权 OAuth app 信息，以便用户可以查看和撤销其应用程序授权。

若要构建此链接，您需要提供您的 OAuth app 的 `client_id`，这是您在注册该应用程序时从 GitHub 收到的。

```http
http(s)://HOSTNAME/settings/connections/applications/:client_id
```

> \[!TIP]
> 要详细了解您的 OAuth app 可为用户访问的资源，请参阅 [为用户发现资源](/zh/enterprise-server@3.22/rest/guides/discovering-resources-for-a-user)。

## 故障排除

* [排查授权请求错误](/zh/enterprise-server@3.22/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-authorization-request-errors)
* [排查 OAuth 应用访问令牌请求错误](/zh/enterprise-server@3.22/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-oauth-app-access-token-request-errors)
* [设备流错误](#error-codes-for-the-device-flow)
* [令牌过期和吊销](/zh/enterprise-server@3.22/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation)

## 其他阅读材料

* [关于 GitHub 的身份验证](/zh/enterprise-server@3.22/authentication/keeping-your-account-and-data-secure/about-authentication-to-github)