SERVER SIDE
CONFIGURATION
Add "bcrypt" to your gemfile
Add "jwt" to you gemfile
Remember to run bundle install
SET UP THE MODEL
Add "has_secure_password"
Add authenticate class method that takes in email (or username) and password
CREATE THE MIGRATION
Add the column for password_digest
TEST
Test if it works in the Rails console.
Create a user with password.
User.authenticate([email], [password])
It should return the User record.
ADD THE ROUTES
ADD APPLICATION CONTROLLER HELPER METHODS
ADD AUTH CONTROLLER
User Postman to test if it works
Send POST request to your API endpoint
Add user params in the request's body
Should return json specified in AuthController
CLIENT SIDE
ADD AUTH ADAPTER
Add login method that takes in login parameters (email and password) to send the request to the database
Add current user method that finds the current user via the jwt token
ADD LOGIN LOGIC
The login method is added to the Login component, which will be triggered once the user submits the login form.
SET THE REDUCER AND ACTION
Once the user is found the redux store is updated with the user object (which is being assembled by auth#create). The reducer looks like this:
The corresponding action is pretty straightforward:
Don't forget to connect the dispatch method with the component so that it can update the redux store.
ADD THE LOGIN LOGIC TO ROUTES
The routes uses a simple ternar to authenticate users with isLoggedIn. App.isLoggedIn checks if the user has been set in the redux store:
SUMMARY OF THE LOGIN PROCESS - INITIAL LOGIN
This process is triggered once when the user types in his email and password and submits the form.
Frontend:
User submit from the Login component invokes AuthAdapter.login with the parameters email and password
AuthAdapter.login assembles the header and sends the request to the API endpoint 'login'
Backend:
The API endpoint hits the method auth#create
auth#create does multiple things:
looks for the user with the given arguments (email, password)
creates the jwt token (with the helper method application#issue_token
returns the user object and the jwt token
Frontend:
onLogin receives the user object
sets the localStorage with the jwt
sets the redux store with the user object
the routes check if the user object is present in the redux store; if no user has been set, the user is redirected to the login/main page
The request to retrieve the current user will hit the endpoint 'me' and the controller method auth#show (see above for ApplicationController).
SUMMARY OF THE CURRENT USER PROCESS - AFTER INITIAL LOGIN
This process is triggered every time a page is being refreshed. The user has already been authenticated on intial login and the jwt token is stored in the browser, so there is no need to create a new one. Instead of asking the user to re-submit his login credentials the jwt from localStorage will be sent to the backend. There it is being decoded and the user object is being returned so that the redux store can be refreshed. It is very similar to the login process, except that we are hitting auth#show instead of auth#create
Frontend:
componentWillMount -> AuthAdapter.currentUser sends the jwt token to the API
Backend:
The API endpoint hits the method auth#show
auth#show does multiple things:
the helper method Application#authorize! intercepts auth#show if no user is being found
auth#show calls Application #current_user
Application#currentUser calls various helper methods
#token grabs the token from the HTTP request header
#decoded_token uses the JWT method to turn the hashed string into the original object
#token_user_id returns the user id
#current_user uses the user id to query the database and returns the user object
auth#show renders the json object with the user info
Frontend:
componentWillMount -> AuthAdapter.currentUser uses the object to set the user in the redux store
sets the localStorage with the jwt
sets the redux store with the user object
the routes check if the user object is present in the redux store; if no user has been set, the user is redirected to the login/main page