Two-factor authentication feature in API part.#1753
Conversation
| def verify_with_otp | ||
| return unless @api_user.mfa_login_and_write? | ||
| otp = request.headers["HTTP_OTP"] || '' | ||
| return if @api_user&.otp_verified?(otp) |
There was a problem hiding this comment.
is this & needed here? verify_authenticated_user ensures that user see 401 if @api_user is nil.
There was a problem hiding this comment.
Right. At first, I'm considering a case that verify_with_otp is put before verify_authenticated_user. But since the method relies on existence of @api_user, it must be put after verify_authenticated_user.
|
This looks good so far 👍 Can we please make some headway with
|
|
Something like this seems reasonable. @sonalkr132 |
Its fine but can be better. Public pens are MIT licensed, if you are going to copy any of them, please make sure it matches our color scheme. |
|
@sonalkr132 |
|
|
||
| def mfa_write_authorized?(otp) | ||
| return true unless mfa_login_and_write? | ||
| otp_verified?(otp || '') |
There was a problem hiding this comment.
please don't do otp || '', either add default argument to mfa_write_authorized? or otp_verified?.
There was a problem hiding this comment.
I think I can replace otp || '' with otp.to_s
| <%= select_tag :level, options_for_select([ | ||
| ['Login and Write', 'mfa_login_and_write'], | ||
| ['Login Only', 'mfa_login_only'], | ||
| ['Disabled', 'no_mfa']], @user.mfa_level), :class => 'form__input form__select' %> |
There was a problem hiding this comment.
Login only and Login and write don't make much sense to me. At least to user, we should show something more appropriate like UI Authentication and UI and API Authentication.
There was a problem hiding this comment.
We should also consider updating function names as well. I am not a stickler for naming it UI and API Authentication but ..login_and_write is just misleading. For example, api request to show key is not a write operation and by login_only we don't mean api login. Distinction has more to do with whether we are checking something for api or ui.
Sorry, I failed to point it out previously.
There was a problem hiding this comment.
The name login and write or login only derives from the original RFC issue about multi-factor authentication. NPM uses these names to identify their different authentication level. These are basic elements in mfa implementation, so I just borrowed them at that time without too much hesitation.
Currently, fetching API key requires OTP only when auth level set to login and write. Truly, these would be confusing to both users and contributors who see this code. Some of other actions not wrapped with mfa now, like API key reset or reset password, but they are also critical. Should they require OTP if UI Authentication enabled? Except that, I think name around UI and API is good. Thanks.
There was a problem hiding this comment.
I did it. Auth for UI Only and Auth for UI and API is okay.
| page.fill_in "otp", with: recoveries.sample | ||
| click_button "Disable" | ||
| page.select "Disabled" | ||
| find('#mfa-edit input[type=submit]').click |
There was a problem hiding this comment.
Please make a function out of above two line.
| should redirect_to('the profile edit page') { edit_profile_path } | ||
| should 'disable mfa' do | ||
| refute @user.reload.mfa_enabled? | ||
| context 'when input recovery code' do |
| none: None | ||
| not_found: Not Found | ||
| please_sign_up: Access Denied. Please sign up for an account at https://rubygems.org | ||
| please_send_correct_otp: You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry. |
There was a problem hiding this comment.
your request doesn't have the correct OTP code.
Won't we be showing this when OPT is missing too? if yes, please update it to say you didn't provide otp or provided otp was incorrect.
In my opinion, asking mfa once during login through UI is good enough. I wish we could do the same for api as well, as pointed out by @colby-swandale
We are asking for otp for every critical cli components because as of now, there is no concept of session when it comes to our api. Unless we implement a way of api authentication where tokens expires after a given time, we can't really do the same for api requests. |
|
In my opinion, asking mfa once during login through UI is good enough.
Yes, but change mfa level still requires otp check. Actually we don’t have concept like _no mfa check after a duration_. In GitHub, if I’m going to delete one of my repo, the site would ask me to re-input my password. If I want to delete others, password is not needed in the short period. We don’t have this feature.
I wish we could do the same for api as well.
I think it’s feasible to use JWT-like approach to implement it - wrap API key and _session duration_ with sign in header. There was talk about this before. I can handle it as further work.
Authentication of API just check header fields from every request. In ideal implementation, UI actions needing mfa can also be wrapped just with something like `before_action :mfa_ui_auth`, similar to how API does. User requests these actions, they will be redirected to a mfa prompt page, enter correct otp, finish actions, otherwise redirected back with a flash message.
… Aditya Prakash ***@***.***> 於 2018年8月12日 上午2:15 寫道:
Some of other actions not wrapped with mfa now, like API key reset or reset password, but they are also critical.
In my opinion, asking mfa once during login through UI is good enough. I wish we could do the same for api as well, as pointed out by @colby-swandale
Can we just ask the user to give their MFA token whenever they sign in to an account which has 2FA?
We are asking for otp for every critical cli components because as of now, there is no concept of session when it comes to our api. Unless we implement a way of api authentication where tokens expires after a given time, we can't really do the same for api requests.
aws cli seem to be using related workflow https://aws.amazon.com/premiumsupport/knowledge-center/authenticate-mfa-cli/
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.
|
|
ref #1725 |
| @@ -0,0 +1,12 @@ | |||
| class Api::V1::MultifactorAuthsController < Api::BaseController | |||
There was a problem hiding this comment.
I don't think we are using this in client any more.
| none: None | ||
| not_found: Not Found | ||
| please_sign_up: Access Denied. Please sign up for an account at https://rubygems.org | ||
| otp_incorrect_or_missing: You have enabled multifactor authentication but your OTP code is missing or incorrect. Please check it and retry. |
There was a problem hiding this comment.
your OTP code is missing or incorrect.
this is unnecessarily ambiguous. Can we please add a check for otp being empty and show error message accordingly?
There was a problem hiding this comment.
Right. But change to this may affect rubygems client, I'll check more of it.
| destroy: | ||
| success: You have successfully disabled multifactor authentication. | ||
| update: | ||
| success: You have successfully updated your auth level. |
There was a problem hiding this comment.
aren't we updating mfa level and not auth level?
| go_settings: Register a new device | ||
| enabled: You have enabled multifactor authentication. Please input your OTP from your authenticator or one of your active recovery codes to disable it. | ||
| disable: Disable | ||
| enabled: You have enabled multifactor authentication. Please input your OTP from your authenticator or one of your active recovery codes to change your auth level or disable it. |
| @user.enable_mfa!(ROTP::Base32.random_base32, :ui_and_api_mfa) | ||
| end | ||
|
|
||
| context "on POST to add other user as gem owner with email without OTP" do |
There was a problem hiding this comment.
please drop with email in all these context.
|
Rebase 💥 please. |
|
One minor change. Looks good to merge now 👍 |
Local output is like this: One thing I need to do: I have to comment the code checking current RubyGems client version. Or I would get
|
If we don't delete |
I remember we talked about it and I checked the chat history. It seems that I misunderstood your meanings. Currently, I need to do multi-factor authentication even seconds after signing in with correct OTP code. Security problem arises when such no-otp interval exists. But now we don't have it. Every execution is independent. Only concern: If fetching newest version of Thank you for notifying me about this. I'll delete first it until we found new approaches for authentication. |
- Now 'OTP' header is needed for gem creation if user has enabled `mfa-login-and-write` option. - Related unit tests are also added.
- Add `before_action` to `create` and `destroy` action of owner's API. - Add respective tests for OTP check when doing owner change. - Remove unnecessary safe navigation operator of @api_user in OTP checking.
- `api/v1/multifactor_auths#show` gives MFA level of current user. - Related tests are added.
- 'Login Only' is now 'Auth for UI Only' - 'Login and Write' is not 'Auth for UI and API'
|
Thanks @ecnelises 🍷 ✨ |
2369: [GSoC] Multi-factor feature for RubyGems. r=hsbt a=ecnelises # Description: Hello. This is my GSoC project, dedicated to add multifactor authentication to both RubyGems command program and Gemcutter ([RubyGems.org](https://rubygems.org)). Work for the Gemcutter part has almost been finished (see [PR #1753](rubygems/rubygems.org#1753), [PR #1729](rubygems/rubygems.org#1729) and a series of [my progress reports](https://ecnelises.github.io/)). ## Content: This PR will contain my changes to RubyGems client, adding multifactor auth for `gem push`, `gem signin` and `gem owner` commands. Since no command for editing profile, adding command for changing multifactor auth settings seems unnecessary. ## Workflow: - User set up multifactor auth well in the site. (into `mfa_login_and_write` level) - When user does the actions requiring MFA, an OTP prompt is shown. Or user can add `--otp` option into command, like `gem push mygem-0.0.0.gem --otp 123456`. - If the OTP is incorrect, operation fails with failure text. ______________ # Tasks: - [x] Add OTP requirement to `push_command`. - [x] Add OTP requirement to `owner_command`. - [x] Add OTP prompt to `sign_in`. - [ ] Support for `yank_command`. - [x] Write related tests. I will abide by the [code of conduct](https://github.com/rubygems/rubygems/blob/master/CODE_OF_CONDUCT.md). Co-authored-by: Qiu Chaofan <fwage73@gmail.com> Co-authored-by: SHIBATA Hiroshi <hsbt@ruby-lang.org>
2369: [GSoC] Multi-factor feature for RubyGems. r=hsbt a=ecnelises # Description: Hello. This is my GSoC project, dedicated to add multifactor authentication to both RubyGems command program and Gemcutter ([RubyGems.org](https://rubygems.org)). Work for the Gemcutter part has almost been finished (see [PR #1753](rubygems/rubygems.org#1753), [PR #1729](rubygems/rubygems.org#1729) and a series of [my progress reports](https://ecnelises.github.io/)). ## Content: This PR will contain my changes to RubyGems client, adding multifactor auth for `gem push`, `gem signin` and `gem owner` commands. Since no command for editing profile, adding command for changing multifactor auth settings seems unnecessary. ## Workflow: - User set up multifactor auth well in the site. (into `mfa_login_and_write` level) - When user does the actions requiring MFA, an OTP prompt is shown. Or user can add `--otp` option into command, like `gem push mygem-0.0.0.gem --otp 123456`. - If the OTP is incorrect, operation fails with failure text. ______________ # Tasks: - [x] Add OTP requirement to `push_command`. - [x] Add OTP requirement to `owner_command`. - [x] Add OTP prompt to `sign_in`. - [ ] Support for `yank_command`. - [x] Write related tests. I will abide by the [code of conduct](https://github.com/rubygems/rubygems/blob/master/CODE_OF_CONDUCT.md). Co-authored-by: Qiu Chaofan <fwage73@gmail.com> Co-authored-by: SHIBATA Hiroshi <hsbt@ruby-lang.org>


This PR is for the multifactor auth of the API part. The changes are added into v1 version of rubygems.org API. Current design is:
ProfilesController(or maybe other), getting the mfa setting status of current user (needs authentication).OTPfield in request header, and verify it.I'm not sure whether the approach is compatible for the rubygems versions (not supporting mfa). I will check how rubygems client handles the operation errors.
Below are tasks.
OTPfield in header.yankcommand).