Commit 6fb46b60 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'internal_recovery_api' into 'master'

Add internal API to recovery 2FA

## What does this MR do?

Add an internal API to make SSH 2FA recovery possible. Related to gitlab-org/gitlab-shell!74

See merge request !5510
parents 9ea01f32 bba85773
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.12.0 (unreleased) v 8.12.0 (unreleased)
- Add two-factor recovery endpoint to internal API !5510
- Change merge_error column from string to text type - Change merge_error column from string to text type
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel) - Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
- Optimistic locking for Issues and Merge Requests (title and description overriding prevention) - Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
## User documentation ## User documentation
- [Account Security](user/account/security.md) Securing your account via two-factor authentication, etc.
- [API](api/README.md) Automate GitLab via a simple and powerful API. - [API](api/README.md) Automate GitLab via a simple and powerful API.
- [CI/CD](ci/README.md) GitLab Continuous Integration (CI) and Continuous Delivery (CD) getting started, `.gitlab-ci.yml` options, and examples. - [CI/CD](ci/README.md) GitLab Continuous Integration (CI) and Continuous Delivery (CD) getting started, `.gitlab-ci.yml` options, and examples.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab. - [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
......
# Account Security
- [Two-Factor Authentication](two_factor_authentication.md)
# Two-Factor Authentication
## Recovery options
If you lose your code generation device (such as your mobile phone) and you need
to disable two-factor authentication on your account, you have several options.
### Use a saved recovery code
When you enabled two-factor authentication for your account, a series of
recovery codes were generated. If you saved those codes somewhere safe, you
may use one to sign in.
First, enter your username/email and password on the GitLab sign in page. When
prompted for a two-factor code, enter one of the recovery codes you saved
previously.
> **Note:** Once a particular recovery code has been used, it cannot be used again.
You may still use the other saved recovery codes at a later time.
### Generate new recovery codes using SSH
It's not uncommon for users to forget to save the recovery codes when enabling
two-factor authentication. If you have an SSH key added to your GitLab account,
you can generate a new set of recovery codes using SSH.
Run `ssh git@gitlab.example.com 2fa_recovery_codes`. You will be prompted to
confirm that you wish to generate new codes. If you choose to continue, any
previously saved codes will be invalidated.
```bash
$ ssh git@gitlab.example.com 2fa_recovery_codes
Are you sure you want to generate new two-factor recovery codes?
Any existing recovery codes you saved will be invalidated. (yes/no)
yes
Your two-factor authentication recovery codes are:
119135e5a3ebce8e
11f6v2a498810dcd
3924c7ab2089c902
e79a3398bfe4f224
34bd7b74adbc8861
f061691d5107df1a
169bf32a18e63e7f
b510e7422e81c947
20dbed24c5e74663
df9d3b9403b9c9f0
During sign in, use one of the codes above when prompted for
your two-factor code. Then, visit your Profile Settings and add
a new device so you do not lose access to your account again.
```
Next, go to the GitLab sign in page and enter your username/email and password.
When prompted for a two-factor code, enter one of the recovery codes obtained
from the command line output.
> **Note:** After signing in, you should immediately visit your **Profile Settings
-> Account** to set up two-factor authentication with a new device.
### Ask a GitLab administrator to disable two-factor on your account
If the above two methods are not possible, you may ask a GitLab global
administrator to disable two-factor authentication for your account. Please
be aware that this will temporarily leave your account in a less secure state.
You should sign in and re-enable two-factor authentication as soon as possible
after the administrator disables it.
...@@ -101,6 +101,31 @@ module API ...@@ -101,6 +101,31 @@ module API
{} {}
end end
end end
post '/two_factor_recovery_codes' do
status 200
key = Key.find(params[:key_id])
user = key.user
# Make sure this isn't a deploy key
unless key.type.nil?
return { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' }
end
unless user.present?
return { success: false, message: 'Could not find a user for the given key' }
end
unless user.two_factor_enabled?
return { success: false, message: 'Two-factor authentication is not enabled for this user' }
end
codes = user.generate_otp_backup_codes!
user.save!
{ success: true, recovery_codes: codes }
end
end end
end end
end end
...@@ -38,6 +38,68 @@ describe API::API, api: true do ...@@ -38,6 +38,68 @@ describe API::API, api: true do
end end
end end
describe 'GET /internal/two_factor_recovery_codes' do
it 'returns an error message when the key does not exist' do
post api('/internal/two_factor_recovery_codes'),
secret_token: secret_token,
key_id: 12345
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 Not found')
end
it 'returns an error message when the key is a deploy key' do
deploy_key = create(:deploy_key)
post api('/internal/two_factor_recovery_codes'),
secret_token: secret_token,
key_id: deploy_key.id
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Deploy keys cannot be used to retrieve recovery codes')
end
it 'returns an error message when the user does not exist' do
key_without_user = create(:key, user: nil)
post api('/internal/two_factor_recovery_codes'),
secret_token: secret_token,
key_id: key_without_user.id
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Could not find a user for the given key')
expect(json_response['recovery_codes']).to be_nil
end
context 'when two-factor is enabled' do
it 'returns new recovery codes when the user exists' do
allow_any_instance_of(User).to receive(:two_factor_enabled?).and_return(true)
allow_any_instance_of(User)
.to receive(:generate_otp_backup_codes!).and_return(%w(119135e5a3ebce8e 34bd7b74adbc8861))
post api('/internal/two_factor_recovery_codes'),
secret_token: secret_token,
key_id: key.id
expect(json_response['success']).to be_truthy
expect(json_response['recovery_codes']).to match_array(%w(119135e5a3ebce8e 34bd7b74adbc8861))
end
end
context 'when two-factor is not enabled' do
it 'returns an error message' do
allow_any_instance_of(User).to receive(:two_factor_enabled?).and_return(false)
post api('/internal/two_factor_recovery_codes'),
secret_token: secret_token,
key_id: key.id
expect(json_response['success']).to be_falsey
expect(json_response['recovery_codes']).to be_nil
end
end
end
describe "GET /internal/discover" do describe "GET /internal/discover" do
it do it do
get(api("/internal/discover"), key_id: key.id, secret_token: secret_token) get(api("/internal/discover"), key_id: key.id, secret_token: secret_token)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment