DEV Community

loading...

Laravel API Authentication/Authorization with JWT

todo1017 profile image Scriptmastership Updated on ・3 min read

Objective

  • API only
  • JWT based Authentication
  • Login with nickname instead email
  • Check Authorization by Middleware (multiple role: admin, user, ...)

Steps

  1. Install Laravel
  2. Prepare Database
  3. Add JWT Auth
  4. Add a route middleware to check user role
  5. Test

1. Install Laravel (7.x)

First, create an empty project using the Laravel CLI.

> composer create-project --prefer-dist laravel/laravel:^7.0
> cd myapi
myapi> php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

2. Prepare Database

The CLI will create some migration files. Just replace the "email" with "nickname" inside the user migration file.

myapi/database/migrations/XXX_create_users_table.php

$table->string('nickname')->unique();
Enter fullscreen mode Exit fullscreen mode

Next, create a database and update the .env file. And run the following command to create database tables.

console

myapi> php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Next, put some users to test the API at the end

console

myapi> php artisan make:seeder UserSeeder
Enter fullscreen mode Exit fullscreen mode

myapi/database/seeds/UserSeeder.php

...
  use Illuminate\Support\Facades\DB;
  use Illuminate\Support\Facades\Hash;
  ...
      public function run() {
          DB::table('users')->insert([
              'name' => 'robert jin',
              'nickname' => 'scriptmastership',
              'password' => Hash::make('admin123'),
              'role' => 'admin'
          ]);
          DB::table('users')->insert([
              'name' => 'test user',
              'nickname' => 'testuser',
              'password' => Hash::make('test1234'),
              'role' => 'user'
          ]);
      }
  ...
Enter fullscreen mode Exit fullscreen mode

console

myapi> php artisan db:seed
Enter fullscreen mode Exit fullscreen mode

3. Add JWT Auth

Install the JWTAuth plugin using composer and generate some required defaults.

console

myapi> composer require tymon/jwt-auth
myapi> php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
myapi> php artisan jwt:secret
Enter fullscreen mode Exit fullscreen mode

Create an empty controller that will handle auth requests.

console

myapi> php artisan make:controller AuthController
Enter fullscreen mode Exit fullscreen mode

Add JWTAuth functions to the user model.

myapi/app/User.php

...
use Tymon\JWTAuth\Contracts\JWTSubject;
...
class User extends Authenticatable implements JWTSubject
...
    /**
     * 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 [];
    }
...
Enter fullscreen mode Exit fullscreen mode

Make Laravel's default guard use the JWTAuth.

myapi/config/auth.php

...
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],
...
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],
Enter fullscreen mode Exit fullscreen mode

Write required api routes and connect it with the AuthController.

myapi/routes/api.php

Route::prefix('auth')->group(function() {
  Route::post('login', 'AuthController@login');
  Route::middleware(['auth:api'])->group(function() {
      Route::post('logout',  'AuthController@logout');
      Route::post('refresh', 'AuthController@refresh');
      Route::post('me', 'AuthController@me');
  });
});
Enter fullscreen mode Exit fullscreen mode

Fill in the AuthController with the following code. Carefully check the login() which handles the "login by nickname".

myapi/app/Http/Controllers/AuthController.php

...
use Illuminate\Support\Facades\Auth;
...
class AuthController extends Controller
    ...
    public function me()
    {
        return response()->json(auth()->user());
    }

    public function login(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'nickname' => 'required|exists:users',
            'password' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'errors' => $validator->errors()
            ], 400);
        }

        $user = User::where('nickname', $request->nickname)->first();
        if (!Hash::check($request->password, $user->password)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }
        $token = auth()->login($user);
        return $this->respondWithToken($token, $user);
    }

    public function logout()
    {
        auth()->logout();
        return response()->json(['message' => 'Successfully logged out']);
    }

    public function refresh()
    {
        return $this->respondWithToken(auth()->refresh());
    }

    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'expires_in' => auth()->factory()->getTTL() * 60
        ]);
    }
...
Enter fullscreen mode Exit fullscreen mode

4. Add a route middleware to check user role

Create a new middleware using Laravel CLI.

console

php artisan make:middleware UserScope
Enter fullscreen mode Exit fullscreen mode

Next, we are going to set permission to every route based on user role. If we use route middleware, we can set the permission directly in the route definition file.

myapi/app/Http/Kernel.php

protected $routeMiddleware = [
  ...
  'userscope' => \App\Http\Middleware\UserScope::class,
];
Enter fullscreen mode Exit fullscreen mode

myapi/app/User.php

public function hasRole($role)
{
    return User::where('role', $role)->first();
}
Enter fullscreen mode Exit fullscreen mode

myapi/app/Http/Middleware/UserScope.php

public function hasRole($role)
{
    return User::where('role', $role)->first();
}
Enter fullscreen mode Exit fullscreen mode

5. Test

First, write some routes and controller functions only for the test purpose.

myapi/routes/api.php

Route::middleware(['auth:api'])->group(function() {
    ...
    Route::post('admin_only', 'AuthController@admin_only')->middleware('userscope:admin');
    Route::post('user_only', 'AuthController@user_only')->middleware('userscope:user');
});
Enter fullscreen mode Exit fullscreen mode

myapi/app/Http/Controllers/AuthController.php

public function admin_only(Request $request)
{
    return response()->json(['message' => 'i am the admin']);
}
public function user_only(Request $request)
{
    return response()->json(['message' => 'i am a user']);
}
Enter fullscreen mode Exit fullscreen mode

We will see how does the authentication and authorization work.

POST api/auth/login

REQUEST BODY {
  "nickname" : "testuser",
  "password" : "test1234"
}

---

RESPONSE BODY {
  "access_token" : "xxx.xxxxxx.xxxx",
  "expires_in" : 3600
}
Enter fullscreen mode Exit fullscreen mode
POST api/auth/user_only

REQUEST HEADER {
  "Authorization": "Bearer xxx.xxxxxx.xxxx"
}
REQUEST BODY {}

---

RESPONSE BODY {
  "message" : "i am a user"
}
Enter fullscreen mode Exit fullscreen mode
POST api/auth/admin_only
REQUEST HEADER {
  "Authorization": "Bearer xxx.xxxxxx.xxxx"
}
REQUEST BODY {}

--------

RESPONSE BODY {
  "error" => "Permission Denied."
}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)

pic
Editor guide