Configuración de Token para una API de Rails 7 con una base de datos postgres

Joel Rojas
10 min readSep 21, 2022

Este tutorial describira como configurar el JWT en una API Rails versión 7 con una base de datos de postgres, discha configuración se hará con las gemas devise, devise-jwt y figaro.

1.- Creación del proyecto

Primero que todo debemos tener instalado en nuestro sistema operativo Ruby on Rails versión 7, Ruby versión mayor a 3.0.2, y postgres

Desde la terminal y sobre la carpeta donde queremos crear el proyecto creamos la API, en este caso se llamará “ApiRailsToken” haciendo referencia al tipo de aplicación que se va a crear y configurar, de igual manera vamos a especificar que el motor de la base de datos sera postgres y no sqlite que es la que configura rails por defecto

Se verifican los archivos del proyecto creado con con el comando “cd ApiRailsToken” y luego “ls”, para pasar a instalar las gemas que estan en el archivo GemFile escribiendo en la terminal “bundle install”.

2.- Configuración de seguridad para la base de datos

Como es una api creada con una base de datos postgres a continuación hacemos la respectiva configuración y nos vamos al archivo database.yml ubicado en la carpeta config/ en este caso la configuración queda de la siguente manera(recuerde que el puerto de la base de datos puede cambiar, en este caso el puerto es 5432).

Debemos tomar en cuante que no se deben exponer nuestros datos al momento de subir el proyecto a la nube así que hacemos una configuración adicional con una gema llamada “figaro” que nos permite darle seguridad a los datos del archivo (en este caso utilizaremos esta gema solo para conocer una configuración adicional) copiamos en el Gemfile

gem “figaro

Desde la terminal

bundle install

Y luego nuevamente desde la terminal

bundle exec figaro install

Este nos va generar una configuración que consta de crear un archivo en la ruta “config/application.yml” y tambien va agregar este mismo archivo a los archivos que serán ignorados al momento de hacer un commit del proyecto. Nos dirigimos al archivo creado application.yml y escribimos la configuración de los datos necesarios de la siguiente manera:

Nos vamos al archivo database.yml y escribimos la configuración en los campos requeridos como se muestra a continuación

Asi de esta manera configuramos las variables de entorno para la base de datos evitando subir el contenido de las variables cuando hagamos un commit, application.yml no va subir con los demas archivos dejando nuestras variables seguras, recuerde que esto se puede verificar en el archivo gitignore donde estan los archivos que no subiran a la nube.

Una vez hecha la configuración de seguridad para la base de datos ejecutamos

rails db:migrate

Y en la termianl deberiamos recibir un mensaje similar al siguiente

WARNING: Use strings for Figaro configuration. 5432 was converted to “5432”.
Created database ‘ApiRailsToken_development’
Created database ‘ApiRailsToken_test’

El mensaje nos indica que figaro cambio la variable del puerto 5432 a string “5432”, cambiamos esa variable en el archivo config/application.yml dejandolo con las comillas

3.- Configuración del archivo CORS

Para continuar con la configuración de seguridad de la api activamos una gema en el archivo GemFile para configurar el archivo cors, ya que debemos recordar que CORS es un mecanismo de seguridad basado en encabezados HTTP que define quién puede interactuar con la Api.

gem “rack-cors”

bunlde install

Y luego vamos a la siguiente ruta config/initializers/cors.rb y activamos la siguiente configuración.

4.- Configuración y activación del JWT

Ya en este punto generamos un scaffold para una de las tablas que se utilizará en este tutorial, esto con la intensión de verificar una vez configurada la api cual es la diferencia entre una api con jwt y sin jwt.

rails g scaffold Api::V1::Manager first_name:string last_name:string notes:text

Ejecutamos la migración.

rails db:migrate

