View on GitHub



RubyGems GSoC Progress report on Jun 8th

This is a weekly report regarding my GSoC 2018 project, adding two factor authentication to existing RubyGems command line tool and site, a worldwide gem hosting site supported by community.

This week, 2fa feature about UI part is almost done, except:

  1. Unit tests about current feature (2fa toggle, user login OTP check)
  2. A feature that toggles the feature (feature flags)
  3. Tweaks on UI design and workflow

The first and second should be finished within this week, or at least before first round of evaluation.1 So it can be merged into master for insides to test, gradually updating the third.

At rest of this post, I’ll talk about implementation details, possible issues, further work and my plan.

Please login to continue

As we discussed and learned from what other communities has done, user’s 2fa settings should be at one of three levels:

I’ve not currently add a choice for the three levels. Instead, user can simply choose to turn it on or off. Since API part is not finished, adding the third is meaningless. But that would be quick.

Previous post shows what 2fa settings page looks like. But since I modified them, it would be better to put them here.



The upper one is a page giving a QR-code showing necessary information for your authenticator app to generate 6-digit code. It’s rendered as SVG. If you have some problem with the QR-code, you can also manually type parameters on the right into the app. Issuer name, the one before your email, is the HTTP host you are using. Just type the OTP code to continue.

If everything goes well, 10 recovery codes will be shown here. They are important for those lost their phones, every one can be used once in replacement of OTP. So I’m not sure if I should add a checkbox before continue. Click continue and you will be redirected to profile edit page.


I moved 2fa related controller methods into a single controller, see this commit. To prevent user from re-enabling, there are two filters.

def require_no_auth
  return if current_user.no_auth?
  flash[:error] = t('two_factor_auths.authed_no_access')
  redirect_to edit_profile_path

def require_auth_set
  return unless current_user.no_auth?
  flash[:error] = t('two_factor_auths.no_auth_no_access')
  redirect_to edit_profile_path

A user must have enabled 2fa before disabling it, and vice versa.

Another thing to implement is extra authentication when logging in. Logic here seems a little bit complex. So to make controller clean, I drop something into individual methods. Now our login action looks like:

def create
  if verifying_otp? && !@user.otp_verified?(session_params[:otp])
    session[:mfa_user] = nil
  elsif !verifying_otp? && @user&.mfa_enabled?
    session[:mfa_user] =
    render 'sessions/otp_prompt'

Both OTP checking and normal login request are sent to the same method. Originally, I thought I can use a before_action to wrap an action which requires OTP check. But since it needs one more submit and page redirect, combining the logic with action seems a better approach. This commit is about it.

Issues & Further work

I just keep them here for memorization and possible discussion.


Further work

Some of things below may consume a lot of time. I’ll work on them if I have extra time after 2fa done.


Google’s newest timeline shows Final Evaluation will be Aug 14. So all coding and testing work should be done before August for time with documents. 2fa stuff on API is expected to be simpler than what’s on UI part since client program can interactively require otp code if received a 401 response. It is expected to be finished within June. After that I will hacking RubyGems for 2fa, and I’m not sure if there’s something related to Bundler here.

If you have any advice, reviews or questions, please commit at Issue #1725, PR #1729, or share your comments in #rubygsoc-2fa channel on Bundler Slack. Thanks.

  1. Chinese usually consider Monday as start of a week, I’m not clear about people from other countries, LOL 

  2. U2F is a USB device authentication protocol, Firefox and Chrome supports it well now