Skip to content
Bryan Eli edited this page Oct 14, 2020 · 8 revisions

There are two main ways to log in to Accounts, using a social network or using an email and password.

Email and password login

Using the email address that a user types in, we search for "verified" email addresses and from that we get the matching User accounts.

ContactInfo.verified.where(value: email).preload(:user).tap do |matches|
   return matches.map(&:user) if matches.any?
end

Then, in order to check the password that the user typed in, we basically use the gem bcrypt. bcrypt provides a secure way to store and then check for correct passwords using the authenticate method which compares users' provided password against the password_digest we've stored on sign up. bcrypt defines an authenticate method on the Identity model, and all you need to provide is the user's id and the password, like so:

Identity.authenticate({ user_id: user&.id }, login_form_params.password)

Side note

One thing we do differently from what is advised as the most secure way of authenticating users is: we let our users know whether they've just entered the wrong password for an account which in fact does exist in our database, or if there is not a (verified) account associated with the provided email or username. This is a tradeoff we make in order to be more friendly with our users but at the same time, we do have rate limiting in place so the security risk is minimal.

Social Login

Summary: Logging in with a social network—to the end user—is a one-click operation. In the backend at a high level—thanks to the omniauth gem—it is pretty simple. Where the code in Accounts gets a little more complicated is in the case that we don't recognize the social provider by its name and unique ID, so we have to make use of the email address in which case we might sign up a new user instead of logging in.

In this wiki we'll focus on the simple case that we recognize a user's social provider so we just log them in.

What happens in the frontend

The user has two ways to log in: using Facebook or using Google (for educators it tends to be Gsuite, not the regular Gmail). Click, and the user gets redirected back to the app they came from (like openstax.org or Tutor at tutor.openstax.org).

What happens in the backend

At a high level, the omniauth gem takes care of all the OAuth back and forth and just gives us the provider name and the uid unique ID which we use to find a user. If the user had not authenticated successfully with Facebook/Google, then omniauth wouldn't have given us the uid and provider, so when we do get it, we trust that the user owns that account.

      elsif (outputs.authentication = Authentication.find_by(provider: @oauth_provider, uid: @oauth_uid))
        # User found with the given authentication.
        # We will log them in.

The relevant pieces of code:

Gems used:

Clone this wiki locally