Json Web Token, es un estandar abierto (RFC-7519) que define una forma compacta y auto contenida para trasmitir información de manera segura, entre dos partes como un objeto JSON. Esta información puede ser verificada y de confianza por su firma digital. JWTs pueden ser firmados usango un algoritmo secreto como HMAC o algoritmos de llave publica como RSA o ECDSA.
- Autorizacíón: Es el escenario más común de utilización de los JWT. Una vez el usuario esta logead, cada solicitud subsecuente debe incluir el JWT, permitiendo al usuario acceder a rutas, servicios y recursos que son permitidos con ese token.
- Intercambión de información: JSON Web Tokens, son una buena forma de transmitir informacion segura entre partes. Pues pueden ser firmados, por ejemplo, usando una pareja de llave publica y privada se puede estár seguro de que el emisor es quien dice ser. Además como la firma es calculada usando el header y el payload, se puede verificar que el contenido no ha sido modificado.
La explicación de la estructura del json y como se encripta el token podemos encontrarla en jwt.io
Vamos a configurar un sistema de autenticación para nuestra api basado en JWT con ayuda de la gema knock..
Creamos scaffold para gestionar los usuarios del nuestro api
rails g scaffold user name:string email:string:uniq username:uniq password_digest
nota: los tipos de dato por defecto son strings, por lo tanto en ese caso password_digest lo será
El campo que va a guardar nuestra contraseña, password_digest, es un nombre estándar que vamos a utilizar más adelante para proteger nuestra API.
Configuramos el bcrypt en el modelo user
Esta libreria es la que gestiona la parte de contraseña con el campo password_digest en la tabla users.
Primero se descomenta la gema bcrypt en el archivo Gemfile (se encuentrá ubicado en la raiz de nuestro proyecto)
gem 'bcrypt', '~> 3.1.7'
Ahora es necesario agregar la sentencia has_secure_password
al modelo user. (app/models/user.rb
)
corremos el gestor de dependencias
$ bundle
Cuando vamos a crear un nuevo usuario en el body de la petición vamos a pasar dos parametros password y password_confirmation, aquí es donde la gema bcrypt hace su trabajo y más específicamente el método has_secure_password. Lo que hace es comparar el password y el password_confirmation y si son iguales, encripta la contraseña en el campo password_digest de la base de datos.
Por razones de seguridad Rails, por defecto evita la asignación masiva de parametros a nuestros modelos, entonces para que podamos hacer una asignación masiva al crear un nuevo usuario, debemos hacer un whitelist (lista de parametros permitidos) en el método user_params
(app/controllers/users_controller
) de la siguiente manera:
def user_params
params.permit(:name, :email,:role_id , :username, :password, :password_confirmation)
end
Configurar JWT
Configuramos el JWT para la autenticación del api.
- En el Gemfile agregamos la gema 'knock´
$ gem 'knock'
- Ejecutamos nuevamente el manejador de dependencias
$ bundle
- Instalamos y configuramos knock
$ rails g knock:install $ rails g knock:token_controller User
- En
app/controllers/application_controller.rb
incluimos la libreria como módulo, agregando al incio de la clase la siguiente linea.
include Knock::Authenticable
-
Luego debemos proteger todos los demás controladores para ello agregamos la linea
before_action :authenticate_user
en el controlador principalapplicationController
, como todos los controlladores extienden de el, adquirirán este método y por ende para cualquier petición a alguno de sus métodos el usuario deberá estar autenticado. -
Agregamos en el controlador
app/controllers/user_token_controller.rb
skip_before_action :verify_authenticity_token, raise: false
- En el
config/inizializers/knock.rb
agregamos
config.token_secret_signature_key = -> { Rails.application.credentials.read }
Y listo, ya podemos probar en postman nuestra autenticación JWT.
Para ello primero creamos un usuario. http://localhost:3000/users
con el body:
{
"name" : "Nuevo Usuario",
"email": "myemail@email.com",
"username" : "newUser",
"password":"123456",
"password_confirmation": "123456"
}
Luego para solicitar el token de acceso JWT de un determinado usuario, hacemos una peticion a http://localhost:3000/user_token
con el body:
{
"auth":{
"email": "myemail@email.com",
"password":"123456"
}
}
El servicio responderá con el jwt token, que podremos utilizar como autenticación enviandolo para consumir todos los servicios de nuestro api.
{
"jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzg5NTg1NzcsInN1YiI6Mn0.K3PIXtGaGnaOEVdOle_2dg8eB-miSm2OSxOTZE8wgXg"
}
Para poder utilizarlo debemos enviarlo en nuestras peticiones como header utilizando el esquema Bearer así:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzg5NTg1NzcsInN1YiI6Mn0.K3PIXtGaGnaOEVdOle_2dg8eB-miSm2OSxOTZE8wgXg