Final Project: React/Redux

Kenlyn Terai
Nov 15, 2017 · 5 min read

It was with a certain excitement and dread that I approached the final project. “It is supposed to be your magnum opus — a culmination of
all the skills you’ve learned so far,” they said. “Go wild!…you’re free to add on as much stuff as you’d like,” they said.

The latter endorsement was an invitation to too much fun, with frequent refiguring about how to scale the next coding obstacle. This is what I ended up building:

Snappy Name: We/Mix

Domain: A full CRUD music video playlist builder

Why: Because music drives creativity and it’s fun

Source API: Audiodb.com and YouTube’s iFrame Player API utilized via the react-youtube package

Authentication: The bcrypt gem and JSON Web Tokens (HS256 algorithm)

Rails API Testing: Model and request testing via rspec-rails, shoulda-matchers, factory_bot_rails, and Faker

Styling Packages: fixed-data-table-2, react-image-fallback, semantic-ui-react

I learned so much from doing this project which would encompass more than just one blog post. To begin with, I got to put Test Driven Development and JSON Web Token Authentication into practice. The following are some highlights:

Creating a useful Rails API

Although I’d worked through labs and understood the concepts in their individual parts, I had not yet created an API which could handle CRUD actions. It seemed like the best way to accomplish this was to take a Test Driven Development approach to ensure that I would build what I wanted.

I leaned on Luke’s series of Instructor Videos as guidelines in test buildout and ensuring that my API was coming through as expected. I got a lot of practice using Postman, used only sporadically in API Development labs, and finally became really comfortable using it. I was also introduced to RESTed, another great tool to quickly format and make HTTP requests and view the response.

Authentication

I was curious about using JSON Web Tokens since it had not been introduced in the course material. Following one of Luke’s aforementioned study group videos for the initial build out on the backend, I dove into the “Wild West” of React Authentication to find my own solution. Here’s a summary of the steps I took to build mine:

Rails Backend: we-mix-api (1)

Step 1: Implement password hashing and encryption with bcrypt

  1. Enabled the bcrypt gem in my gemfile for password hashing and encryption and ran bundle install
  2. Created a user migration with :username, :password_digest, and :email attributes
  3. Going to the user model, enabled has_secure_password to take advantage of ActiveRecord methods that tap into bcrypt (e.g. authenticate ). Also added validations for :email and :username presence and uniqueness.
  4. Now routes are needed to enable creation of users on signup. In the config/routes.rb file, I added the `post ‘/signup’, to: “users#signup”` route
  5. In the Users Controller,users_controller.rb, I created the signup method and a private `user_params` to make sure that only permitted parameters are included in User creation.
  6. I added a Sessions Controller and then wrote a basiclogin action.^
  7. With both Users and Sessions Controllers set up to encrypt passwords and then authenticate Users, I turned to implementing JWT.

Step 2: Enable coding and decoding of user attributes with JSON Web Tokens

  1. Added the jwt-gem to my gemfile (JWT info: https://jwt.io/) and ran bundle install
  2. Select a hashing algorithm — I ran with the default, HMAC (HS256)
  3. I needed methods to create and decode JWT’s. To implement this, I created an auth.rb file in the lib folder, built an `Auth` class, and then built create_token and decode_token class methods according to the video. The create_token method takes a User object as an argument and then creates a token associated with it.
  4. Now to make these methods useful. In the Users Controller, I want to create a token when a user signs up for my app. If a User has saved, then a token would be created at the same time, effectively logging them in at the same time.
  5. Turning to the Sessions Controller and session creation, the login action receives a user’s email and password and then finds a user by email and authenticated by their password. If both are true, a JWT token is created and sent to the client via JSON.

^If present, comment out protect_from_forgery_with: :exception line in `application_controller.rb`. protect_from_forgery exists to protect Rails applications from Cross-Site Request Forgery (CSRF), a serious vulnerability from placing trust on the session identification cookies passed between browser and server. JSON Web Tokens would replace the function of Rails’ default CSRF protections.

React/Redux Client: we-mix app

On the React side, my goals were to enable Users to

  • Login and CRUD their own video playlist
  • Prevent other users from seeing their playlist or perform any other action on the User’s account
  • Logout

To accomplish this, I did the following:

  1. Created LoginPage and SignUpPage Containers.
  2. I decided that the JWT would be stored in localStorage, which is not in itself connected to the store. Because of this, I decided to place functions involved in Login and Signup We/Mix API calls in an api directory. User sign up, or registration, occurs in a UserApi Class containing a signup function which “POST’s” a new User’s email, username, and password to the create action in the Users Controller. User login occurs in a SessionApi Class containing a login function which “POST’s” a User’s credentials to the login action in the Sessions Controller. Both of these calls result in a returned JWT which is then stored in the window’s localStorage.
  3. I still wanted to have Session and User actions connected to the store. I did this by

(1) importing bindActionCreators from redux, and connect from react-redux to both the LoginPage and SignUpPage containers.

(2) setting up a sessionActions file to hold sessions-related action creators. So far, these action creators set the received JWT to localStorage and clear the token from localStorage on logout.

(3) Similarly, I set up a userActions file to hold user-related action creators which set the received JWT to localStorage.

(4) I am still considering adding actions to each of these files and dispatching them to the user_reducer to add an “authenticated” attribute to the store. However, it’s not yet clear that it would be helpful since I’ve successfully implemented an EnsureLoggedIn wrapper component, which brings me to…

4. Protection from other viewers: The Rails API only sends videos associated to the authorized user in the index action. I created an EnsureLoggedIn wrapper component to protect routes unless a user was logged in.

Acknowledgments

With all of the ideas in my head, I could not have accomplished this without the community at Learn and talking through ideas with friends and instructors. Special thanks go to Luke Ghenco for direction, Alice Balbuena for the invaluable study group and for taking time, and Chris Cole, David Kennell, and Jaci Hayden for their general support. Thank you!!

References

  1. Luke Ghenco, “JWT Authentication With Rails,” January 27, 2017
  2. Sophie DeBenedetto, “JWT Authentication with React + Redux,” September 9, 2016
  3. Adam Khoury, “JavaScript Storage Interface sessionStorage localStorage Tutorial,” Oct 4, 2015
  4. Raja Rao DV, “React Redux CRUD App/react-redux-blog,” Accessed November 2017 (Associated blog :Securing React Redux Apps With JWT Tokens)
  5. Scott Luptowski, “Adding Login and Authentication Sections to your React or React Native app,” Aug 25, 2016

More From Medium

Also tagged Json Web Token

Also tagged Authentication

Also tagged Authentication

Setting up Signup and Basic Validations

51

Top on Medium

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade