diff options
author | Steve Abrams <sabrams@gitlab.com> | 2019-07-22 11:50:25 +0300 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2019-07-22 11:50:25 +0300 |
commit | aba93fe2d5661cf3c086f65838db2965c746fdbf (patch) | |
tree | be934a757b6b0a64391de1d572f54e16f6ffc82e | |
parent | 30a0d4600e46af1b01f90332679f64c432219d5a (diff) |
OAuth2 support for GitLab personal access tokens
PATs are accepted using the OAuth2 compliant header
"Authorization: Bearer {token}" in order to allow for
OAuth requests while 2FA is enabled.
-rw-r--r-- | app/models/personal_access_token.rb | 1 | ||||
-rw-r--r-- | changelogs/unreleased/63438-oauth2-support-with-gitlab-personal-access-token.yml | 5 | ||||
-rw-r--r-- | doc/api/README.md | 6 | ||||
-rw-r--r-- | doc/user/project/packages/npm_registry.md | 23 | ||||
-rw-r--r-- | lib/gitlab/auth/user_auth_finders.rb | 17 | ||||
-rw-r--r-- | spec/lib/gitlab/auth/user_auth_finders_spec.rb | 14 |
6 files changed, 50 insertions, 16 deletions
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index f69f0e2dccb..7ae431eaad7 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -7,6 +7,7 @@ class PersonalAccessToken < ApplicationRecord add_authentication_token_field :token, digest: true REDIS_EXPIRY_TIME = 3.minutes + TOKEN_LENGTH = 20 serialize :scopes, Array # rubocop:disable Cop/ActiveRecordSerialize diff --git a/changelogs/unreleased/63438-oauth2-support-with-gitlab-personal-access-token.yml b/changelogs/unreleased/63438-oauth2-support-with-gitlab-personal-access-token.yml new file mode 100644 index 00000000000..815010e15ae --- /dev/null +++ b/changelogs/unreleased/63438-oauth2-support-with-gitlab-personal-access-token.yml @@ -0,0 +1,5 @@ +--- +title: Personal access tokens are accepted using OAuth2 header format +merge_request: 30277 +author: +type: added diff --git a/doc/api/README.md b/doc/api/README.md index 8e60d1c61df..f9cc1a09870 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -272,6 +272,12 @@ Example of using the personal access token in a header: curl --header "Private-Token: <your_access_token>" https://gitlab.example.com/api/v4/projects ``` +You can also use personal access tokens with OAuth-compliant headers: + +```shell +curl --header "Authorization: Bearer <your_access_token>" https://gitlab.example.com/api/v4/projects +``` + Read more about [personal access tokens][pat]. ### Session cookie diff --git a/doc/user/project/packages/npm_registry.md b/doc/user/project/packages/npm_registry.md index 481b1ce0337..ca0aa9965ef 100644 --- a/doc/user/project/packages/npm_registry.md +++ b/doc/user/project/packages/npm_registry.md @@ -49,35 +49,32 @@ Registry. ## Authenticating to the GitLab NPM Registry If a project is private or you want to upload an NPM package to GitLab, -credentials will need to be provided for authentication. Support is available -only for [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow). +credentials will need to be provided for authentication. Support is available for [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow) or [personal access tokens](../../profile/personal_access_tokens.md). -CAUTION: **2FA not supported:** -Authentication for personal access tokens is not yet supported -([#9140](https://gitlab.com/gitlab-org/gitlab-ee/issues/9140)). If you have 2FA -enabled, you won't be able to authenticate to the GitLab NPM Registry. +CAUTION: **2FA is only supported with personal access tokens:** +If you have 2FA enabled, you need to use a [personal access token](../../profile/personal_access_tokens.md) with OAuth headers. Standard OAuth tokens won't be able to authenticate to the GitLab NPM Registry. ### Authenticating with an OAuth token -To authenticate with an [OAuth token](../../../api/oauth2.md#resource-owner-password-credentials-flow), -add a corresponding section to your `.npmrc` file: +To authenticate with an [OAuth token](../../../api/oauth2.md#resource-owner-password-credentials-flow) +or [personal access token](../../profile/personal_access_tokens.md), add a corresponding section to your `.npmrc` file: ```ini ; Set URL for your scoped packages. ; For example package with name `@foo/bar` will use this URL for download @foo:registry=https://gitlab.com/api/v4/packages/npm/ -; Add the OAuth token for the scoped packages URL. This will allow you to download +; Add the token for the scoped packages URL. This will allow you to download ; `@foo/` packages from private projects. -//gitlab.com/api/v4/packages/npm/:_authToken=<your_oauth_token> +//gitlab.com/api/v4/packages/npm/:_authToken=<your_token> -; Add OAuth token for uploading to the registry. Replace <your_project_id> +; Add token for uploading to the registry. Replace <your_project_id> ; with the project you want your package to be uploaded to. -//gitlab.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=<your_oauth_token> +//gitlab.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=<your_token> ``` Replace `<your_project_id>` with your project ID which can be found on the home page -of your project and `<your_oauth_token>` with your OAuth token. +of your project and `<your_token>` with your OAuth or personal access token. If you have a self-hosted GitLab installation, replace `gitlab.com` with your domain name. diff --git a/lib/gitlab/auth/user_auth_finders.rb b/lib/gitlab/auth/user_auth_finders.rb index a5efe33bdc6..bba7e2cbb3c 100644 --- a/lib/gitlab/auth/user_auth_finders.rb +++ b/lib/gitlab/auth/user_auth_finders.rb @@ -90,8 +90,8 @@ module Gitlab def find_personal_access_token token = current_request.params[PRIVATE_TOKEN_PARAM].presence || - current_request.env[PRIVATE_TOKEN_HEADER].presence - + current_request.env[PRIVATE_TOKEN_HEADER].presence || + parsed_oauth_token return unless token # Expiration, revocation and scopes are verified in `validate_access_token!` @@ -99,9 +99,12 @@ module Gitlab end def find_oauth_access_token - token = Doorkeeper::OAuth::Token.from_request(current_request, *Doorkeeper.configuration.access_token_methods) + token = parsed_oauth_token return unless token + # PATs with OAuth headers are not handled by OauthAccessToken + return if matches_personal_access_token_length?(token) + # Expiration, revocation and scopes are verified in `validate_access_token!` oauth_token = OauthAccessToken.by_token(token) raise UnauthorizedError unless oauth_token @@ -110,6 +113,14 @@ module Gitlab oauth_token end + def parsed_oauth_token + Doorkeeper::OAuth::Token.from_request(current_request, *Doorkeeper.configuration.access_token_methods) + end + + def matches_personal_access_token_length?(token) + token.length == PersonalAccessToken::TOKEN_LENGTH + end + # Check if the request is GET/HEAD, or if CSRF token is valid. def verified_request? Gitlab::RequestForgeryProtection.verified?(current_request.env) diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/user_auth_finders_spec.rb index 1e2aebdc84b..4751f880cee 100644 --- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/user_auth_finders_spec.rb @@ -138,6 +138,20 @@ describe Gitlab::Auth::UserAuthFinders do expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError) end end + + context 'with OAuth headers' do + it 'returns user' do + env['HTTP_AUTHORIZATION'] = "Bearer #{personal_access_token.token}" + + expect(find_user_from_access_token).to eq user + end + + it 'returns exception if invalid personal_access_token' do + env['HTTP_AUTHORIZATION'] = 'Bearer invalid_20byte_token' + + expect { find_personal_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError) + end + end end describe '#find_user_from_web_access_token' do |