RubyGems GSoC Report for UI Part
This is an article about progress of adding multi-factor authentication (MFA) into RubyGems.org site. There have been several reports for different parts of implementation, you can check them out here. Currently, at early July 2018, work for the site except API (we often call them UI part) has already been finished and merged into RubyGems’ main develop branch, see PR#1729.
What is MFA and why we need it?
This topic deserves another article. Authentication of users’ identities has been always a problem since Web 2.0 or earlier internet services came into being. Simplest way of logging in is using the combination of username (or email) and password. Your password is static and if someone stole it your account would be got. Considering every time you’ll type the same password or sometimes you have to rent your account to your friend (like Steam account), password authentication is quite vulnerable.
There are many solutions now. An impressive one to me is TypingDNA, which authenticates you by how to type. Security controls used by banks and some network game providers in the early times are also a good but inconvenient solution. The MFA we are talking about now is an economic choice. As I wrote in past posts, MFA is an authentication system as addition to traditional auth methods by using one-time-passwords (OTP), which solves the two problems I raised above. In real life we usually have three types of MFAs:
By an algorithm for symmetric OTPs
The first one is almost synonymous to TOTP and HOTP auth system. They are discribed in detail in their respective RFC documents, maybe I’ll use a single article for introduction to how they work.
By using SMS in mobile phones
The second one is used by Telegram, Signal, WhatsApp and many other mobile-oriented apps. Note that messages sent to your phone is quite possible to be modified without you informed. So some apps requires you to send message to them to do this authentication due to difference in difficulties between modifying incoming messages and sending-out messages. SMS is still costy, and inconvenient for desktop users, nevertheless.
This is quite similar as the security controls I said in above text. Currently there’s a universal standard named U2F and products like Yubikey or other cheaper ones. Chrome, Opera and Firefox now supports it well. I’ll discuss it in further articles.
Here we choose time-based one-time password (TOTP) as the solution, for it’s simple, reliable and used by many large organizations and projects. Many people use mobile auth apps like Authy or Google Authenticator. Adding MFA for them just means scanning another QR-code.
The development starts from a research on gems for the functionality. If you search two factor authentication in GitHub, a list of multi factor auth related gems will be shown, but some of them are no longer maintained and most of them are extension of devise. After checking source code of them, I found they all depends on rotp for the main totp feature.
So I added rotp into the project first, along with a gem showing QR-code named rqrcode. You can refer to this post for more details.
Why not a separate gem?
Actually I was planning to write a separate gem for the multi-factor auth functionality aiming at integrating with Clearance. But I didn’t do that for several reasons.
- The gems I found first are most extensions for Devise. Devise is a great and famous gem for its full feature and maturity. It takes your controller, model or even some part of views over for the login and registration process. So extending the logic is easier, than doing such things onto a gem that only focuses on much fewer stuff.
- What if I wrote a wrapper (such as
- The key is to give a well-implemented multi-factor authentication feature to RubyGems.org. So making everything works is the first level of concern, especially for our site is not complex, comparing with big ones like GitLab or Discourse.
But this is still a good idea. And I may work on it after these done.
Structure of the addition
The final pull request merged contains 18 commits, having about 800 lines of code. Main changes includes:
- New gems added into Gemfile and Gemfile.lock.
- A new controller about multi-factor authentication stuff added, named
MultifactorAuthsController. It has
destroy, three actions. See this post for detail.
- When user accesses
multifactor_auths#newfrom profile settings page, a QR-code will be shown (of course, it does a pre-check for current settings on the auth) and the key is stored in session. User can post a form with a digit generated from the key.
- If the digit code (OTP) is correct, the auth is enabled and recovery keys will be shown to user. Otherwise, it fails and key in session will be cleared.
multifactor_auths#destroyhandles users’ request on disabling the auth.
- When user accesses
sessions#create, we checks auth settings of current user. If the extra auth is enabled, OTP will be required. That will be posted to a single method named
- Methods about authentication and auth setting state enums are added into users model. See this work report.
- A new database migration adding key, state, recovery codes of multi-factor auth, along with
last_otp_at, an attribute for preventing a OTP authenticated many times.
- Certainly, the related pages are also added.
- Unit tests for the controller and model, with integration tests, are written. See report on Jun 19th.
- Actually, I did not write serious tests for my projects before. This helps me understand how to consider test branches, what to assert (or refute) and, the importance of testing. It really reduces anxiety when adding new things.
- Open-source projects are somewhat in a loose communication. The first thing is to figure things out by self. And it’s good to remember ‘you can you up’.
- The community has fewer active people than I thought! So every contribution may be very important.
- It’s necessary to let others (especially mentors) know your progress.
Thanks for my mentors, @sonalkr32 and @indirect. They answered my silly questions and reviewed my code. I also want to say thanks to GitLab, for they are a good model showing what is a good multifactor-auth workflow. (In fact, I heard of the noun from GitLab) Some of other participants of Ruby GSoC this year, I talked with them, knew how different people interacts with their mentors. This also helped me.