OAuth 2.0 身份提供者 API
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
使用此 API 可通过 OAuth 2.0 协议允许第三方服务代表用户访问 GitLab 资源。 更多信息,请参阅将 GitLab 配置为 OAuth 2.0 认证身份提供者。
此功能基于 doorkeeper Ruby gem。
跨源资源共享
许多 /oauth 端点支持跨源资源共享 (CORS)。从 GitLab 15.1 开始,以下端点也支持 CORS 预检请求:
/oauth/revoke/oauth/token/oauth/userinfo
只有某些标头可用于预检请求:
- 简单请求中列出的标头。
Authorization标头。
例如,X-Requested-With 标头不能用于预检请求。
支持的 OAuth 2.0 流程
GitLab 支持以下授权流程:
- 带代码交换证明密钥 (PKCE)的授权代码: 最安全。没有 PKCE,您必须在移动客户端上包含客户端密钥,建议用于客户端和服务器应用程序。
- 授权代码:安全且常见的流程。安全服务器端应用程序的推荐选项。
- 资源所有者密码凭据:仅用于安全托管的第一方服务。GitLab 建议不要使用此流程。
- 设备授权授予(GitLab 17.1 及更高版本)面向无浏览器访问设备的安全流程。需要辅助设备完成授权流程。
OAuth 2.1 的草案规范特别省略了隐式授予和资源所有者密码凭据流程。
请参阅 OAuth RFC 了解所有这些流程的工作原理,并为您的用例选择合适的流程。
授权代码流程(带或不带 PKCE)需要先通过用户账户中的 /user_settings/applications 页面注册 application。注册期间,通过启用适当的范围,您可以限制 application 可以访问的资源范围。创建后,您将获得 application 凭据:应用程序 ID 和 客户端密钥。客户端密钥必须保持安全。当您的应用程序架构允许时,保持 应用程序 ID 机密也是有利的。
有关 GitLab 中的范围列表,请参阅提供者文档。
防止 CSRF 攻击
为了保护基于重定向的流程,
OAuth 规范建议使用"一次性 CSRF 令牌,在状态参数中携带,与用户代理安全绑定",每次请求
/oauth/authorize 端点时使用。这可以防止
CSRF 攻击。
在生产环境中使用 HTTPS
在生产环境中,为您的 redirect_uri 使用 HTTPS。
在开发环境中,GitLab 允许不安全的 HTTP 重定向 URI。
由于 OAuth 2.0 的安全性完全基于传输层,您不应使用不受保护的 URI。更多信息,请参阅 OAuth 2.0 RFC 和 OAuth 2.0 威胁模型 RFC。
在以下部分中,您可以找到有关如何使用每种流程获取授权的详细说明。
带代码交换证明密钥 (PKCE) 的授权代码
PKCE RFC 包含从授权请求到访问令牌的 详细流程描述。以下步骤描述了我们对流程的实现。
带 PKCE 的授权代码流程,简称 PKCE,使得在公共客户端上安全地执行 客户端凭据与访问令牌的 OAuth 交换成为可能,而完全不需要访问 客户端密钥。这使得 PKCE 流程对于单页 JavaScript 应用程序或其他客户端应用程序非常有利, 在这些应用程序中,对用户保密在技术上是不可能的。
在开始流程之前,生成 STATE、CODE_VERIFIER 和 CODE_CHALLENGE。
STATE是一个不可预测的值,由客户端用于在请求和回调之间保持状态。 它也应该用作 CSRF 令牌。CODE_VERIFIER是一个随机字符串,长度在 43 到 128 个字符之间, 使用字符A-Z、a-z、0-9、-、.、_和~。CODE_CHALLENGE是CODE_VERIFIER的 SHA256 哈希的 URL 安全 base64 编码字符串:- SHA256 哈希在编码前必须是二进制格式。
- 在 Ruby 中,您可以使用
Base64.urlsafe_encode64(Digest::SHA256.digest(CODE_VERIFIER), padding: false)进行设置。 - 作为参考,当
CODE_VERIFIER字符串ks02i3jdikdo2k0dkfodf3m39rjfjsdk0wk349rj3jrhf被哈希 并使用前面的 Ruby 代码段编码时,会产生CODE_CHALLENGE字符串2i0WFA-0AerkjQm4X4oDEhqA17QIAKNjXpagHBXmO_U。
-
请求授权代码。为此,您应该将用户重定向到
/oauth/authorize页面,并带有以下查询参数:https://gitlab.example.com/oauth/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&response_type=code&state=STATE&scope=REQUESTED_SCOPES&code_challenge=CODE_CHALLENGE&code_challenge_method=S256&root_namespace_id=ROOT_NAMESPACE_ID此页面要求用户批准应用程序根据
REQUESTED_SCOPES中指定的范围访问其账户的请求。 然后用户被重定向回指定的REDIRECT_URI。scope 参数 是与用户关联的范围的空格分隔列表。 例如,scope=read_user+profile请求read_user和profile范围。root_namespace_id是与项目关联的根命名空间 ID。当为关联的组配置了 SAML SSO 时,应使用此可选参数。 重定向包括授权code,例如:https://example.com/oauth/redirect?code=1234567890&state=STATE -
使用上一个请求返回的授权
code(在以下示例中表示为RETURNED_CODE),您可以使用任何 HTTP 客户端请求access_token。 以下示例使用 Ruby 的rest-client:parameters = 'client_id=APP_ID&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI&code_verifier=CODE_VERIFIER' RestClient.post 'https://gitlab.example.com/oauth/token', parameters示例响应:
{ "access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54", "token_type": "bearer", "expires_in": 7200, "refresh_token": "8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1", "created_at": 1607635748 } -
要检索新的
access_token,请使用refresh_token参数。刷新令牌可以在access_token本身过期后使用。此请求:- 使现有的
access_token和refresh_token无效。 - 在响应中发送新令牌。
parameters = 'client_id=APP_ID&refresh_token=REFRESH_TOKEN&grant_type=refresh_token&redirect_uri=REDIRECT_URI&code_verifier=CODE_VERIFIER' RestClient.post 'https://gitlab.example.com/oauth/token', parameters示例响应:
{ "access_token": "c97d1fe52119f38c7f67f0a14db68d60caa35ddc86fd12401718b649dcfa9c68", "token_type": "bearer", "expires_in": 7200, "refresh_token": "803c1fd487fec35562c205dac93e9d8e08f9d3652a24079d704df3039df1158f", "created_at": 1628711391 } - 使现有的
redirect_uri 必须与原始授权请求中使用的 redirect_uri 匹配。
现在您可以使用访问令牌向 API 发出请求。
授权代码流程
请查看 RFC 规范 获取详细的流程描述。
授权代码流程本质上与 带 PKCE 的授权代码流程相同,
在开始流程之前,生成 STATE。它是一个不可预测的值,
由客户端用于在请求和回调之间保持状态。它也应该
用作 CSRF 令牌。
-
请求授权代码。为此,您应该将用户重定向到
/oauth/authorize页面,并带有以下查询参数:https://gitlab.example.com/oauth/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&response_type=code&state=STATE&scope=REQUESTED_SCOPES&root_namespace_id=ROOT_NAMESPACE_ID此页面要求用户批准应用程序根据
REQUESTED_SCOPES中指定的范围访问其账户的请求。 然后用户被重定向回指定的REDIRECT_URI。scope 参数 是与用户关联的范围的空格分隔列表。 例如,scope=read_user+profile请求read_user和profile范围。root_namespace_id是与项目关联的根命名空间 ID。当为关联的组配置了 SAML SSO 时,应使用此可选参数。 重定向包括授权code,例如:https://example.com/oauth/redirect?code=1234567890&state=STATE -
使用上一个请求返回的授权
code(在以下示例中显示为RETURNED_CODE),您可以使用任何 HTTP 客户端请求access_token。 以下示例使用 Ruby 的rest-client:parameters = 'client_id=APP_ID&client_secret=APP_SECRET&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI' RestClient.post 'https://gitlab.example.com/oauth/token', parameters示例响应:
{ "access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54", "token_type": "bearer", "expires_in": 7200, "refresh_token": "8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1", "created_at": 1607635748 } -
要检索新的
access_token,请使用refresh_token参数。刷新令牌可以在access_token本身过期后使用。此请求:- 使现有的
access_token和refresh_token无效。 - 在响应中发送新令牌。
parameters = 'client_id=APP_ID&client_secret=APP_SECRET&refresh_token=REFRESH_TOKEN&grant_type=refresh_token&redirect_uri=REDIRECT_URI' RestClient.post 'https://gitlab.example.com/oauth/token', parameters示例响应:
{ "access_token": "c97d1fe52119f38c7f67f0a14db68d60caa35ddc86fd12401718b649dcfa9c68", "token_type": "bearer", "expires_in": 7200, "refresh_token": "803c1fd487fec35562c205dac93e9d8e08f9d3652a24079d704df3039df1158f", "created_at": 1628711391 } - 使现有的
redirect_uri 必须与原始授权请求中使用的 redirect_uri 匹配。
现在您可以使用返回的访问令牌向 API 发出请求。
设备授权授予流程
请查看 RFC 规范 获取设备授权授予流程的详细描述,从设备授权请求到浏览器登录的令牌响应。
设备授权授予流程使得可以从输入受限设备安全地验证您的 GitLab 身份,在这些设备上浏览器交互不是一种选择。
这使得设备授权授予流程对于尝试从无头服务器或其他没有或有限 UI 的设备使用 GitLab 服务的用户来说非常理想。
-
要请求设备授权,从输入受限的 设备客户端向
https://gitlab.example.com/oauth/authorize_device发送请求。例如:parameters = 'client_id=UID&scope=read' RestClient.post 'https://gitlab.example.com/oauth/authorize_device', parameters成功请求后,向用户返回包含
verification_uri的响应。例如:{ "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", "user_code": "0A44L90H", "verification_uri": "https://gitlab.example.com/oauth/device", "verification_uri_complete": "https://gitlab.example.com/oauth/device?user_code=0A44L90H", "expires_in": 300, "interval": 5 } -
设备客户端向请求用户显示响应中的
user_code和verification_uri。 然后,该用户在具有浏览器访问权限的辅助设备上:- 转到提供的 URI。
- 输入用户代码。
- 根据提示完成身份验证。
-
在显示
verification_uri和user_code后,设备客户端 立即开始使用初始响应中返回的关联device_code轮询令牌端点:parameters = 'grant_type=urn:ietf:params:oauth:grant-type:device_code &device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS &client_id=1406020730' RestClient.post 'https://gitlab.example.com/oauth/token', parameters -
设备客户端从令牌端点接收响应。如果授权成功, 则返回成功响应,否则返回错误响应。 潜在的错误响应分为以下任一类:
- OAuth 授权框架访问令牌错误响应定义的那些。
- 此处描述的设备授权授予流程特有的那些。 设备流程特有的那些错误响应在以下内容中描述。 有关每个潜在响应的更多信息,请参阅相关的设备授权授予 RFC 规范和 授权令牌 RFC 规范。
示例响应:
{ "error": "authorization_pending", "error_description": "..." }收到此响应后,设备客户端继续轮询。
如果轮询间隔太短,则返回减速错误响应。例如:
{ "error": "slow_down", "error_description": "..." }收到此响应后,设备客户端降低其轮询速率并以新速率继续轮询。
如果设备代码在身份验证完成之前过期,则返回过期令牌错误 响应。例如:
{ "error": "expired_token", "error_description": "..." }此时,设备客户端应停止并发起新的设备授权请求。
如果授权请求被拒绝,则返回访问被拒绝错误响应。例如:
{ "error": "access_denied", "error_description": "..." }身份验证请求已被拒绝。用户应验证其凭据或联系其系统管理员
-
用户成功身份验证后,返回成功响应:
{ "access_token": "TOKEN", "token_type": "Bearer", "expires_in": 7200, "scope": "read", "created_at": 1593096829 }
此时,设备身份验证流程已完成。返回的 access_token 可以提供给 GitLab,以在访问 GitLab 资源时验证用户身份,例如通过 HTTPS 克隆或访问 API 时。
实现客户端设备流程的示例应用程序可在以下位置找到:https://gitlab.com/johnwparent/git-auth-over-https。
资源所有者密码凭据流程
请查看 RFC 规范 获取详细的流程描述。
确保为 GitLab 实例选择了允许通过 HTTP(S) 进行 Git 的密码身份验证 复选框以支持密码凭据流程。
在此流程中,请求令牌以换取资源所有者凭据 (用户名和密码)。
凭据仅应在以下情况下使用:
- 资源所有者和客户端之间存在高度信任。例如, 客户端是设备操作系统的一部分或高度特权的应用程序。
- 其他授权授予类型不可用(例如授权代码)。
切勿存储用户的凭据,并且仅在您的客户端部署到受信任的环境时使用此授予类型, 在 99% 的情况下,个人访问令牌 是更好的选择。
尽管此授予类型需要客户端直接访问资源所有者凭据, 但资源所有者凭据仅用于单个请求,并交换为访问令牌。 通过将凭据与长期访问令牌或刷新令牌交换,此授予类型可以消除客户端存储资源所有者凭据以供将来使用的需要。
要请求访问令牌,您必须向 /oauth/token 发出 POST 请求,
并带有以下参数:
{
"grant_type" : "password",
"username" : "user@example.com",
"password" : "secret"
}示例 cURL 请求:
echo 'grant_type=password&username=<your_username>&password=<your_password>' > auth.txt
curl --data "@auth.txt" --request POST "https://gitlab.example.com/oauth/token"您也可以使用注册的 OAuth 应用程序使用此授予流程,方法是使用
应用程序的 client_id 和 client_secret 进行 HTTP 基本身份验证:
echo 'grant_type=password&username=<your_username>&password=<your_password>' > auth.txt
curl --data "@auth.txt" --user client_id:client_secret \
--request POST "https://gitlab.example.com/oauth/token"然后,您收到包含访问令牌的响应:
{
"access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
"token_type": "bearer",
"expires_in": 7200
}默认情况下,访问令牌的范围是 api,提供完整的读/写访问权限。
对于测试,您可以使用 oauth2 Ruby gem:
client = OAuth2::Client.new('the_client_id', 'the_client_secret', :site => "https://example.com")
access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token使用 access token 访问 GitLab API
access token 允许您代表用户向 API 发出请求。
您可以将令牌作为 GET 参数传递:
GET https://gitlab.example.com/api/v4/user?access_token=OAUTH-TOKEN或者您可以将令牌放入 Authorization 标头:
curl --header "Authorization: Bearer OAUTH-TOKEN" "https://gitlab.example.com/api/v4/user"使用 access token 通过 HTTPS 访问 Git
具有范围
read_repository 或 write_repository 的令牌可以通过 HTTPS 访问 Git。使用令牌作为密码。
您可以将用户名设置为任何字符串值。您应该使用 oauth2:
https://oauth2:<your_access_token>@gitlab.example.com/project_path/project_name.git或者,您可以使用 Git 凭据助手 通过 OAuth 向 GitLab 进行身份验证。这会自动处理 OAuth 令牌刷新。
检索令牌信息
要验证令牌的详细信息,请使用 Doorkeeper gem 提供的
token/info 端点。更多信息,请参阅
/oauth/token/info。
您必须提供访问令牌,可以通过以下任一方式:
-
作为参数:
GET https://gitlab.example.com/oauth/token/info?access_token=<OAUTH-TOKEN> -
在 Authorization 标头中:
curl --header "Authorization: Bearer <OAUTH-TOKEN>" "https://gitlab.example.com/oauth/token/info"
以下是示例响应:
{
"resource_owner_id": 1,
"scope": ["api"],
"expires_in": null,
"application": {"uid": "1cb242f495280beb4291e64bee2a17f330902e499882fe8e1e2aa875519cab33"},
"created_at": 1575890427
}已弃用的字段
响应中包含 scopes 和 expires_in_seconds 字段,但现在已弃用。scopes 字段是 scope 的别名,expires_in_seconds 字段是 expires_in 的别名。更多信息,请参阅 Doorkeeper API 更改。
撤销令牌
要撤销令牌,请使用 revoke 端点。API 返回 200 响应代码和空的
JSON 哈希以表示成功。
parameters = 'client_id=APP_ID&client_secret=APP_SECRET&token=TOKEN'
RestClient.post 'https://gitlab.example.com/oauth/revoke', parametersOAuth 2.0 令牌和 GitLab 注册表
标准 OAuth 2.0 令牌支持对 GitLab 注册表的不同程度的访问,因为它们:
- 不允许用户身份验证到:
- 允许用户通过 容器注册表 API 获取、列出和删除注册表。
- 允许用户通过 Maven 虚拟注册表 API 获取、列出和删除注册表对象。