Iniciamos el servidor con rails s. Si tienes un gestor de bases de datos para postgres como por ejemplo PgAdmin puedes ver que se creo la base de datos ApiRailsToken_development con la tabla api_v1_managers, o tambien lo puedes hacer desde la terminal con el usuario postgres.

Para activar el token agregamos las siguientes gemas en el archivo Gemfile.

gem “devise”

gem “devise-jwt”

Ejecutamos Bundle install desde la terminal y luego generamos los o él usuario que van ha recibir la configuración de seguridad, para este caso lo llamaremos Admin.

rails g devise:install
rails g devise Admin
rails db:migrate

Configuramos el modelo generado por devise en este caso admin.rb de la siguiente manera, puedes copiar y pegar.

class Admin < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable,
:recoverable,
:jwt_authenticatable,
:registerable,
jwt_revocation_strategy: JwtDenylist
end

En la misma carpeta de modelos creamos otro archivo con el siguiente nombre “jwt_denylist.rb” y lo configuramos de la siguiente manera, puedes copiar y pegar.

class JwtDenylist < ApplicationRecord
include Devise::JWT::RevocationStrategies::Denylist

self.table_name = 'jwt_denylist'
end

Generamos una migración desde la terminal.

rails g migration CreateJwtDenylist

En el archivo generado por la migración escribimos la siguiente configuración, puedes copiar y pegar

class CreateJwtDenylist < ActiveRecord::Migration[7.0]
def change
create_table :jwt_denylist do |t|
t.string :jti, null: false
t.datetime :exp, null: false
end
add_index :jwt_denylist, :jti
end
end

Ejecutamos la migración.

rails db:migrate

Ahora generamos la clave secreta, en este caso vamos a utilizar la gema “dotenv-rails” para configurar el DEVISE_JWT_SECRET_KEY así que agragamos al Gemfile la siguiente linea:

gem ‘dotenv-rails’, groups: [:development, :test]

Ejecutamos bundle install y ahi mismo desde la terminal ejecutamos “rake secret” y de esta manera generamos una clave secreta y la copiamos para pegarla en el archivo .env que debemos crear en la raiz del proyecto.

Nos vamos al archivo devise ubicado en la siguiente ruta config/initializers/devise.rb y pegamos la siguiente configuración al final del archivo, especificamente antes de “end” puedes copiar y pegar.

config.jwt do |jwt|
jwt.secret = ENV['DEVISE_JWT_SECRET_KEY']
jwt.dispatch_requests = [
['POST', %r{^/sign_in$}]
]
jwt.revocation_requests = [
['DELETE', %r{^/sign_out$}]
]
jwt.expiration_time = 15.day.to_i
end

Asi debe quedar el archivo.

Ahora creamos los controladores que permitiran ejecutar las acciones de la sesión, creamos una carpeta admins en la carpeta controllers, y en ella creamos dos archivos archivos con el siguiente nombre cada uno.

sessions_controller.rb
registrations_controller.rb

La configuración para sessions_controller.rb es la siguiente:

module Admins
class SessionsController < Devise::SessionsController
respond_to :json

private
def respond_with(resource, _opts = {})
render json: { message: 'Signed up sucessfully.' }, status: :ok
end
def respond_to_on_destroy
current_admin ? log_out_success : log_out_failure
end
def log_out_success
render json: { message: "Logged out." }, status: :ok
end
def log_out_failure
render json: { message: "Logged out failure."}, status: :unauthorized
end
end
end

Y la configuración para registrations_controller.rb es la siguiente:

module Admin
class RegistrationsController < Devise::RegistrationsController
include RackSessionFixController
respond_to :json
private
def respond_with(resource, _opts = {})
resource.persisted? ? register_success : register_failed
end
def register_success
render json: { message: 'Signed up.' }
end
def register_failed
render json: { message: "Signed up failure." }
end
end
end

En la carpeta app/controllers/concerns creamos el archivo rack_session_fix_controller.rb y lo configuramos de la siguiente manera:

