
Well, this is it. Each portfolio project throughout the Flatiron curriculum has marked an important milestone on my journey, and building an app using React is the capstone. I landed on creating an e-commerce site for my project, a completely new kind of challenge that I was excited to dive into. My app, Bazaar, is intended to be a local marketplace for creators and entrepreneurs to share their goods. In addition to React, I decided to utilize the extensive Material UI library to enhance styling and overall user experience, the Stripe API to simulate payments, Redux-Thunk to manage state and handle asynchronous actions, and of course a Rails API to handle data persistence. For this final project reflection, here are some useful tidbits I picked up along the way and my final impressions concerning the last leg of this adventure.
Let’s begin with a brief discussion about adding third party user authentication in React. Considering the fact that this is such a common task in modern web apps, I was surprised that implementing the react-google-login OAuth component in my app proved to be tricky. Google provides some instructions here, but dealing with a Rails backend is not really addressed and it became quite a hurdle to overcome. Here are the steps I took to address this issue.
- First, familiarize yourself with the profile object Google sends after a user logs in via the React component on the frontend. Here’s an example:
{email: "test@gmail.com', givenName: 'test', familyName: 'test', googleId: '123456', imageUrl: 'example', name: 'test test'}
- Also included in this response are two tokens from Google (tokenId and accessToken) that need to be sent to your backend like so:
export const googleLogin = (response) => { return (dispatch) => { dispatch({ type: 'LOADING_USER' }); const token = response.tokenId const requestOptions = { method: 'POST', headers: { 'Authorization': `Bearer ${response.accessToken}`, 'Content-Type': 'application/json', 'access-token': `${response.accessToken}` }, body: JSON.stringify(token)}
- Next, the response token is sent to the following url and checked by Google. The token is verified and the response object can now be used to find or create a new user in your server by accessing the attributes you want. Here is the method I used in my controller, along with an example response object from Google:
def create
url = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=#{params['_json']}"
response = HTTParty.get(url)
@user = User.create_user_for_google(response.parsed_response)
@user.save
if @user
render json: @user
else
render json: { status: 'error', message: 'user not found' }
end
end
___________________________________________________________________{
// These six fields are included in all Google ID Tokens.
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"iat": "1433978353",
"exp": "1433981953",
// These seven fields are only included when the user has granted the "profile" and
// "email" OAuth scopes to the application.
"email": "testuser@gmail.com",
"email_verified": "true",
"name" : "Test User",
"picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
"given_name": "Test",
"family_name": "User",
"locale": "en"
}
- It should be noted that Google does not recommend checking tokens via the above url in production, but for development purposes it works. Once the user is found or created, it is then sent back to the frontend.
Whew, that’s a lot! While not the perfect solution, it is effective and allows users to easily login using Google.
One more helpful tidbit. Using React Router is a great way to implement different views in your application. When it comes to user authentication for each of these routes, checking if the user is logged in can become tedious. By adding a higher-order component (HOC) and connecting it to the Redux store, taking care of authentication is a breeze. Here’s an example of the component I used:
import React from 'react';import { connect } from 'react-redux';import { Redirect, Route } from 'react-router';const AuthRoute = (props) => { const { user, type } = props; if (type === 'private' && !user.isAuth) return <Redirect to='/'/>; return <Route {...props} /> };const mapStateToProps = ({ user }) => ({
user
});export default connect(mapStateToProps)(AuthRoute);
Since the function is connected to the store, it can check if the user is authenticated or not and either redirect to the home page or return a new component that sends the user to the desired path. Then, in my App.js file where my routes are defined, I can add the following:
<AuthRoute path='/cart' type='private' />
Again, this is a great way to protect routes and keep some logic contained to the higher-order component.
Now for final impressions. One of the things I really love about the Flatiron curriculum and getting to build applications in a variety of settings is the opportunity to be flexible and really immerse myself in different languages and frameworks. Working with React was admittedly a ton of fun largely because of the massive amount of resources out there, whether that be through libraries like Material UI or some other middleware provider. Building an application from scratch presents its own challenges, but much like any new adventure, settling into your surroundings and embracing the hurdles that come up as learning opportunities is crucial and ultimately very rewarding.
This may be my last portfolio project as a Flatiron student, but it is definitely not the end of my journey. More than anything, I can take a moment to appreciate how much I’ve grown and feel proud of that. At the same time, I also recognize that there is so much more to learn and experience, which I am really excited about as well. More to come soon. Until next time!