[Laravel] Xác thực API với Laravel 5.6 Passport Authentication (Part1) (ok)

https://viblo.asia/p/laravel-xac-thuc-api-voi-laravel-56-passport-authentication-part1-Do754p8V5M6

Ví dụ đã thực hiện thành công

Content-Type: application/json
X-Requested-With: XMLHttpRequest

C:\xampp\htdocs\auth\app\Http\Controllers\AuthController.php

name: Lionel 3
email: lionel3@gmail.com
password: 123456
password_confirmation: 123456
{
    "email": "administrator@gmail.com",
    "password": "123456",
    "remember_me": true
}
{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNzAxYmQ1MDViYTgwMTc1ZjNiZjRkNzRkNmE0NjA1MDNmMDQxMzVmMzIwOTk5YzM5MGZlYWNiNmMwZTc4MGU1M2NjYjA4NWUzZDViMTBiYzYiLCJpYXQiOjE1OTU4MzUzODUsIm5iZiI6MTU5NTgzNTM4NSwiZXhwIjoxNjI3MzcxMzg1LCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.bRXVOoH2qhTZ7ge_QDO9CA6QPJTT5HPUjQMxx1oGkJXlBdjRDWO46oUv3njfdc783pKa8XvhCRnKgR1DCvRrF6znXkWnkbUwFpahmd_k8uQmh4MsLslsJZuS0v0Vjd-uMwef6swGsDOV26NhEglM07SfpvBMhepDBBEly9hPQXBUbAgJ3_XlZr4jF6bW30XvBtfuV1wVtB2UMQV-zxcdIs5_OS9-Z5j7xIvosowzeoz61WBlg-863f4mH_cpeCOrat6lhlD0FX5ZzvIqqe53qUO6hxPR_9n5qATiqQ4dkZXLWXIMrRLup85a2Qk7I0mJZM1Ao9kJgLlaAkRvX1JBxmGtOW7f_CB5z789yUiDnfWn5ljE9GZ0BnydfH6QO0dVmvk0S48VKhUAjrlu--IbF1pcW1LgXkb7WXAL5cIHpi5F5kRTtQO9HOgKeO-BFi9ylMbIZ1RBbv-n-8W-a6rybhOedTgpkWO0O59c9wTzargR0OKqJAJeubA-LSn_Li-HisR0eaBhIG93ENtE6HcfT_voI7b27mg7SJrwGMnZ4zPPF8ov78qaq4ou0vXevV8x8akRXEXKlRXnhC2B23w0Iwg9opbkphAvK73XwpxL6gmpA3pSjhd0rbFQe2Hbd2VUAcQwkz8vMkkB7aB3eA8SFABjSsd1goPi0FlTADHF2jo",
    "token_type": "Bearer",
    "expires_at": "2020-08-03 07:36:25"
}

Chú ý: Mỗi lần đăng nhập nó sẽ trả cho ta một Bearer khác nhau :)

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNzAxYmQ1MDViYTgwMTc1ZjNiZjRkNzRkNmE0NjA1MDNmMDQxMzVmMzIwOTk5YzM5MGZlYWNiNmMwZTc4MGU1M2NjYjA4NWUzZDViMTBiYzYiLCJpYXQiOjE1OTU4MzUzODUsIm5iZiI6MTU5NTgzNTM4NSwiZXhwIjoxNjI3MzcxMzg1LCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.bRXVOoH2qhTZ7ge_QDO9CA6QPJTT5HPUjQMxx1oGkJXlBdjRDWO46oUv3njfdc783pKa8XvhCRnKgR1DCvRrF6znXkWnkbUwFpahmd_k8uQmh4MsLslsJZuS0v0Vjd-uMwef6swGsDOV26NhEglM07SfpvBMhepDBBEly9hPQXBUbAgJ3_XlZr4jF6bW30XvBtfuV1wVtB2UMQV-zxcdIs5_OS9-Z5j7xIvosowzeoz61WBlg-863f4mH_cpeCOrat6lhlD0FX5ZzvIqqe53qUO6hxPR_9n5qATiqQ4dkZXLWXIMrRLup85a2Qk7I0mJZM1Ao9kJgLlaAkRvX1JBxmGtOW7f_CB5z789yUiDnfWn5ljE9GZ0BnydfH6QO0dVmvk0S48VKhUAjrlu--IbF1pcW1LgXkb7WXAL5cIHpi5F5kRTtQO9HOgKeO-BFi9ylMbIZ1RBbv-n-8W-a6rybhOedTgpkWO0O59c9wTzargR0OKqJAJeubA-LSn_Li-HisR0eaBhIG93ENtE6HcfT_voI7b27mg7SJrwGMnZ4zPPF8ov78qaq4ou0vXevV8x8akRXEXKlRXnhC2B23w0Iwg9opbkphAvK73XwpxL6gmpA3pSjhd0rbFQe2Hbd2VUAcQwkz8vMkkB7aB3eA8SFABjSsd1goPi0FlTADHF2jo

http://auth.com/api/auth/user

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiOWNkYmVkOGI5OWU2ODk0MjM2NzIwYmRjMzcyYmY4NGJhZjkzOGI4ZGI5MDVhYTY0YjY1MjVjM2VkNTIxNzdlYTA3NTMyYmQ1YThmYjM1N2EiLCJpYXQiOjE1OTU4MzQ3OTUsIm5iZiI6MTU5NTgzNDc5NSwiZXhwIjoxNjI3MzcwNzk1LCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.VhyovRoHuZoAgJ-k2FQ2p9J8zM9rRd6SRa7EiYOsxfmZI6dlJuo7WRCtNO24Brrztvhn3AQQUjjv7rBbGNEGm3JNHSqnHytfdG1WM6GxAXpKWl8k9u29rEkd796xq6q10rY44dUop1EWHXrHgW2hoEBtUycOYRHOZrQJNz9qb-ah3HKQKSXOdfepPVqQIkYBiaMSwRaoSUfF8gp-fP0Li9ZQn5zUytr3PNZ-9AR6PJMvZWgSENE5FrFdvrXNFF7_MbJvpwYhhe1dkEodczyziZKusJBZgd7ROsyn3ina4JiiiFhwaMywhhHHCQY27ZRmRVgpyCVJZsxtuQ90t08f13kywaVsBw0gm9SeZW_wOuZKDnYSiPlbnJuO9DYxt9EgBFH2IC_XXddzK1r6gKLPoVd__uQwJpl6AZrRZXQNuW8qSrdY9UmM-F4MqODNxCabRIS_woKQmGmciIXD4w665ttADrLuxJy7sstk6vXiqLAfZ8U6tgHlELTrgi3HDxvd6d1fWAaKmRFUB40lvV5i-Y4mPqsOFs-2tpe4kmY4kCjDjGKWgU-zdJnnIcpBV60ucgHbTF86PnZQU0la9L1c_DkZvwd1uwS6ETo-fGnvJJbC6f-ngUy_-7-52FE5rmZK7ljewC8G15Ts3odyQJ0YjLg75NTxvAy4TL0_m1KBJog
{
    "id": 3,
    "name": "Administrator",
    "email": "administrator@gmail.com",
    "email_verified_at": null,
    "created_at": "2020-07-27T02:37:12.000000Z",
    "updated_at": "2020-07-27T02:37:12.000000Z"
}

C:\xampp\htdocs\auth\app\Http\Controllers\AuthController.php

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\User;
class AuthController extends Controller {
  /**
   * Create user
   *
   * @param  [string] name
   * @param  [string] email
   * @param  [string] password
   * @param  [string] password_confirmation
   * @return [string] message
   */
  public function signup(Request $request) {
    $request->validate([
      'name'     => 'required|string',
      'email'    => 'required|string|email|unique:users',
      'password' => 'required|string|confirmed',
    ]);
    $user = new User([
      'name'     => $request->name,
      'email'    => $request->email,
      'password' => bcrypt($request->password),
    ]);
    $user->save();
    return response()->json(['message' => 'Successfully created user!'], 201);
  }
  /**
   * Login user and create token
   *
   * @param  [string] email
   * @param  [string] password
   * @param  [boolean] remember_me
   * @return [string] access_token
   * @return [string] token_type
   * @return [string] expires_at
   */
  public function login(Request $request) {
    $request->validate([
      'email'       => 'required|string|email',
      'password'    => 'required|string',
      'remember_me' => 'boolean',
    ]);
    $credentials = request(['email', 'password']);
    if (!Auth::attempt($credentials)) {
      return response()->json(['message' => 'Unauthorized'], 401);
    }
    $user        = $request->user();
    $tokenResult = $user->createToken('Personal Access Token');
    $token       = $tokenResult->token;
    if ($request->remember_me) {
      $token->expires_at = Carbon::now()->addWeeks(1);
    }
    $token->save();
    return response()->json([
      'access_token' => $tokenResult->accessToken,
      'token_type'   => 'Bearer',
      'expires_at'   => Carbon::parse(
        $tokenResult->token->expires_at
      )->toDateTimeString(),
    ]);
  }
  /**
   * Logout user (Revoke the token)
   *
   * @return [string] message
   */
  public function logout(Request $request) {
    $request->user()->token()->revoke();
    return response()->json([
      'message' => 'Successfully logged out',
    ]);
  }
  /**
   * Get the authenticated User
   *
   * @return [json] user object
   */
  public function user(Request $request) {
    return response()->json($request->user());
  }
}

C:\xampp\htdocs\auth\app\User.php

<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable {
  use Notifiable, HasApiTokens;
  /**
   * The attributes that are mass assignable.
   *
   * @var array
   */
  protected $fillable = [
    'name', 'email', 'password',
  ];
  /**
   * The attributes that should be hidden for arrays.
   *
   * @var array
   */
  protected $hidden = [
    'password', 'remember_token',
  ];
  /**
   * The attributes that should be cast to native types.
   *
   * @var array
   */
  protected $casts = [
    'email_verified_at' => 'datetime',
  ];
}

C:\xampp\htdocs\auth\routes\api.php

<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::middleware('auth:api')->get('/user', function (Request $request) {
  return $request->user();
});
Route::group([
    'prefix' => 'auth'
], function () {
    Route::post('login', 'AuthController@login');
    Route::post('signup', 'AuthController@signup');
  
    Route::group([
      'middleware' => 'auth:api'
    ], function() {
        Route::get('logout', 'AuthController@logout');
        Route::get('user', 'AuthController@user');
    });
});

C:\xampp\htdocs\auth\config\app.php

 'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Laravel\Passport\PassportServiceProvider::class,
]

C:\xampp\htdocs\auth\config\auth.php

'driver' => 'passport',

[Laravel] Xác thực API với Laravel 5.6 Passport Authentication (Part1)

Laravelpassport

Chào mọi người, hôm nay mình lại quay lại đây, hôm nay mình sẽ hướng dẫn mọi người xác thực API bằng laravel passport

Trong các ứng dụng phần mềm hiện đại, các web API là không thể thiếu, có rất nhiều các mô hình ứng dụng sử dụng web API như mô hình server-to-server, hay mô hình SPA (Single Page Application). Trong quá trình phát triển các API, rất cần thiết phải bảo vệ dữ liệu khỏi những con mắt tò mò, điều này với các hệ thống truyền thống rất đơn giản còn với API thì sao? Laravel tạo ra một gói thư viện Passport giúp thực hiện xác thực trong API đơn giản đơn giản hơn, nó cung cấp đầy đủ OAuth2. Laravel Passport được xây dựng dựa trên League OAuth2 server được phát triển bởi Alex Bilbie Chúng ta bắt đầu luôn nhé

Install Laravel

Chúng ta tải laravel thông qua composer nhé

composer create-project --prefer-dist laravel/laravel auth

khi tải về mn nhớ tạo 1 file .env và chạy composer install và php artisan key:generate đây là những bước bình thường mình sẽ không nói sâu đến nữa

Install Laravel Passport Package

Chúng t a chỉ cần tải thư việc này về bằng composer nhé

composer require laravel/passport

Giống như hầu hết các package cho Laravel khác, chúng ta cần thêm service provider của Laravel Passport trong file config/app.php (việc thêm service provider cần được thực hiện sau khi package đã được tải về thông qua Composer)

Laravel\Passport\PassportServiceProvider::class,

Laravel Passport cung cấp sẵn một số migration class để tạo các bảng cần thiết để lưu trữ authorization codes, access tokens, refresh tokens, personal access tokens, thông tin về clients (danh sách các file migration có thể xem tại đây). Để cài đặt các bảng cần thiết cho Laravel Passport, chúng ta dùng lệnh sau:

php artisan migrate

Generate Key

Bước tiếp theo, chúng ta cần tạo encryption keys (được dùng khi tạo access tokens) và tạo các client liên quan đến Personal Access Grant và Password Grant

php artisan passport:install

sau khi chạy xong chúng ta thêm Laravel\Passport\HasApiTokens vào model 'App\user'. Trait này sẽ cung cấp 1 vài hepler cho phép kiểm tra token và phạm vi của người dùng

Passport Config

<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
    use Notifiable, HasApiTokens;
}

Tiếp theo là gọi phương thức Passport::routes trong AuthServiceProvider, Phương thức này dùng để đăng ký những routes cần thiết để cho những vấn đề liên quan đến access tokens and revoke access tokens, clients, and personal access tokens

<?php
namespace App\Providers;
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];
    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
        Passport::routes();
    }
}

Cuối cùng là tại file config/auth.php, bạn nên chọn driver của api à passport, Điều này sẽ hướng ứng dụng của bạn sử dụng Passport's TokenGuard khi các thực yêu của của API

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

Create Api Routes

<?php
use Illuminate\Http\Request;
Route::group([
    'prefix' => 'auth'
], function () {
    Route::post('login', 'AuthController@login');
    Route::post('signup', 'AuthController@signup');
  
    Route::group([
      'middleware' => 'auth:api'
    ], function() {
        Route::get('logout', 'AuthController@logout');
        Route::get('user', 'AuthController@user');
    });
});

Create Controller

Chúng ta tạo 1 controller là AuthController nhé

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\User;
class AuthController extends Controller
{
    /**
     * Create user
     *
     * @param  [string] name
     * @param  [string] email
     * @param  [string] password
     * @param  [string] password_confirmation
     * @return [string] message
     */
    public function signup(Request $request)
    {
        $request->validate([
            'name' => 'required|string',
            'email' => 'required|string|email|unique:users',
            'password' => 'required|string|confirmed'
        ]);
        $user = new User([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password)
        ]);
        $user->save();
        return response()->json([
            'message' => 'Successfully created user!'
        ], 201);
    }
  
    /**
     * Login user and create token
     *
     * @param  [string] email
     * @param  [string] password
     * @param  [boolean] remember_me
     * @return [string] access_token
     * @return [string] token_type
     * @return [string] expires_at
     */
    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required|string|email',
            'password' => 'required|string',
            'remember_me' => 'boolean'
        ]);
        $credentials = request(['email', 'password']);
        if(!Auth::attempt($credentials))
            return response()->json([
                'message' => 'Unauthorized'
            ], 401);
        $user = $request->user();
        $tokenResult = $user->createToken('Personal Access Token');
        $token = $tokenResult->token;
        if ($request->remember_me)
            $token->expires_at = Carbon::now()->addWeeks(1);
        $token->save();
        return response()->json([
            'access_token' => $tokenResult->accessToken,
            'token_type' => 'Bearer',
            'expires_at' => Carbon::parse(
                $tokenResult->token->expires_at
            )->toDateTimeString()
        ]);
    }
  
    /**
     * Logout user (Revoke the token)
     *
     * @return [string] message
     */
    public function logout(Request $request)
    {
        $request->user()->token()->revoke();
        return response()->json([
            'message' => 'Successfully logged out'
        ]);
    }
  
    /**
     * Get the authenticated User
     *
     * @return [json] user object
     */
    public function user(Request $request)
    {
        return response()->json($request->user());
    }
}

OK ngon rồi đấy, giờ chạy php artisan serve để test nhé

Tests

Chúng ta xử dụng Postman để test api nhé, trong phần Header mn nhớ có key này nhé

 Content-Type: application/json
X-Requested-With: XMLHttpRequest
  • Api Signup

  • Api Login

  • Api Logout

  • Api User

End

OK bài này hôm nay đến đây thôi, bài sau mình sẽ hướng dẫn làm

  • Part 2. Confirm account + notifications

  • Part 3. Generate avatar

  • Part 4. Reset Password

  • Part 5. Send Notifications with Queues on Redis nhé

Refer

TABLE OF CONTENTS

SUGGESTED ORGANIZATIONS

AvatarSun* R&D Lab 41 70 167AvatarViblo 9 11 67AvatarSun* Cebu Branch / Awesome Ars Academia Cebu 14 21 50

Tìm hiểu về Authentication trong LaravelHoàng Nguyễn12 min read 23586 14 237JWT with Laravel 5.5 and Angular 4 (P1)Sơn Híp2 min read 921 9 012Multiple authentication in Laravel 5.2Dung Nguyen Quang9 min read 3687 5 28Hướng dẫn sử dụng jwt token với laravel P1Viet Anh8 min read 3430 5 06

More from Trần Văn Mỹ

[Devops] Build Docker cho dự án sử dụng những công nghệ cũ nhấtTrần Văn Mỹ9 min read 1050 5 816[Devops] CI/CD cùng CircleCI và Deployer cho dự án LaravelTrần Văn Mỹ15 min read 1031 7 212[Backend] Deploy dự án Laravel bằng DeployerTrần Văn Mỹ6 min read 650 8 014(Frontend) Cookie – Chiếc “bánh qui” vô hại?Trần Văn Mỹ5 min read 310 1 04

Comments

  • Write

  • Preview

AvatarPost CommentAvatarVương Minh Thái@vuongthai95Feb 15th, 2019 10:07 AM

hay bạn ơi! 0 |ReplyShareAvatarThang pham@thangpmFeb 21st, 2019 11:05 AM

Hi bạn, cho mình hỏi chút

public function user(Request $request)

{

    return response()->json($request->user());

}

Cái hàm lấy thông tin user này mình ko hiểu lắm nhỉ, mình chạy api thì ko thấy trả về thông tin gì, Bạn giải thích giúp mình với. thank 0 |ReplyShareAvatarVũ Nguyễn@vunguyen10111995Feb 21st, 2019 4:20 PM

bạn đã đăng nhập để lấy được access_token chưa 0 |ReplyShareAvatarTrần Văn Mỹ@mih2t9xFeb 21st, 2019 4:37 PM

Vào tận đây để reply comment cơ à, chú có tâm quá =)) 0 |ReplyShareAvatarVũ Nguyễn@vunguyen10111995Feb 21st, 2019 4:38 PM

nó hiện lên discussions, ngày nào chả check cái đó =)) 0 |ReplyShareAvatarTrần Văn Mỹ@mih2t9xFeb 21st, 2019 4:39 PM

sịn sò đấy 😂 0 |ReplyShareAvatarThang pham@thangpmFeb 21st, 2019 4:49 PM

Mình có lấy access token rồi bạn 0 |ReplyShareAvatarTrần Văn Mỹ@mih2t9xFeb 21st, 2019 4:38 PM

Bạn gửi cho mình xem cái postman bạn call api như nào 0 |ReplyShareAvatarThang pham@thangpmFeb 21st, 2019 4:49 PM

get_token.png
get_user.png

Đây bạn ơi 0 |ReplyShareAvatarTrần Văn Mỹ@mih2t9xFeb 21st, 2019 4:53 PM

Bạn thử return 1 hay return ra cái gì đấy trong function user xem đã call được vào function đấy chưa 0 |ReplyShareAvatarThang pham@thangpmFeb 21st, 2019 4:57 PM

ok.png

được bạn ơi.

mình chỉ ko hiểu hàm này của bạn là ntn ? $request->user() 0 |ReplyShareAvatarVũ Nguyễn@vunguyen10111995Feb 21st, 2019 5:00 PM

bạn tích lại cái content-type:application/json rồi test lại xem 😃 0 |ReplyShareAvatarThang pham@thangpmFeb 21st, 2019 5:02 PM

vẫn thế bạn ơi 😦 0 |ReplyShareAvatarVũ Nguyễn@vunguyen10111995Feb 21st, 2019 5:09 PM

ặc, mình test vẫn ok cơ mà nhỉ 0 |ReplyShareAvatarThang pham@thangpmFeb 21st, 2019 5:10 PM

chắc sai code ở đâu đó rồi bạn. mình đang xem lại 0 |ReplyShareAvatarTrần Văn Mỹ@mih2t9xFeb 21st, 2019 5:03 PM

$request->user() dùng để truy xuất ra người dùng được xác thực hiện tại, cái này nó cũng giống như bạn dùng Auth::user() ấy 0 |ReplyShareAvatarThang pham@thangpmFeb 21st, 2019 5:09 PM

Cảm ơn bạn nhiều nhé ^^ . để mình xem lại code xem 0 |ReplyShareAvatarThang pham@thangpmFeb 22nd, 2019 10:25 AM

Tạo cái dự án mới thì lại chạy ngon rồi bạn ^^ 0 |ReplyShareAvatarThang pham@thangpmFeb 22nd, 2019 10:31 AM

Untitled.png

ngồi gõ lại code của bạn mà gõ nhầm 😞 0 |ReplyShareAvatarTrần Văn Mỹ@mih2t9xFeb 22nd, 2019 1:04 PM

ok bạn, phải gặp bug như thế thì bạn mới nâng cao trình độ được bạn à 👍, chứ cứ làm theo bài viết của t thì cũng chỉ là học vẹt thôi ko đọng dk kiến thức gì cả đâu, 😀😀😀 0 |ReplyShareAvatarThang pham@thangpmFeb 22nd, 2019 4:00 PM

👍 0 |ReplyShareAvatarMinh Cong Nguyen@luffybkchyproFeb 26th, 2019 7:23 AM

Hi Mỹ, cho mình hỏi chút. Khi mình test bằng Postman phần api user thì khi điền access token vào phần header thì báo lỗi Unauthenticated. 1.PNG Nhưng khi chuyển sang mục authorization rồi điền access token thì lại được. 2.PNG 0 |ReplyShareAvatarTrần Văn Mỹ@mih2t9xFeb 26th, 2019 11:02 AM

@luffybkchypro cái Key Authorization của bạn đang bị sai rồi kià , Bearer đằng sau làm gì có dấu . (chấm) bạn ơi 0 |ReplyShareAvatarTin Lam@lamtin222Jun 6th, 2019 9:06 AM

Cho mình hỏi, cái expired_at của token tại sao không có hiệu quả? mình thử add 5 phút, nhưng sau 5 phút thì dùng cái token đó vẫn access dc?

Last updated