Lumen is the perfect solution for building micro-services based on PHP. In this tutorial, we will build a simple and secure REST API. At the end of this tutorial, you should be able to build production-ready JWT (authentication) template for lumen which will help you build your own APIs. So, let’s get started!
In this tutorial we will use WAMP to host our micro-service. We assume that you have already installed the composer and you are familiar with it. Make sure you have also installed lumen CLI using the following CMD command.
composer global require "laravel/lumen-installer"
Installation of Lumen framework
- Navigate to the www directory of WAMP and delete any file/folder resides there:
C:\wamp64\www
- Open CMD there and type (also the dot)
composer create-project --prefer-dist laravel/lumen .
- Create a new database and assing a user using the phpmyadmin. For this example our database the user and the password will named
homestead
. Afterwards open the .env file found in the root directory of the lumen framework and assign the correct fields like below:
(APP_KEY, DB_*) Beware that the APP_KEY should be a random string and will ensure the security of the application.
APP_NAME=Lumen
APP_ENV=local
APP_KEY=eyJ0eXai34567AQiLCJhbGciOiJIUzI1NiJ9
APP_DEBUG=true
APP_URL=http://localhost
APP_TIMEZONE=UTC
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=homestead
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
- Create a .htaccess file (below) in the root directory of the framework to point the public/ floder as initial path and allow the lumen framwork function correctly.
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
- Check if lumen is actually working correcty: Open your preferred browser and go to
http://localhost
. If everything is ok, you should be able to see:
Lumen (7.0.5) (Laravel Components ^7.0)
Install required packages
- To complete our tutorial we will need some extra packages to be installed. The configuration of the packages will be done later on in this article. By using the following two CMD commands you will have all the dependecies that will be needed:
composer require chuckrincon/lumen-config-discover
composer require tymon/jwt-auth:dev-develop
- Afterwards, we need to create a new folder in our root directory named
config
. In this folder we can easily add all the configuration files we would like to use and thanks to the config-discover package, the framework will automatically load and use them. In our root directory we need to use the following command:
mkdir config
- To make things work we need alter a bit the
/bootstrap/app.php
file.
– Activate the Eloquent and Facades.
– Register the Authentication middleware.
– Register the App, Auth, Event, Tymon\JWTAuth, Chuckrincon\LumenConfigDiscover service providers.
<?php
require_once __DIR__.'/../vendor/autoload.php';
(new Laravel\Lumen\Bootstrap\LoadEnvironmentVariables(
dirname(__DIR__)
))->bootstrap();
date_default_timezone_set(env('APP_TIMEZONE', 'UTC'));
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| Here we will load the environment and create the application instance
| that serves as the central piece of this framework. We'll use this
| application as an "IoC" container and router for this framework.
|
*/
$app = new Laravel\Lumen\Application(
dirname(__DIR__)
);
$app->withFacades();
$app->withEloquent();
/*
|--------------------------------------------------------------------------
| Register Container Bindings
|--------------------------------------------------------------------------
|
| Now we will register a few bindings in the service container. We will
| register the exception handler and the console kernel. You may add
| your own bindings here if you like or you can make another file.
|
*/
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
/*
|--------------------------------------------------------------------------
| Register Config Files
|--------------------------------------------------------------------------
|
| Now we will register the "app" configuration file. If the file exists in
| your configuration directory it will be loaded; otherwise, we'll load
| the default version. You may register other files below as needed.
|
*/
$app->configure('app');
/*
|--------------------------------------------------------------------------
| Register Middleware
|--------------------------------------------------------------------------
|
| Next, we will register the middleware with the application. These can
| be global middleware that run before and after each request into a
| route or middleware that'll be assigned to some specific routes.
|
*/
// $app->middleware([
// App\Http\Middleware\ExampleMiddleware::class
// ]);
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);
/*
|--------------------------------------------------------------------------
| Register Service Providers
|--------------------------------------------------------------------------
|
| Here we will register all of the application's service providers which
| are used to bind services into the container. Service providers are
| totally optional, so you are not required to uncomment this line.
|
*/
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
$app->register(App\Providers\EventServiceProvider::class);
$app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class);
$app->register(Chuckrincon\LumenConfigDiscover\DiscoverServiceProvider::class);
/*
|--------------------------------------------------------------------------
| Load The Application Routes
|--------------------------------------------------------------------------
|
| Next we will include the routes file so that they can all be added to
| the application. This will provide all of the URLs the application
| can respond to, as well as the controllers that may handle them.
|
*/
$app->router->group([
'namespace' => 'App\Http\Controllers',
], function ($router) {
require __DIR__.'/../routes/web.php';
});
return $app;
- Generate the JWT secret by typing the following command in the CMD
php artisan jwt:secret
Add configuration files
- In our example we will need to create the auth.php configuration file in the
/config
folder
<?php
return [
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\Models\User::class
]
]
];
Create users table
- At the time of this writing, Lumen supports four database systems. In this tutorial we are making use of MySQL. Let’s create a migration for the
users
table where all the basic information regarding the users and the token authorization will be held. With the following command a new file at the database/migrations will be created.
php artisan make:migration create_users_table
- Now we need to fill the generated file inside the folder
database/migrations
with the data fields we want.
<?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->increments('id');
$table->string('username')->unique();
$table->string('password');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
}
- And to proceed with the table creation we have to initiate the migration using the following command:
php artisan migrate
- Afterwards we will need to alter the
(or /app/Models/User.php) class as follows:/app/User.php
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
use Laravel\Lumen\Auth\Authorizable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
{
use Authenticatable, Authorizable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'username',
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'password',
];
/**
* 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 [];
}
}
Implement the Controllers
- Now, we have to alter the
\app\Http\Controllers\AuthController.php
, and include the registration and login methods as well as any other method that we would like to use.
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use App\Models\User;
class AuthController extends Controller
{
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login','register']]);
}
/**
* Store a new user.
*
* @param Request $request
* @return Response
*/
public function register(Request $request)
{
//validate incoming request
$this->validate($request, [
'username' => 'required|string|unique:users',
'password' => 'required|confirmed',
]);
try
{
$user = new User;
$user->username= $request->input('username');
$user->password = app('hash')->make($request->input('password'));
$user->save();
return response()->json( [
'entity' => 'users',
'action' => 'create',
'result' => 'success'
], 201);
}
catch (\Exception $e)
{
return response()->json( [
'entity' => 'users',
'action' => 'create',
'result' => 'failed'
], 409);
}
}
/**
* Get a JWT via given credentials.
*
* @param Request $request
* @return Response
*/
public function login(Request $request)
{
//validate incoming request
$this->validate($request, [
'username' => 'required|string',
'password' => 'required|string',
]);
$credentials = $request->only(['username', 'password']);
if (! $token = Auth::attempt($credentials)) {
return response()->json(['message' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* Get user details.
*
* @param Request $request
* @return Response
*/
public function me()
{
return response()->json(auth()->user());
}
}
- Moreover, we have to alter the
\app\Http\Controllers\Controller.php
, and include the global method respondWithToken.
<?php
namespace App\Http\Controllers;
use Laravel\Lumen\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Auth;
class Controller extends BaseController
{
public function respondWithToken($token)
{
return response()->json([
'token' => $token,
'token_type' => 'bearer',
'expires_in' => null
], 200);
}
}
Add routes
- Now that we have the users table we need to provide some basic functionalities. The most vitals are the users registration/login. As indicated in the previous code block,
AuthController
is our controller class and theregister
,login
,me
are methods of that class. To add them in our routes list, locateroutes/web.php
and insert the needed code as seen below:
<?php
/*
|--------------------------------------------------------------------------
| 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->group(['middleware' => 'auth','prefix' => 'api'], function ($router)
{
$router->get('me', 'AuthController@me');
});
$router->group(['prefix' => 'api'], function () use ($router)
{
$router->post('register', 'AuthController@register');
$router->post('login', 'AuthController@login');
});
Validate our implementation
- To validate our implementation we can use POSTMAN. As i first step we will validate the
registration
like the following screenshot:
- Secondly, we will validate the
login
:
- Finally, we can test the me endpoint (and ensure that only authorized access is allowed)
hello,
i’m getting ” Object not found! The requested URL was not found on this server. ” when I try Postman. happens with all the routes.
Hi, Could you please verify that you performed all the mentioned steps correctly and give me a bit more info about your issue? If you open your browser and type http://localhost do you get the lumen version “Lumen (7.0.5) (Laravel Components ^7.0)” ?
Hello, I tested this api using postman, but it is reporting this error at api/login:
I’m use laravel/lumen-framework (v7.1.4).
Please help
Hello, I tested this api using postman, but it is reporting this error at api/login:
“Argument 1 passed to Tymon\JWTAuth\JWTGuard::login() must be an instance of Tymon\JWTAuth\Contracts\JWTSubject, instance of App\User given, called in H:\sourcecode\lumen7-jwt\vendor\tymon\jwt-auth\src\JWTGuard.php on line 127 (500 Internal Server Error)”
I’m use laravel/lumen-framework (v7.1.4).
Please help
Hello, this tutorial is based on Lumen version 7.0.5 and i sincerely do not know if the 7.1.x version is compatible. When i have some free time i will test the 7.1.x version and i’ll let you know. Thanks
You must implement JWTSubject from User model.
Hello, apologies for late reply, turned out that it’s related with Arch Linux’s apache mod_write configurations. I must have configured something wrongly or missed something in the configs. I moved the project to Ubuntu and it’s working flawlessly. Thank you so much.
I’m really happy you found this tutorial helpful!
Hi,
When you already written an auth middleware in route group, do you really need to call it from auth controller constructor ?
I think you shouldn’t it will reduce a small amount of performance. It is best if you put it in route group only, easy to manage, which route use auth or not.
its not recommend for Last Laravel & Lumen, Php version
i’m using Lumen (7.2.1) (Laravel Components ^7.0), Php 7.3
the tymon jwp package is not write a new file documen that you want in /bootstrap/app.php :
app->register(Chuckrincon\LumenConfigDiscover\DiscoverServiceProvider::class);
when you execute command “php artisan jwt:secret” it will return class not found for two line in app.php
and this issues still not resolved from past year 2017,
but
thank you for your share exeperience
Hello,
Any news or new tutorial to add jwt to lumen 8?
I saw that in some versions of lumen 7.1 ^ there were some changes
Hi..! The tutorial now also works for the Lumen 8. The main change is the User Model path, from /app/User.php to /app/Models/User.php
[03-Mar-2021 15:43:37 UTC] PHP Fatal error: Uncaught Error: Call to undefined function app_path() in Command line code:1
Stack trace:
#0 {main}
thrown in Command line code on line 1
Getting the above on Lumen 8.2.3 after completing the above.
Thanks. Confirm works on Lumen 8.2.3 👍 (had a typo earlier which was giving me the “Uncaught Error: Call to undefined function app_path()” which sent me down a rabbit hole).
How can i achieve above using different table (customer) instead of user table.
I’ve tried, but it throwing invalid user error