Authentication and React-Redux
By: Damely Tineo
Tags: Cookie, React Redux, Authentication, Cors, Activerecord

This past week I added authentication to my Rails React Redux Storyteller application — the ability for users to sign up and log in and out of the app.

In order to add comments, view stories, rate stories, or add new stories, visitors must be registered users.

In order to better understand authentication I’ll review a few key concepts I encountered throughout the process of adding this feature to my application.

Signup & Login Front-end Process

When a user first signs up via the new user form, their information, once validated (using Active Record validations), is sent to the server/database via a POST fetch request (see below method). Upon successful authentications, meaning a new user is successfully created, a user object is returned within the data object of the fetch JSON response.

export const signup = (user, history) => {

return (dispatch) => {

fetch(`http://localhost:3000/users`, {

method: 'POST',

headers: {

'Content-Type': 'application/json',

},

credentials: 'include',

body: JSON.stringify({ user: user }),

})

.then((res) => res.json())

.then((data) => {

if (data.user) {

dispatch({

type: 'AUTH_SUCCESSFUL',

payload: { loggedIn: data.logged_in, currentUser: data.user },



});

history.push(`/stories`);

}

else {

dispatch({

type: 'AUTH_UNSUCCESSFUL',

payload: { errors: data },

});

}

})

};

};

Remembering Redux’s Action -> Reducer -> Updated state flow, this user’s data is next passed along to the corresponding reducer to then be assigned to the store state and accessible to all components within our app.

The react-redux “connect” method allows us to import action methods as props in our component. In my stories component I can now hook into the redux store, and grab the information passed from the action to the reducer to access and display the current user’s information.

const mapStateToProps = state => {

return {

errors: state.auth.errors

}

} export default connect(mapStateToProps, { signup })(SignUp); import { connect } from 'react-redux';

import { signup } from '../actions/authActions'

“Signup” is the method that is called when a user submits a sign up form: this.props.signup(this.state, this.props.history) meaning we have now reached a full circle.

What’s happening in the backend? After successfully completing the signup process, the controller method logs in the user and returns said user’s information to be used by the front-end. This is done by saving the user id to the session id. session[:id] = @user.id and rendering the previously mentioned JSON object json: { status: 201, user: @user, logged_in: true} to the front-end.

Last but not least, Persisting Logged-in State at Page Refresh. Now that my user has successfully signed up and is logged in, an equally important part of this process is ensuring that our logged in state and its current user are persisted on the client-side.

To do this we must first make sure that the cookie is presented to the browser (browser, meet cookie. Cookie, meet browser. Good? OK) then we need to make sure said cookie is stored in the browser and that it matches the session_store cookie value at every HTTP request. Note: fetch won’t send cookies, unless you set the credentials init option (and on the backend include “credentials: true” in the cors.rb file).

Persistence is not only used to improve the functionality and user experience of our apps but also to block access to those components we only want displayed if and when the user is logged in. We achieve this with a simple redirect.

Extras for configuring your Rails backend:

In cors.rb remember to update your origins to ‘http://localhost:3001' , or the origin from which your web apps is making its cross-origin HTTP request.

In your session_store.rb remember to set a unique value to the cookie that will be stored in the browser Rails.application.config.session_store :cookie_store, :key => ‘_storyteller’ .

Lastly, since we are using a cookie and our requests are coming from an external source, remember, in the Application Controller, to disable forgery protection on controller by skipping the verification : before_action:skip_before_action :verify_authenticity_token