- 跨域资源共享
- 支持的 OAuth 2.0 流程
- 使用
access token
访问 API - 使用
access_token
访问 Git over HTTPS -
获取
access_token
的详情 - 销毁一个
access_token
- OAuth 2.0 令牌在极狐GitLab 容器仓库和包仓库上的支持
OAuth 2.0 身份认证 API (BASIC ALL)
极狐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 令牌: