├── database
├── migrations
│ ├── .gitkeep
│ ├── 2021_02_26_092051_create_posts_table.php
│ └── 2021_02_26_094144_create_users_table.php
├── seeders
│ └── DatabaseSeeder.php
└── factories
│ ├── PostFactory.php
│ └── UserFactory.php
├── resources
└── views
│ └── .gitkeep
├── app
├── Console
│ ├── Commands
│ │ └── .gitkeep
│ └── Kernel.php
├── Events
│ ├── Event.php
│ └── ExampleEvent.php
├── Http
│ ├── Controllers
│ │ ├── Controller.php
│ │ ├── ExampleController.php
│ │ ├── PostController.php
│ │ └── AuthController.php
│ └── Middleware
│ │ ├── ExampleMiddleware.php
│ │ └── Authenticate.php
├── Models
│ ├── Post.php
│ └── User.php
├── Providers
│ ├── AppServiceProvider.php
│ ├── EventServiceProvider.php
│ └── AuthServiceProvider.php
├── Jobs
│ ├── ExampleJob.php
│ └── Job.php
├── Listeners
│ └── ExampleListener.php
└── Exceptions
│ └── Handler.php
├── storage
├── app
│ └── .gitignore
├── logs
│ └── .gitignore
└── framework
│ ├── views
│ └── .gitignore
│ └── cache
│ ├── data
│ └── .gitignore
│ └── .gitignore
├── .gitignore
├── .styleci.yml
├── .editorconfig
├── .env.example
├── tests
├── TestCase.php
└── ExampleTest.php
├── config
└── auth.php
├── phpunit.xml
├── public
├── .htaccess
└── index.php
├── README.md
├── routes
└── web.php
├── artisan
├── composer.json
└── bootstrap
└── app.php
/database/migrations/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/views/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/Console/Commands/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | /.idea
3 | Homestead.json
4 | Homestead.yaml
5 | .env
6 | .phpunit.result.cache
7 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | php:
2 | preset: laravel
3 | disabled:
4 | - unused_use
5 | js: true
6 | css: true
7 |
--------------------------------------------------------------------------------
/app/Events/Event.php:
--------------------------------------------------------------------------------
1 | call('UsersTableSeeder');
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Lumen
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://localhost
6 | APP_TIMEZONE=UTC
7 |
8 | LOG_CHANNEL=stack
9 | LOG_SLACK_WEBHOOK_URL=
10 |
11 | DB_CONNECTION=mysql
12 | DB_HOST=127.0.0.1
13 | DB_PORT=3306
14 | DB_DATABASE=homestead
15 | DB_USERNAME=homestead
16 | DB_PASSWORD=secret
17 |
18 | CACHE_DRIVER=file
19 | QUEUE_CONNECTION=sync
20 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | $this->faker->sentence,
16 | 'body' => $this->faker->paragraph
17 | ];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/config/auth.php:
--------------------------------------------------------------------------------
1 | [
5 | 'guard' => 'api',
6 | 'passwords' => 'users',
7 | ],
8 |
9 | 'guards' => [
10 | 'api' => [
11 | 'driver' => 'jwt',
12 | 'provider' => 'users',
13 | ],
14 | ],
15 |
16 | 'providers' => [
17 | 'users' => [
18 | 'driver' => 'eloquent',
19 | 'model' => \App\Models\User::class
20 | ]
21 | ]
22 | ];
23 |
--------------------------------------------------------------------------------
/tests/ExampleTest.php:
--------------------------------------------------------------------------------
1 | get('/');
16 |
17 | $this->assertEquals(
18 | $this->app->version(), $this->response->getContent()
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/Providers/EventServiceProvider.php:
--------------------------------------------------------------------------------
1 | [
16 | \App\Listeners\ExampleListener::class,
17 | ],
18 | ];
19 | }
20 |
--------------------------------------------------------------------------------
/app/Listeners/ExampleListener.php:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ./tests
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Handle Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->name,
26 | 'email' => $this->faker->unique()->safeEmail,
27 | 'password' => app('hash')->make('123456')
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/Jobs/Job.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('title');
19 | $table->text('body');
20 | $table->timestamps();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | *
27 | * @return void
28 | */
29 | public function down()
30 | {
31 | Schema::dropIfExists('posts');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/database/migrations/2021_02_26_094144_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->string('password');
21 | $table->timestamps();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | *
28 | * @return void
29 | */
30 | public function down()
31 | {
32 | Schema::dropIfExists('users');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Develop REST API with Lumen and JWT authentication
2 |
3 | # Installation
4 |
5 | 1. Clone this repo
6 |
7 | ```
8 | git clone https://github.com/samironbarai/lumen-rest-api-jwt-auth.git
9 | ```
10 |
11 | 2. Install composer packages
12 |
13 | ```
14 | cd lumen-rest-api-jwt-auth
15 | $ composer install
16 | ```
17 |
18 | 3. Create and setup .env file
19 |
20 | ```
21 | make a copy of .env.example
22 | $ copy .env.example .env
23 | $ php artisan key:generate
24 | put database credentials in .env file
25 | $ php artisan jwt:secret
26 | ```
27 |
28 | 4. Migrate and insert records
29 |
30 | ```
31 | $ php artisan migrate
32 | ```
33 |
34 | To test application follow the tutorial bellow.
35 | Click on the image bellow to see YouTube video.
36 |
37 | [](https://www.youtube.com/watch?v=qG0djDRXV_g)
38 |
39 | Please visit my website.
40 | [samironbarai.com](https://samironbarai.com/)
41 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | run();
29 |
--------------------------------------------------------------------------------
/app/Http/Middleware/Authenticate.php:
--------------------------------------------------------------------------------
1 | auth = $auth;
26 | }
27 |
28 | /**
29 | * Handle an incoming request.
30 | *
31 | * @param \Illuminate\Http\Request $request
32 | * @param \Closure $next
33 | * @param string|null $guard
34 | * @return mixed
35 | */
36 | public function handle($request, Closure $next, $guard = null)
37 | {
38 | if ($this->auth->guard($guard)->guest()) {
39 | return response('Unauthorized.', 401);
40 | }
41 |
42 | return $next($request);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Providers/AuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | app['auth']->viaRequest('api', function ($request) {
34 | if ($request->input('api_token')) {
35 | return User::where('api_token', $request->input('api_token'))->first();
36 | }
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 | get('/', function () use ($router) {
17 | return $router->app->version();
18 | });
19 |
20 |
21 | $router->group(['prefix' => 'api'], function () use ($router) {
22 | $router->post('/register', 'AuthController@register');
23 | $router->post('/login', 'AuthController@login');
24 |
25 | $router->group(['middleware' => 'auth'], function () use ($router) {
26 | $router->post('/logout', 'AuthController@logout');
27 | $router->get('/posts', 'PostController@index');
28 | $router->post('/posts', 'PostController@store');
29 | $router->put('/posts/{id}', 'PostController@update');
30 | $router->delete('/posts/{id}', 'PostController@destroy');
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(
32 | 'Illuminate\Contracts\Console\Kernel'
33 | );
34 |
35 | exit($kernel->handle(new ArgvInput, new ConsoleOutput));
36 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laravel/lumen",
3 | "description": "The Laravel Lumen Framework.",
4 | "keywords": ["framework", "laravel", "lumen"],
5 | "license": "MIT",
6 | "type": "project",
7 | "require": {
8 | "php": "^7.3|^8.0",
9 | "flipbox/lumen-generator": "^8.2",
10 | "laravel/lumen-framework": "^8.0",
11 | "tymon/jwt-auth": "^1.0"
12 | },
13 | "require-dev": {
14 | "fakerphp/faker": "^1.9.1",
15 | "mockery/mockery": "^1.3.1",
16 | "phpunit/phpunit": "^9.3"
17 | },
18 | "autoload": {
19 | "psr-4": {
20 | "App\\": "app/",
21 | "Database\\Factories\\": "database/factories/",
22 | "Database\\Seeders\\": "database/seeders/"
23 | }
24 | },
25 | "autoload-dev": {
26 | "classmap": [
27 | "tests/"
28 | ]
29 | },
30 | "config": {
31 | "preferred-install": "dist",
32 | "sort-packages": true,
33 | "optimize-autoloader": true
34 | },
35 | "minimum-stability": "dev",
36 | "prefer-stable": true,
37 | "scripts": {
38 | "post-root-package-install": [
39 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/Models/User.php:
--------------------------------------------------------------------------------
1 | getKey();
43 | }
44 |
45 | /**
46 | * Return a key value array, containing any custom claims to be added to the JWT.
47 | *
48 | * @return array
49 | */
50 | public function getJWTCustomClaims()
51 | {
52 | return [];
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/Exceptions/Handler.php:
--------------------------------------------------------------------------------
1 | title = $request->title;
20 | $post->body = $request->body;
21 |
22 | if ($post->save()) {
23 | return response()->json(['status' => 'success', 'message' => 'Post created successfully']);
24 | }
25 | } catch (\Exception $e) {
26 | return response()->json(['status' => 'error', 'message' => $e->getMessage()]);
27 | }
28 | }
29 |
30 | public function update(Request $request, $id)
31 | {
32 | try {
33 | $post = Post::findOrFail($id);
34 | $post->title = $request->title;
35 | $post->body = $request->body;
36 |
37 | if ($post->save()) {
38 | return response()->json(['status' => 'success', 'message' => 'Post updated successfully']);
39 | }
40 | } catch (\Exception $e) {
41 | return response()->json(['status' => 'error', 'message' => $e->getMessage()]);
42 | }
43 | }
44 |
45 | public function destroy($id)
46 | {
47 | try {
48 | $post = Post::findOrFail($id);
49 |
50 | if ($post->delete()) {
51 | return response()->json(['status' => 'success', 'message' => 'Post deleted successfully']);
52 | }
53 | } catch (\Exception $e) {
54 | return response()->json(['status' => 'error', 'message' => $e->getMessage()]);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/Http/Controllers/AuthController.php:
--------------------------------------------------------------------------------
1 | name;
13 | $email = $request->email;
14 | $password = $request->password;
15 |
16 | // Check if field is empty
17 | if (empty($name) or empty($email) or empty($password)) {
18 | return response()->json(['status' => 'error', 'message' => 'You must fill all the fields']);
19 | }
20 |
21 | // Check if email is valid
22 | if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
23 | return response()->json(['status' => 'error', 'message' => 'You must enter a valid email']);
24 | }
25 |
26 | // Check if password is greater than 5 character
27 | if (strlen($password) < 6) {
28 | return response()->json(['status' => 'error', 'message' => 'Password should be min 6 character']);
29 | }
30 |
31 | // Check if user already exist
32 | if (User::where('email', '=', $email)->exists()) {
33 | return response()->json(['status' => 'error', 'message' => 'User already exists with this email']);
34 | }
35 |
36 | // Create new user
37 | try {
38 | $user = new User();
39 | $user->name = $request->name;
40 | $user->email = $request->email;
41 | $user->password = app('hash')->make($request->password);
42 |
43 | if ($user->save()) {
44 | return $this->login($request);
45 | }
46 | } catch (\Exception $e) {
47 | return response()->json(['status' => 'error', 'message' => $e->getMessage()]);
48 | }
49 | }
50 |
51 | /**
52 | * Log the user out (Invalidate the token).
53 | *
54 | * @return \Illuminate\Http\JsonResponse
55 | */
56 | public function logout()
57 | {
58 | auth()->logout();
59 |
60 | return response()->json(['message' => 'Successfully logged out']);
61 | }
62 |
63 | public function login(Request $request)
64 | {
65 | $email = $request->email;
66 | $password = $request->password;
67 |
68 | // Check if field is empty
69 | if (empty($email) or empty($password)) {
70 | return response()->json(['status' => 'error', 'message' => 'You must fill all the fields']);
71 | }
72 |
73 | $credentials = request(['email', 'password']);
74 |
75 | if (!$token = auth()->attempt($credentials)) {
76 | return response()->json(['error' => 'Unauthorized'], 401);
77 | }
78 |
79 | return $this->respondWithToken($token);
80 | }
81 |
82 | /**
83 | * Get the token array structure.
84 | *
85 | * @param string $token
86 | *
87 | * @return \Illuminate\Http\JsonResponse
88 | */
89 | protected function respondWithToken($token)
90 | {
91 | return response()->json([
92 | 'access_token' => $token,
93 | 'token_type' => 'bearer',
94 | 'expires_in' => auth()->factory()->getTTL() * 60
95 | ]);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | bootstrap();
8 |
9 | date_default_timezone_set(env('APP_TIMEZONE', 'UTC'));
10 |
11 | /*
12 | |--------------------------------------------------------------------------
13 | | Create The Application
14 | |--------------------------------------------------------------------------
15 | |
16 | | Here we will load the environment and create the application instance
17 | | that serves as the central piece of this framework. We'll use this
18 | | application as an "IoC" container and router for this framework.
19 | |
20 | */
21 |
22 | $app = new Laravel\Lumen\Application(
23 | dirname(__DIR__)
24 | );
25 |
26 | // $app->withFacades();
27 |
28 | $app->withEloquent();
29 |
30 | /*
31 | |--------------------------------------------------------------------------
32 | | Register Container Bindings
33 | |--------------------------------------------------------------------------
34 | |
35 | | Now we will register a few bindings in the service container. We will
36 | | register the exception handler and the console kernel. You may add
37 | | your own bindings here if you like or you can make another file.
38 | |
39 | */
40 |
41 | $app->singleton(
42 | Illuminate\Contracts\Debug\ExceptionHandler::class,
43 | App\Exceptions\Handler::class
44 | );
45 |
46 | $app->singleton(
47 | Illuminate\Contracts\Console\Kernel::class,
48 | App\Console\Kernel::class
49 | );
50 |
51 | /*
52 | |--------------------------------------------------------------------------
53 | | Register Config Files
54 | |--------------------------------------------------------------------------
55 | |
56 | | Now we will register the "app" configuration file. If the file exists in
57 | | your configuration directory it will be loaded; otherwise, we'll load
58 | | the default version. You may register other files below as needed.
59 | |
60 | */
61 |
62 | $app->configure('auth');
63 |
64 | /*
65 | |--------------------------------------------------------------------------
66 | | Register Middleware
67 | |--------------------------------------------------------------------------
68 | |
69 | | Next, we will register the middleware with the application. These can
70 | | be global middleware that run before and after each request into a
71 | | route or middleware that'll be assigned to some specific routes.
72 | |
73 | */
74 |
75 | // $app->middleware([
76 | // App\Http\Middleware\ExampleMiddleware::class
77 | // ]);
78 |
79 | $app->routeMiddleware([
80 | 'auth' => App\Http\Middleware\Authenticate::class,
81 | ]);
82 |
83 | /*
84 | |--------------------------------------------------------------------------
85 | | Register Service Providers
86 | |--------------------------------------------------------------------------
87 | |
88 | | Here we will register all of the application's service providers which
89 | | are used to bind services into the container. Service providers are
90 | | totally optional, so you are not required to uncomment this line.
91 | |
92 | */
93 |
94 | // $app->register(App\Providers\AppServiceProvider::class);
95 | $app->register(App\Providers\AuthServiceProvider::class);
96 | // $app->register(App\Providers\EventServiceProvider::class);
97 | $app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);
98 | $app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class);
99 |
100 | /*
101 | |--------------------------------------------------------------------------
102 | | Load The Application Routes
103 | |--------------------------------------------------------------------------
104 | |
105 | | Next we will include the routes file so that they can all be added to
106 | | the application. This will provide all of the URLs the application
107 | | can respond to, as well as the controllers that may handle them.
108 | |
109 | */
110 |
111 | $app->router->group([
112 | 'namespace' => 'App\Http\Controllers',
113 | ], function ($router) {
114 | require __DIR__ . '/../routes/web.php';
115 | });
116 |
117 | return $app;
118 |
--------------------------------------------------------------------------------