3

API REST en Laravel 8 con autenticación JWT

<h1>Autenticación con token JWT en Laravel 8</h1>

Laravel es un framework de PHP increible, pero tal vez te has preguntado como usarlo de Backend para que otras aplicaciones puedan obtener información de nuestra aplicación, o simplemente, deseas crear un Frontend que consuma los recursos de la API REST.

De ahí que surja la duda, ¿Cómo hago para autenticar los usuarios?

La solución es JWT.

Si todavía no conoces como funciona Laravel o qué es Laravel, te recomiendo ver el Curso de PHP con Laravel.

JWT te permite retornar un token de autenticación que te identificará como usuario del sistema, que se guardará del lado del navegador (localStorage, sessionStorage, cookies, Redux, etc…).

Una vez que ya entendiste la idea, manos a la obra.

<h1>Creando nuestro proyecto</h1>

NOTA: No es necesario realizar estos pasos si tu proyecto de Laravel ya está creado.

Lo primero que debemos hacer es tener instalado el gestor de dependencias de PHP composer.

Aquí te dejo un link para que puedas instalarlo sin problemas, en la versión de Ubuntu.

Para otras sistemas operativos, te recomiendo la página oficial de Composer

Una vez lo tengamos instalado, comprobamos que funcione correctamente, escribiendo en nuestra terminal:

composer --version

Tendrás un resultado similar a este:

Composerversion 1.10.15 2020-10-13 15:59:09

Si no tienes instalado Laravel, lo instalamos de esta forma en Ubuntu:

composerglobalrequire laravel/installer

Para otros sistemas operativos, les recomiendo leer el Manual de instalación de Laravel

Una vez, tengamos nuestras dependencias listas, nos movemos a nuestro directorio donde vamos a crear una carpeta para guardar nuestro proyecto. y ejecutamos el siguiente comando para crear nuestro proyecto de Laravel:

composer create-project--prefer-dist laravel/laravel nombre-proyecto

Una vez creado nuestro proyecto, accedemos a el:

cd nombre-proyecto/

Ahora vamos a probar si funciona la aplicación correctamente, iniciando el servidor de desarrollo:

php artisan serv

Nos mostrará un enlace a nuestro servidor local, algo parecido a esto:

Starting Laravel development server:http://127.0.0.1:8000

Copiamos el URL en nuestro navegador y nos debe mostrar esta vista:

Y ya estaría configurado nuestro proyecto correctamente.

<h1>Generando nuestra base de datos</h1>

Es necesario configurar nuestras variables de entorno de conexión a nuestra base de datos en el archivo .env, para esto puedes usar tu editor de texto de confianza (Vim, Nano, VS Code, Sublime Text, etc…)

Debemos tener nuestra base de datos creada sin tablas para ejecutar la migración por defecto de Laravel, que permite crear la tabla users

php artisan migrate

Si quieres personalizar tu tabla usuarios, puedes personalizar esto en config/auth.php y app/Models/User.php

<h1>Instalando y configurando JWT</h1>

Vamos a instalar las dependencias de JWT:

composer require tymon/jwt-auth:dev-develop --prefer-source

Debes incluir la siguiente línea en el archivo config/app.php dentro del array de providers

Tymon\JWTAuth\Providers\LaravelServiceProvider::class,

En el mismo archivo, en el array aliases agregamos las siguientes líneas:

'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,

Comprobamos que funcione correctamente con el siguiente comando:

phpartisanjwt:secret

Ahora en la clase User, implementamos los siguientes 2 métodos:

publicfunctiongetJWTIdentifier(){
	return $this->getKey();
}

publicfunctiongetJWTCustomClaims(){
	return [];
}

Es necesario que esta clase implemente JWTSubject

use Tymon\JWTAuth\Contracts\JWTSubject;

classUserextendsAuthenticatableimplementsJWTSubject{

Crearemos un middleware para JWT con el siguiente comando:

phpartisanmake:middlewareJwtMiddleware

Modificamos el nuevo Middleware con las siguientes líneas de código:

<?phpnamespaceApp\Http\Middleware;

useClosure;
useJWTAuth;
useException;
useTymon\JWTAuth\Http\Middleware\BaseMiddleware;

classJwtMiddlewareextendsBaseMiddleware{

    publicfunctionhandle($request, Closure $next){
        try {
            $user = JWTAuth::parseToken()->authenticate();
        } catch (Exception $e) {
            if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
                return response()->json(['status' => 'Token is Invalid']);
            }elseif ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
                return response()->json(['status' => 'Token is Expired']);
            }else{
                return response()->json(['status' => 'Authorization Token not found']);
            }
        }
        return $next($request);
    }
}

Para finalizar este paso, añadimos el nuevo Middleware al Kernel, agregando la siguiente línea al archivo en app/http/Kernel.php en routeMiddleware:

'jwt.verify' => \App\Http\Middleware\JwtMiddleware::class,
<h1>Configurando métodos y rutas</h1>

Ahora procedemos a crear UserController para crear los métodos de registro y autenticación:

phpartisanmake:controllerUserController

Una vez creado en la app/Http/Controllers, procedemos a modificarlo para que quede de la siguiente forma:

<?phpnamespaceApp\Http\Controllers;

    useApp\Models\User;
    useIlluminate\Http\Request;
    useIlluminate\Support\Facades\Hash;
    useIlluminate\Support\Facades\Validator;
    useJWTAuth;
    useTymon\JWTAuth\Exceptions\JWTException;

classUserControllerextendsController{
    publicfunctionauthenticate(Request $request){
    $credentials = $request->only('email', 'password');
    try {
        if (! $token = JWTAuth::attempt($credentials)) {
            return response()->json(['error' => 'invalid_credentials'], 400);
        }
    } catch (JWTException $e) {
        return response()->json(['error' => 'could_not_create_token'], 500);
    }
    return response()->json(compact('token'));
    }

    publicfunctiongetAuthenticatedUser(){
    try {
        if (!$user = JWTAuth::parseToken()->authenticate()) {
                return response()->json(['user_not_found'], 404);
        }
        } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
                return response()->json(['token_expired'], $e->getStatusCode());
        } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
                return response()->json(['token_invalid'], $e->getStatusCode());
        } catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
                return response()->json(['token_absent'], $e->getStatusCode());
        }
        return response()->json(compact('user'));
    }


    publicfunctionregister(Request $request){
            $validator = Validator::make($request->all(), [
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6|confirmed',
        ]);

        if($validator->fails()){
                return response()->json($validator->errors()->toJson(), 400);
        }

        $user = User::create([
            'name' => $request->get('name'),
            'email' => $request->get('email'),
            'password' => Hash::make($request->get('password')),
        ]);

        $token = JWTAuth::fromUser($user);

        return response()->json(compact('user','token'),201);
    }
}

Lo siguiente es indicarle a nuestro aplicación cuales son las rutas a las que se accederán para registrarse e iniciar sesión. Para esto modificaremos el archivo routes/api.php.
Debemos comentar las siguientes lineas:

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});
// Route::middleware('auth:api')->get('/user', function (Request $request) {
//     return $request->user();
// });

Y escribir las siguientes líneas:

Route::post('register', 'App\Http\Controllers\[email protected]');
Route::post('login', 'App\Http\Controllers\[email protected]');

Creamos un group para indicar cuales son las rutas que necesitan autenticación:

Route::group(['middleware' => ['jwt.verify']], function() {

    Route::post('user','App\Http\Controllers\[email protected]');

});

NOTA: Las rutas que guardemos en api, tendrán por defecto la ruta /api

Ya tenemos todo listo para poder registrar usuarios y hacer login.

<h1>Enviando peticiones a nuestro backend</h1>

Podemos usar Postman para realizar una petición en formato JSON con POST hacia la ruta /api/register:

{
    "name" : "randomusername",
    "password" : "strongkey123",
    "password_confirmation" : "strongkey123",
    "email" : "[email protected]"
}


Se puede ver como se registró correctamente nuestro usuario.
El inicio de sesión consiste en enviar las credenciales de acceso y obtener un token, que nos permitirá acceder a rutas que se necesita autenticación. La ruta de login es /api/login usando el método GET

{
    "email" : "[email protected]",
    "password" : "strongkey123"
}

Como último paso es acceder a una ruta protegida con nuestro token de autenticación.
Nuestro token se debe enviar con el header Authorization y colcando la palabra Bearer antes de nuestro token

Authorization: Bearer <nuestroToken>

La ruta para obtener los datos de usuario es /api/user mediante el método POST

Ahora sí, está todo listo.

Ya estás listo para crear tu backend en Laravel 8 para comunicarte con cualquier aplicación que quieras desarrollar.

Te invito a escribir en la caja de comentarios y, NUNCA PARES DE APRENDER

Escribe tu comentario
+ 2
Ordenar por:
3
10640Puntos

excelente. me pase todo el dia buscando esto . no sabia como hacerlo de manera eficiente en el back y quería algo que no fuera tan engorroso. tu tutorial me salvo la vida.
muchas gracias

1
41Puntos

Muy buen tutorial justo lo que buscaba solo una sugerencia en la parte donde mencionas php artisan jwt:secret para mi que soy nuevo en laravel pense que era como una prueba sin embargo revisando la documentacion se refiere a la secret key necesaria para realizar la encriptación creo que valdria la pena especificarlo.

1

Excelente tutorial, muchas gracias, en la definición de la ruta de login se usó el método post, pero después se hace la solicitud a esa ruta con el método get, así que normalmente ¿cuál de los dos métodos se usa para un login?

0
2274Puntos

Está genial! gran aporte y supe fácil de implementar,

Una duda, alguna idea de como podría implementar 2 backends (diferentes frameworks) con jwt token? con que la autentificación la ejecuta laravel, no acabo de como puedo aplicarlo para esto

0

Excelente el tutorial, justo lo que estaba buscando, pero me está dando un error en la recuperación de los datos de user…

“status”: “Authorization Token not found”

Lo seguí hasta
Tymon\JWTAuth\Exceptions\JWTException: The token could not be parsed from the request in file E:\internet\www\pedidosresto\back-resto\vendor\tymon\jwt-auth\src\JWT.php on line 185
Alguna pista ???
Gracias!!

1
un año

Tengo el mismo problema, en el archivo que esta en la ruta
app\http\Middleware\JwtMiddleware.php

<code><?phpnamespaceApp\Http\Middleware;

useClosure;
useIlluminate\Http\Request;
useJWTAuth;
useException;
useTymon\JWTAuth\Http\Middleware\BaseMiddleware;

classJwtMiddlewareextendsBaseMiddleware{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */publicfunctionhandle(Request $request, Closure $next){
        try {
            
            $user = JWTAuth::parseToken()->authenticate();
            
        } catch (Exception $e) {
            if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
                return response()->json(['status' => 'Token is Invalid']);
            }elseif ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
                return response()->json(['status' => 'Token is Expired']);
            }else{
                return response()->json(['status' => 'Authorization Token not found', 'detail' => $e->getMessage(), 'error'=> $e]);
            }
        }
        return $next($request);
    }
}

Asi te debe devolver el problema que tienes, por ejemplo yo en mi tabla de usuarios tengo el problema que no tengo un campo con el nombre id

<code>
{
    "status": "Authorization Token not found",
    "detail": "SQLSTATE[42S22]: [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]El nombre de columna 'id' no es válido. (SQL: select top 1 * from [std_Usuarios] where [id] is null)",
    "error": {
        "errorInfo": [
            "42S22",
            207,
            "[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]El nombre de columna 'id' no es válido.",
            "42000",
            8180,
            "[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]No se puede preparar la instrucción o instrucciones."
        ]
    }
}
0
4Puntos

Excelente aporte, gracias por compartir tu conocimiento, me fue de mucha ayuda, Bendiciones!!!