Lumen Tutorial : How to build a JWT Authenticated API with Lumen
Tags: php, lumen, jwt
Published: 2023-08-20 16:22:45

A JSON web token(JWT) is JSON Object which is used to securely transfer information over the web(between two parties). It can be used for an authentication system and can also be used for information exchange.

The token is mainly composed of header, payload, signature. These three parts are separated by dots(.). JWT defines the structure of information we are sending from one party to the another, and it comes in two forms – Serialized, Deserialized. 

The Serialized approach is mainly used to transfer the data through the network with each request and response. While the deserialized approach is used to read and write data to the web token.


In this tutorial, i would like to show you how to build a JWT Authenticated API with Lumen 8.

Let's code !!


## Install Lumen
 
First step, you have to install new lumen project. If you want to use latest version of lumen, you can run command below : 

```
composer create-project --prefer-dist laravel/lumen blog
```
but, if you want to using specific version of lumen, you can run command below

```
composer create-project laravel/lumen blog "5.*.*"
```
you just have to change main version number (5, 6, 7, 8, etc)

## Make Migration for Database

- Make sure, your env file connected to some database address

```
DB_CONNECTION=mysql
DB_HOST=type-your-host-here
DB_PORT=3306
DB_DATABASE=type-your-db-here
DB_USERNAME=type-your-username
DB_PASSWORD=type-your-password
```

- Create a new migration file, run this command 

```
php artisan make:migration create_users_table
```

- Open migration file, and add code below

```
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

```

- Run the migration

```
php artisan migrate
```

This command, will execute migration file and create new tables in your database.

## Install JWT Package
install jwt-auth via composer 

```
composer require tymon/jwt-auth:*
```

## Generate JWT Secret Key
Generate key secret JWT, run command below

```
php artisan jwt:secret
```

## Setup Folder & Important Config File
 
- Create new folder, named it _config_ (same level with _app_ folder)
- You have to copy file from _vendor/tymon/jwt-auth/config/config.php_ to _config_ folder, and rename file to _jwt.php_

- Next, make new file inside _config_ folder, _auth.php_

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92ix6k02q28j650zfywe.png)

- Open file _app.php_ (bootstrap/app.php), edit and add this code 

```
// Uncomment this line
$app->withFacades();
$app->withEloquent();

// Add this line
$app->configure('jwt');
$app->configure('auth');

//Then uncomment the auth middleware 
$app->routeMiddleware([
    'auth' => App\Http\Middleware\Authenticate::class,
]);

$app->register(App\Providers\AuthServiceProvider::class);

// Add this line in the same file:
$app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class);
```

- Open _auth.php_ (config/auth.php) add this code

```
<?php

return [
    'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],

    'guards' => [
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => \App\Models\User::class
        ]
    ]
];
```

## Create Model
Let's make new file model, User.php (app/models/user.php), add code below : 

```
<?php

namespace App\Models;

use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;

//this is new
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject 
{
    use Authenticatable, Authorizable;

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}
```

## Create Controller
Next, you have to make new controller file, _AuthController_ (app/http/controllers/AuthController), add thic code : 

```
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\User;

class AuthController extends Controller
{

    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login', 'refresh', 'logout']]);
    }
    /**
     * Get a JWT via given credentials.
     *
     * @param  Request  $request
     * @return Response
     */
    public function login(Request $request)
    {

        $this->validate($request, [
            'email' => 'required|string',
            'password' => 'required|string',
        ]);

        $credentials = $request->only(['email', 'password']);

        if (! $token = Auth::attempt($credentials)) {
            return response()->json(['message' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

     /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth()->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth()->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth()->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'user' => auth()->user(),
            'expires_in' => auth()->factory()->getTTL() * 60 * 24
        ]);
    }
}
```

## Setup Route
Open _web.php_ file (routes/web.php), add this code 

```
<?php

/** @var \Laravel\Lumen\Routing\Router $router */

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It is a breeze. Simply tell Lumen the URIs it should respond to
| and give it the Closure to call when that URI is requested.
|
*/

$router->get('/', function () use ($router) {
    return $router->app->version();
});

$router->get('/get-key', function() {
    return \Illuminate\Support\Str::random(32);
});

Route::group([

    'prefix' => 'api'

], function ($router) {
    Route::post('login', 'AuthController@login');
    Route::post('logout', 'AuthController@logout');
    Route::post('refresh', 'AuthController@refresh');
    Route::post('user-profile', 'AuthController@me');

});
```

## Add Encryption key Application
You can't run _php artisan key:generate_ in lumen (like in Laravel), so you have to generate manually by call route /get-key

Open browser, visit this link _http://localhost:8000/get-key_ , copy and paste the key to APP_KEY in env file 

## Refresh ENV Setup 
After you change env file, you have to run 

```
php artisan cache:clear
```

## Running Lumen 
Run lumen with this command 

```
php -S localhost:8000 -t public
```
Open Postman to test your API endpoint's




**_You can clone example code by this link : _** https://gitlab.com/open-for-public/lumen-restfull-api-with-jwt-authentication.git