- 跨域资源共享
- 支持的 OAuth 2.0 流程
- 使用
access token
访问 API - 使用
access_token
访问 Git over HTTPS -
获取
access_token
的详情 - 销毁一个
access_token
- OAuth 2.0 令牌在极狐GitLab 容器仓库和包仓库上的支持
OAuth 2.0 身份认证 API
极狐GitLab 提供 API 以允许第三方服务代理用户访问极狐GitLab 资源,该过程使用 OAuth 2.0 协议。
要为此配置极狐GitLab,请参阅将极狐GitLab 配置为 OAuth 2.0 身份验证提供者。
此功能基于 doorkeeper Ruby gem。
跨域资源共享
CORS 预检请求支持引入于极狐GitLab 15.1。
许多 /oauth
端点支持跨域资源共享 (CORS)。从极狐GitLab 15.1 开始,以下端点也支持 CORS 预检请求:
/oauth/撤销
/oauth/token
/oauth/userinfo
只有某些 header 可用于预检请求:
- 为简单请求列出的 header。
-
Authorization
header。
例如,X-Requested-With
header 不能用于预检请求。
支持的 OAuth 2.0 流程
极狐GitLab 支持以下授权流程:
- 带有 Proof Key for Code Exchange (PKCE) 的授权码: 最安全,推荐用于客户端和服务器应用程序。如果没有 PKCE,您必须在客户端上保存密钥信息。
- 授权码: 安全而常见的流程。推荐用于部署于安全环境的服务器端应用程序。
- 资源所有者密码证书: 仅用于安全托管的第一方服务。建议不要使用此流程。
不支持 Device Authorization Grant。
OAuth 2.1 的规范草案特别省略了隐式授权和资源所有者密码凭证流。
请参阅 OAuth RFC 了解所有这些流程如何工作并为您的用例选择合适的流程。
授权码(带或不带 PKCE)流程需要首先在您用户账户中的 /profile/applications
页面注册应用。
在注册过程中,通过设定适当权限范围,您可以限制应用可以访问的资源。
创建后,您将获得应用凭证:Application ID 和 Client Secret。
Client Secret 必须安全存放。如果您的程序允许,Application ID 也应该视为秘密。
参阅将极狐GitLab 配置为 OAuth 2.0 身份验证提供者可查看可用的权限范围列表。
防御 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 规范 包括了详细的流程描述,从授权请求到访问令牌。以下步骤描述了我们的流程实现。
带有PCKE授权码(下文记为 PKCE),可以在不依赖 Client Secret 的情况下,安全地在不受信任的客户端上进行 OAuth 访问凭据交换以获取访问令牌。这使得 PKCE 特别适合用于 JavaScript 单页应用,或者在客户端上难以安全保存凭据的其他场景。
在开始流程之前,生成STATE
、CODE_VERIFIER
和CODE_CHALLENGE
。
-
STATE
是一个无法预测的值,客户端使用它来维护请求和回调之间的状态。它也应该用作 CSRF 令牌。 -
CODE_VERIFIER
是一个随机字符串,长度在 43 到 128 个字符之间,可用字符为A-Z
、a-z
、0-9
、-
、.
、_
和~
。 -
CODE_CHALLENGE
是一个 URL 安全的 Base64 编码的CODE_VERIFIER
SHA256 哈希。- SHA256 哈希在编码之前必须是二进制格式。
- 在 Ruby 中,您可以使用
Base64.urlsafe_encode64(Digest::SHA256.digest(CODE_VERIFIER), padding: false)
进行设置。 - 作为参考,
CODE_VERIFIER
字符串ks02i3jdikdo2k0dkfodf3m39rjfjsdk0wk349rj3jrhf
对应的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
此页面要求用户批准应用程序访问他们的账户,访问的资源范围由
REQUESTED_SCOPES
指定。 用户在同意后会被重定向回REDIRECT_URI
。资源范围参数是空格分隔的列表。 例如,scope=read_user+profile
请求read_user
和profile
资源范围。 重定向时,URL 中会包括授权code
,形如:https://example.com/oauth/redirect?code=1234567890&state=STATE
-
使用上一步请求返回的授权
code
(以下记为RETURNED_CODE
),您可以请求使用任何 HTTP 客户端向极狐GitLab 请求一个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 }
-
使用
refresh_token
以获取新的access_token
,refresh_token
在access_token
过期后仍然有效。 下列的请求会做两件事:- 作废当前的
access_token
和refresh_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 }
- 作废当前的
access_token
时的 redirect_uri
必须与授权时提供的 redirect_uri
匹配。您现在可以使用 access_token
请求 API 了。
应用授权码流程
授权码流程与 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
此页面要求用户批准应用程序访问他们的账户,访问的资源范围由
REQUESTED_SCOPES
指定。 用户在同意后会被重定向回REDIRECT_URI
。资源范围参数是空格分隔的列表。 例如,scope=read_user+profile
请求read_user
和profile
资源范围。 重定向时,URL 中会包括授权code
,形如:https://example.com/oauth/redirect?code=1234567890&state=STATE
-
使用上一步请求返回的授权
code
(以下记为RETURNED_CODE
),您可以请求使用任何 HTTP 客户端向极狐GitLab 请求一个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 }
-
使用
refresh_token
以获取新的access_token
,refresh_token
在access_token
过期后仍然有效。 下列的请求会做两件事:- 作废当前的
access_token
和refresh_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 }
- 作废当前的
access_token
时的 redirect_uri
必须与授权时提供的 redirect_uri
匹配。您现在可以使用 access_token
请求 API 了。
用户密码流程
在该流程中,用户需要提供用户名和密码以获取 access_token
。
本流程仅应在以下情况下使用:
- 用户和客户端之间有着高度的信任,例如,客户端是操作系统的一部分,或者是一个具有特权的应用程序。
- 其他授权流程不可用,例如应用授权码。
尽管本授权流程需要直接使用用户的用户名和密码,前述凭据只在一开始申请 access_token
时使用一次。
有了 access_token
和 refresh_token
后,客户端就可以使用它们来进行资源访问而无需存储用户凭据。
要申请 access_token
,你需要发起一个到 /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 Basic Authentication 的参数:
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
}
默认情况下,access_token
的权限范围是 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
访问 API
access token
能让您以用户的身份调用 API,您可以将其置于 GET 参数中:
GET https://gitlab.example.com/api/v4/user?access_token=OAUTH-TOKEN
也可以置于 Authorization header 中:
curl --header "Authorization: Bearer OAUTH-TOKEN" "https://gitlab.example.com/api/v4/user"
使用 access_token
访问 Git over HTTPS
具有 read_repository
或 write_repository
权限范围 的 access token
可以作为密钥访问 Git over HTTPS,此时应当使用 oauth2
作为用户名,不使用用户自己的用户名:
https://oauth2:<your_access_token>@gitlab.example.com/project_path/project_name.git
获取 access_token
的详情
您可使用 /oauth/token/info
API 获取 access_token
的详情。该功能由 Doorkeeper gem 提供。
调用接口时需要提供 access_token
:
-
作为 URL 参数:
GET https://gitlab.example.com/oauth/token/info?access_token=<OAUTH-TOKEN>
-
置于 Authorization header 中:
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
字段分别是 scope
和 expires_in
的别名。
这么做是为了回避 doorkeeper 5.0.2 引入的向前不兼容。
不要依赖 scopes
和 expires_in_seconds
,它们会在未来版本中移除。
销毁一个 access_token
您可使用 /oauth/revoke
API 销毁 access_token
。这个 API 会在成功时返回 200 状态码和一个空的 JSON 对象。
parameters = 'client_id=APP_ID&client_secret=APP_SECRET&token=TOKEN'
RestClient.post 'https://gitlab.example.com/oauth/revoke', parameters
OAuth 2.0 令牌在极狐GitLab 容器仓库和包仓库上的支持
极狐GitLab 容器仓库和包仓库目前仅部分支持 OAuth 2.0 令牌: