Lesson 1
User Authentication with Bcrypt Password and JWT
Introduction

Welcome to the first lesson of our course, "Securing and Testing Your Ruby on Rails App." Today, we'll be focusing on user authentication, ensuring user passwords are stored securely using Bcrypt and managing authentication with JWT (JSON Web Tokens).

Authentication is a cornerstone of application security, protecting user data, and controlling access to specific parts of your app. Bcrypt is a proven library for securely hashing and storing passwords, making it computationally difficult for attackers to crack them. JWTs enable secure transmission of information as a JSON object, facilitating robust authentication management. By the end of this lesson, you'll be equipped to integrate JWT-based authentication in your Ruby on Rails app.

Let's dive right in!

Generating the User Model

First, we need to create a User model to store user information, including their securely hashed passwords. This model will encapsulate user-related data and behaviors.

Run the following Rails generator command to create the User model with username and password_digest fields:

Bash
1rails generate model User username:string password_digest:string 2rails db:migrate

Explanation:

  • The rails generate model User username:string password_digest:string command creates a new User model with username and password_digest attributes.
  • The rails db:migrate command applies the migration, creating the users table in the database with the specified attributes.
Securing User Passwords

With the User model in place, we'll add password hashing functionality using the has_secure_password method provided by Rails.

Open the user.rb model file (app/models/user.rb) and add the following:

Ruby
1class User < ApplicationRecord 2 has_secure_password 3end

Explanation:

  • class User < ApplicationRecord: Defines the User model, inheriting from ApplicationRecord.
  • has_secure_password: Uses metaprogramming to add functionalities for securely hashing and storing passwords. It automatically adds attributes like password and password_confirmation to the model, managed in-memory for security purposes.

Now, whenever you create a new user, the has_secure_password method will ensure the password is hashed using Bcrypt and stored in the password_digest field.

Implementing the Register Action

Next, let's implement the register action in the AuthenticationController, which will handle the creation of new users.

Here’s the code for the register action:

Ruby
1class AuthenticationController < ApplicationController 2 def register 3 user = User.new(user_params) 4 if user.save 5 render json: { message: 'User created successfully' }, status: :created 6 else 7 render json: { error: user.errors.full_messages }, status: :unprocessable_entity 8 end 9 end 10 11 private 12 13 def user_params 14 params.require(:user).permit(:username, :password) 15 end 16end

Explanation:

  • The register action creates a new User object with the provided parameters.
  • If the user is saved successfully, it returns a JSON response with a success message and a created status.
  • If there are errors, it returns a JSON response with the error messages and a unprocessable_entity status.
  • The user_params method ensures only permitted parameters (username and password) are allowed for user creation.
Setting Up JWT Authentication

JWTs provide a robust solution for managing authentication. We'll create a token that can be sent to the client and used to verify user identity in subsequent requests. A token is a string of characters that represents a user's authentication credentials. In JWT, tokens contain user information like IDs and expiration dates, and they are securely signed to ensure authenticity. This allows users to authenticate and be recognized without the server having to store session data.

Here's how to set up the generate_token method in the AuthenticationController:

Ruby
1private 2 3def generate_token(user_id) 4 JWT.encode({ user_id: user_id }, Rails.application.credentials.secret_key_base) 5end

Explanation:

  • The generate_token method takes a user_id as an argument.
  • It uses the JWT.encode method to create a token, encoding the user_id with a secret key (Rails.application.credentials.secret_key_base).
Implementing the Login Action

Finally, let’s implement the login action to authenticate a user and generate a JWT token if the credentials are valid.

Here’s the code for the login action:

Ruby
1def login 2 user = User.find_by(username: params[:username]) 3 if user&.authenticate(params[:password]) 4 token = generate_token(user.id) 5 render json: { token: token }, status: :ok 6 else 7 render json: { error: 'Invalid credentials' }, status: :unauthorized 8 end 9end

Explanation:

  • The login action finds a user by the provided username.
  • It checks if the user exists and if the authenticate method verifies the password.
  • If successful, it generates a JWT token using the generate_token method and returns it in the JSON response with a ok status.
  • If the credentials are invalid, it returns an error message with an unauthorized status.

Lastly, register the new actions in the routes.rb file:

Ruby
1Rails.application.routes.draw do 2 post 'register', to: 'authentication#register' 3 post 'login', to: 'authentication#login' 4 resources :todos 5end
Overview and Summary

In this lesson, we covered:

  • Creating the User model and securing passwords with has_secure_password.
  • Implementing the register action to create new users.
  • Setting up JWT-based authentication with the generate_token method.
  • Implementing the login action to authenticate users and provide a JWT token.

By achieving these tasks, you've secured your Ruby on Rails application using JWT for authentication. Now, move on to the practice exercises to further solidify your understanding. Great job!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.