Para este caso actualizamos el archivo de rutas en “config/routes.rb” como se muestra a continuación

Ya para activar la seguridad al momento de consultar datos a la api agregamos en cada controlador que sea necesario la siguente linea

before_action :authenticate_admin!

Es importante saber que el authenticate_admin! cambia según sea el caso del usuario al que asignamos con el comando “rails g devise ”, por ejemplo si la configuración la asignamos de la siguiente manera:

rails g devise User

en authenticate sera de la siguiente manera en cada controlados que lo necesite:

before_action :authenticate_user!

Con esto es importante aclarar que la activación del authenticate cambiara de acuerdo a la configuración que cada quien asigne al generar “rails g devise *****” considerando que ***** hace referencia a los usuarios que llevaran la configuración de seguridad.

Ante de pasar al siguiente punto de este tema y comenzar hacer las pruebas es importante hacer una ultima configuración para que al momento de solicitar información a la Api y no se esté autenticado esta envie como respuesta la solicitud de inicio de sesión, para ello debemos configurar el archivo “config/application.rb como se muestra a continuación:

Pueder copiar y pegar las lineas que debes agregar

config.session_store :cookie_store, key: '_interslice_session'
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options

5.- Pruebas de la API

A continuación veremos las pruebas con y sin jwt activado.

1- Nos vamos al managers _controller.rb y eliminamos la linea before_action :authenticate_admin! y ejecutamos rails s desde la consola, y luego desde una browser colocamos la siguiente dirección, recuerde que estamos trabajando en el ambiente de desarrollo.

De esta manaera vemos desde la consola que los datos que recibimos en el browser no tienen ningun tipo de seguridad al momento de hacer peticiones a la API como se muestra a continuación:

2- Activamos en el controlador before_action :authenticate_admin! y recibimos el siguiente error:

Para evitar este error es necesario activar la configuración en el archivo “config/application.rb” que vimos anteriormente

config.session_store :cookie_store, key: ‘_interslice_session’
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options

Asi al momento de hacer una solicitud a la API y no estar autenticado esta nos redirecciona automaticamente a un inicio de sesión.

3- Ir a la pagina, a partir de ahora las pruebas a la api se harán desde la consola, usted las puede hacer de la forma como le quede mas comodo, puede utilizar postman por ejemplo, en la terminal escribe el siguiente comando.

Debe recibir una linea en la consola como la siguiente indicando que fue redireccionado al inicio de sesión porque aun no esta autenticado.

4- Crear usuario de sesión

Debe recibir un mensaje como el siguiente indicando que el usuario ya fue registrado en la API.

5- iniciar sesión con un parametro para recibir el token

Con -i le permitira recibir un mensaje con el contenido del token como se muestra a continuación:

6- Crear un manager o registro en la API con el encabezado de autorización del token que recibimos, especificamente luego de Bearer debe colocar el token que recibio en el paso anterior.

Una vez creado el registro debe recibir un mensaje como el siguiente indicando que el registro fue creado con exito.

7- Consultar registros, de igual manera que el paso anterior debemos agregar el token en el encabezado de autorización así como en todas las solicitudes que se hagan a la API mientras se este autenticado.

De esta manera se recibe un listado de todos los registros que estan en la tabla de la consulta, en este caso la tabla Managers.

8- Cerrar sesión, ya para finalizar hacemos la solicitud para eliminar el token y cerrar la sesión eliminando la autenticación contra la API.

El mensaje que va ha recibir en la consola será el siguiente:

Ahora bien si hicistes todos los pasos como estan descritos en este tutorial ya tienes un conocimiento general de como configurar una API en Rails 7 y colocarle seguridad a todas la peticiones, espero te sirva de ayuda al momento de crear tus propias API

De igual manera aqui dejo el repositorio para que puedas guiarte y hacer tus propias Apis con una configuración similar

https://github.com/ejoelrojas9/ApiRailsToken

Exito

--

--