From ab9328d7558cfdaa00319985842588e91720d2e3 Mon Sep 17 00:00:00 2001 From: prashant-webkul Date: Mon, 19 Nov 2018 17:04:13 +0530 Subject: [PATCH] Auth API implemented using JWT tokens, removed laravel passport --- app/Criteria/MyCriteria.php | 27 --- app/Http/Kernel.php | 2 - app/Providers/AppServiceProvider.php | 2 +- app/Providers/AuthServiceProvider.php | 7 - composer.json | 4 +- config/app.php | 4 +- config/auth.php | 10 +- config/jwt.php | 173 ++++++++++++++++++ ...193819_create_admin_oauth_tokens_table.php | 37 ++++ ...049_create_customer_oauth_tokens_table.php | 37 ++++ .../Http/Controllers/Admin/AuthController.php | 83 ++++++++- .../Controllers/Customer/AuthController.php | 71 +++++-- packages/Webkul/API/Http/api.php | 23 ++- .../API/Providers/APIServiceProvider.php | 17 ++ .../Admin/src/DataGrids/CustomerDataGrid.php | 7 - .../Admin/src/DataGrids/OrderDataGrid.php | 24 +-- .../Webkul/Customer/src/Models/Customer.php | 28 ++- packages/Webkul/User/src/Models/Admin.php | 26 ++- 18 files changed, 474 insertions(+), 108 deletions(-) delete mode 100644 app/Criteria/MyCriteria.php create mode 100644 config/jwt.php create mode 100644 packages/Webkul/API/Database/Migrations/2018_11_17_193819_create_admin_oauth_tokens_table.php create mode 100644 packages/Webkul/API/Database/Migrations/2018_11_19_104049_create_customer_oauth_tokens_table.php diff --git a/app/Criteria/MyCriteria.php b/app/Criteria/MyCriteria.php deleted file mode 100644 index 6e32e67b2..000000000 --- a/app/Criteria/MyCriteria.php +++ /dev/null @@ -1,27 +0,0 @@ - \App\Http\Middleware\RedirectIfAuthenticated::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, - // 'passport-admin' => \App\Http\Middleware\CustomPassportProvider::class, ]; } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 64e2c2f45..2fe08f2eb 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -22,6 +22,6 @@ class AppServiceProvider extends ServiceProvider */ public function register() { - // + } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 40e6b22b5..454b89b73 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,8 +2,6 @@ namespace App\Providers; -use Illuminate\Support\Facades\Route; -use Laravel\Passport\Passport; use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; @@ -26,10 +24,5 @@ class AuthServiceProvider extends ServiceProvider public function boot() { $this->registerPolicies(); - - Passport::routes(); - // Route::group(['middleware' => 'passport-admin'], function () { - // Passport::routes(); - // }); } } diff --git a/composer.json b/composer.json index 641272706..149aee428 100644 --- a/composer.json +++ b/composer.json @@ -18,11 +18,11 @@ "kalnoy/nestedset": "^4.3", "konekt/concord": "^1.2", "laravel/framework": "5.6.*", - "laravel/passport": "^8.0@dev", "laravel/tinker": "^1.0", "nwidart/laravel-modules": "^3.2", "prettus/l5-repository": "^2.6", - "propaganistas/laravel-intl": "^2.0" + "propaganistas/laravel-intl": "^2.0", + "tymon/jwt-auth": "dev-develop" }, "require-dev": { "barryvdh/laravel-debugbar": "^3.1", diff --git a/config/app.php b/config/app.php index 1571391da..b7729283b 100644 --- a/config/app.php +++ b/config/app.php @@ -199,6 +199,7 @@ return [ Konekt\Concord\ConcordServiceProvider::class, Flynsarmy\DbBladeCompiler\DbBladeCompilerServiceProvider::class, Barryvdh\DomPDF\ServiceProvider::class, + Tymon\JWTAuth\Providers\LaravelServiceProvider::class, //Webkul packages Webkul\Theme\Providers\ThemeServiceProvider::class, @@ -272,6 +273,7 @@ return [ 'Cart' => Webkul\Checkout\Facades\Cart::class, 'Core' => Webkul\Core\Facades\Core::class, 'DbView' => Flynsarmy\DbBladeCompiler\Facades\DbView::class, - 'PDF' => Barryvdh\DomPDF\Facade::class + 'PDF' => Barryvdh\DomPDF\Facade::class, + 'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class, ], ]; \ No newline at end of file diff --git a/config/auth.php b/config/auth.php index 4e6b5f994..55a8c2653 100644 --- a/config/auth.php +++ b/config/auth.php @@ -13,7 +13,7 @@ return [ ], 'api' => [ - 'driver' => 'passport', + 'driver' => 'jwt', 'provider' => 'customers', ], @@ -27,10 +27,10 @@ return [ 'provider' => 'admins' ], - // 'admin-api' => [ - // 'driver' => 'token', - // 'provider' => 'admins', - // ] + 'admin-api' => [ + 'driver' => 'jwt', + 'provider' => 'admins', + ] ], 'providers' => [ diff --git a/config/jwt.php b/config/jwt.php new file mode 100644 index 000000000..f83b6f796 --- /dev/null +++ b/config/jwt.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + + /* + |-------------------------------------------------------------------------- + | JWT Authentication Secret + |-------------------------------------------------------------------------- + | + | Don't forget to set this, as it will be used to sign your tokens. + | A helper command is provided for this: `php artisan jwt:generate` + | + */ + + 'secret' => env('JWT_SECRET', 'changeme'), + + /* + |-------------------------------------------------------------------------- + | JWT time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token will be valid for. + | Defaults to 1 hour + | + */ + + 'ttl' => env('JWT_TTL', 120), + + /* + |-------------------------------------------------------------------------- + | Refresh time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token can be refreshed + | within. I.E. The user can refresh their token within a 2 week window of + | the original token being created until they must re-authenticate. + | Defaults to 2 weeks + | + */ + + 'refresh_ttl' => env('JWT_REFRESH_TTL', 86400), + + /* + |-------------------------------------------------------------------------- + | JWT hashing algorithm + |-------------------------------------------------------------------------- + | + | Specify the hashing algorithm that will be used to sign the token. + | + | See here: https://github.com/namshi/jose/tree/2.2.0/src/Namshi/JOSE/Signer + | for possible values + | + */ + + 'algo' => 'HS256', + + /* + |-------------------------------------------------------------------------- + | User Model namespace + |-------------------------------------------------------------------------- + | + | Specify the full namespace to your User model. + | e.g. 'Acme\Entities\User' + | + */ + + 'user' => 'App\User', + + /* + |-------------------------------------------------------------------------- + | User identifier + |-------------------------------------------------------------------------- + | + | Specify a unique property of the user that will be added as the 'sub' + | claim of the token payload. + | + */ + + 'identifier' => 'id', + + /* + |-------------------------------------------------------------------------- + | Required Claims + |-------------------------------------------------------------------------- + | + | Specify the required claims that must exist in any token. + | A TokenInvalidException will be thrown if any of these claims are not + | present in the payload. + | + */ + + 'required_claims' => ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'], + + /* + |-------------------------------------------------------------------------- + | Blacklist Enabled + |-------------------------------------------------------------------------- + | + | In order to invalidate tokens, you must have the blacklist enabled. + | If you do not want or need this functionality, then set this to false. + | + */ + + 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), + + /* + |-------------------------------------------------------------------------- + | Providers + |-------------------------------------------------------------------------- + | + | Specify the various providers used throughout the package. + | + */ + + 'providers' => [ + + /* + |-------------------------------------------------------------------------- + | User Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to find the user based + | on the subject claim + | + */ + + 'user' => 'Tymon\JWTAuth\Providers\User\EloquentUserAdapter', + + /* + |-------------------------------------------------------------------------- + | JWT Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to create and decode the tokens. + | + */ + + 'jwt' => 'Tymon\JWTAuth\Providers\JWT\Namshi', + + /* + |-------------------------------------------------------------------------- + | Authentication Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to authenticate users. + | + */ + + 'auth' => 'Tymon\JWTAuth\Providers\Auth\Illuminate', + + /* + |-------------------------------------------------------------------------- + | Storage Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to store tokens in the blacklist + | + */ + + 'storage' => 'Tymon\JWTAuth\Providers\Storage\Illuminate' + + ], + +]; diff --git a/packages/Webkul/API/Database/Migrations/2018_11_17_193819_create_admin_oauth_tokens_table.php b/packages/Webkul/API/Database/Migrations/2018_11_17_193819_create_admin_oauth_tokens_table.php new file mode 100644 index 000000000..684d8c15f --- /dev/null +++ b/packages/Webkul/API/Database/Migrations/2018_11_17_193819_create_admin_oauth_tokens_table.php @@ -0,0 +1,37 @@ +increments('id'); + $table->integer('admin_id')->unsigned(); + $table->foreign('admin_id')->references('id')->on('admins'); + $table->integer('throttle')->default(0)->unsigned(); + $table->mediumText('api_token')->nullable(); + $table->string('token_name')->nullable(); + $table->dateTime('ttl'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('admin_oauth_tokens'); + } +} diff --git a/packages/Webkul/API/Database/Migrations/2018_11_19_104049_create_customer_oauth_tokens_table.php b/packages/Webkul/API/Database/Migrations/2018_11_19_104049_create_customer_oauth_tokens_table.php new file mode 100644 index 000000000..418f11017 --- /dev/null +++ b/packages/Webkul/API/Database/Migrations/2018_11_19_104049_create_customer_oauth_tokens_table.php @@ -0,0 +1,37 @@ +increments('id'); + $table->integer('customer_id')->unsigned(); + $table->foreign('customer_id')->references('id')->on('customers'); + $table->integer('throttle')->default(0)->unsigned(); + $table->mediumText('api_token')->nullable(); + $table->string('token_name')->nullable(); + $table->dateTime('ttl'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('customer_oauth_tokens'); + } +} diff --git a/packages/Webkul/API/Http/Controllers/Admin/AuthController.php b/packages/Webkul/API/Http/Controllers/Admin/AuthController.php index 4b10a2681..cb25e2a78 100644 --- a/packages/Webkul/API/Http/Controllers/Admin/AuthController.php +++ b/packages/Webkul/API/Http/Controllers/Admin/AuthController.php @@ -6,7 +6,7 @@ use Webkul\API\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Event; -use Webkul\User\Models\Admin; +use Webkul\User\Repositories\AdminRepository; /** * Session controller for the APIs of user admins @@ -16,6 +16,19 @@ use Webkul\User\Models\Admin; */ class AuthController extends Controller { + /** + * Display a listing of the resource. + * + * @return \Illuminate\Http\Response + */ + protected $_config; + + public function __construct() + { + $this->middleware('admin')->except(['show','create']); + $this->_config = request('_config'); + } + public function create(Request $request) { $request->validate([ @@ -23,18 +36,68 @@ class AuthController extends Controller 'password' => 'required' ]); - if (!auth()->guard('admin')->attempt(request(['email', 'password']))) { - return response()->json(false, 200); + $credentials['email'] = $request->input('email'); + $credentials['password'] = $request->input('password'); + + if ($token = $this->guard()->attempt(request(['email', 'password']))) { + return $this->respondWithToken($token); } - if(auth()->guard('admin')->check()) { - $admin = auth()->guard('admin')->user(); + return response()->json(['error' => 'Unauthorized'], 401); + } - $token = $admin->createToken('admin-token')->accessToken; + /** + * 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', + 'expires_in' => auth('api')->factory()->getTTL() * 60, + 'admin_id' => auth()->guard('admin-api')->user()->id, + 'admin_email' => auth()->guard('admin-api')->user()->email + ]); + } - return response()->json($token, 200); - } else { - return response()->json(false, 200); - } + /** + * Get the guard to be used during authentication. + * + * @return \Illuminate\Contracts\Auth\Guard + */ + public function guard() + { + return auth()->guard('admin-api'); + } + + /** + * Refresh a token. + * + * @return \Illuminate\Http\JsonResponse + */ + public function refresh() + { + return $this->respondWithToken($this->guard()->refresh()); + } + + /** + * Get the authenticated User + * + * @return \Illuminate\Http\JsonResponse + */ + public function me() + { + return response()->json($this->guard()->user()); + } + + public function destroy($id) + { + $this->guard()->logout(); + + return response()->json(['message' => 'Successfully logged out']); } } \ No newline at end of file diff --git a/packages/Webkul/API/Http/Controllers/Customer/AuthController.php b/packages/Webkul/API/Http/Controllers/Customer/AuthController.php index a1c57e7cb..f6520f715 100644 --- a/packages/Webkul/API/Http/Controllers/Customer/AuthController.php +++ b/packages/Webkul/API/Http/Controllers/Customer/AuthController.php @@ -6,8 +6,8 @@ use Webkul\API\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Event; -use Webkul\Customer\Models\Customer; use Webkul\Customer\Http\Listeners\CustomerEventsHandler; +use Auth; use Cart; /** @@ -43,27 +43,68 @@ class AuthController extends Controller 'password' => 'required' ]); - if (!auth()->guard('customer')->attempt(request(['email', 'password']))) { - return response()->json([false], 200); + $credentials['email'] = $request->input('email'); + $credentials['password'] = $request->input('password'); + + if ($token = $this->guard()->attempt(request(['email', 'password']))) { + return $this->respondWithToken($token); } - if(auth()->guard('customer')->check()) { - $customer = auth()->guard('customer')->user(); + return response()->json(['error' => 'Unauthorized'], 401); + } - $token = $customer->createToken('customer-token')->accessToken; + /** + * 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', + 'expires_in' => auth('api')->factory()->getTTL() * 60, + 'customer_id' => auth()->guard('customer')->user()->id, + 'customer_email' => auth()->guard('customer')->user()->email + ]); + } - return response()->json([$token], 200); - } else { - return response()->json([false], 200); - } + /** + * Get the guard to be used during authentication. + * + * @return \Illuminate\Contracts\Auth\Guard + */ + public function guard() + { + return Auth::guard('api'); + } + + /** + * Refresh a token. + * + * @return \Illuminate\Http\JsonResponse + */ + public function refresh() + { + return $this->respondWithToken($this->guard()->refresh()); + } + + /** + * Get the authenticated User + * + * @return \Illuminate\Http\JsonResponse + */ + public function me() + { + return response()->json($this->guard()->user()); } public function destroy($id) { - auth()->guard('customer')->logout(); + $this->guard()->logout(); - Event::fire('customer.after.logout', $id); - - return redirect()->route($this->_config['redirect']); + return response()->json(['message' => 'Successfully logged out']); } -} \ No newline at end of file +} diff --git a/packages/Webkul/API/Http/api.php b/packages/Webkul/API/Http/api.php index 8c18436dd..8318a1a24 100644 --- a/packages/Webkul/API/Http/api.php +++ b/packages/Webkul/API/Http/api.php @@ -1,16 +1,15 @@ group(function () { - //customer APIs - Route::prefix('customer')->group(function () { - Route::post('login', 'Webkul\API\Http\Controllers\Customer\AuthController@create')->name('login'); - }); - - //Admin APIs - Route::prefix('admin')->group(function () { - Route::post('login', 'Webkul\API\Http\Controllers\Admin\AuthController@create'); - }); +Route::group(['middleware' => 'api', 'namespace' => 'Webkul\API\Http\Controllers', 'prefix' => 'api/customer'], function ($router) { + Route::post('login', 'Customer\AuthController@create'); + Route::post('logout', 'Customer\AuthController@destroy'); + Route::post('refresh', 'Customer\AuthController@refresh'); + Route::post('me', 'Customer\AuthController@me'); }); +Route::group(['namespace' => 'Webkul\API\Http\Controllers', 'prefix' => 'api/admin'], function ($router) { + Route::post('login', 'Admin\AuthController@create'); + Route::post('logout', 'Admin\AuthController@destroy'); + Route::post('refresh', 'Admin\AuthController@refresh'); + Route::post('me', 'Admin\AuthController@me'); +}); \ No newline at end of file diff --git a/packages/Webkul/API/Providers/APIServiceProvider.php b/packages/Webkul/API/Providers/APIServiceProvider.php index 954f74cf3..b5aa363f4 100644 --- a/packages/Webkul/API/Providers/APIServiceProvider.php +++ b/packages/Webkul/API/Providers/APIServiceProvider.php @@ -14,6 +14,8 @@ class APIServiceProvider extends ServiceProvider public function boot() { $this->loadRoutesFrom(__DIR__.'/../Http/api.php'); + + $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations'); } /** @@ -23,5 +25,20 @@ class APIServiceProvider extends ServiceProvider */ public function register() { + // $app['Dingo\Api\Auth\Auth']->extend('oauth', function ($app) { + // return new Dingo\Api\Auth\Provider\JWT($app['Tymon\JWTAuth\JWTAuth']); + // }); + + // $app['Dingo\Api\Http\RateLimit\Handler']->extend(function ($app) { + // return new Dingo\Api\Http\RateLimit\Throttle\Authenticated; + // }); + + // $app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) { + // $fractal = new League\Fractal\Manager; + + // $fractal->setSerializer(new League\Fractal\Serializer\JsonApiSerializer); + + // return new Dingo\Api\Transformer\Adapter\Fractal($fractal); + // }); } } diff --git a/packages/Webkul/Admin/src/DataGrids/CustomerDataGrid.php b/packages/Webkul/Admin/src/DataGrids/CustomerDataGrid.php index 7108b1f69..7a6ce731d 100644 --- a/packages/Webkul/Admin/src/DataGrids/CustomerDataGrid.php +++ b/packages/Webkul/Admin/src/DataGrids/CustomerDataGrid.php @@ -82,13 +82,6 @@ class CustomerDataGrid 'label' => 'Email', 'sortable' => false, ], - [ - 'name' => 'phone', - 'alias' => 'Phone', - 'type' => 'number', - 'label' => 'Phone', - 'sortable' => true, - ], [ 'name' => 'customer_group_id', 'alias' => 'CustomerGroupId', diff --git a/packages/Webkul/Admin/src/DataGrids/OrderDataGrid.php b/packages/Webkul/Admin/src/DataGrids/OrderDataGrid.php index 69b396872..0a380f282 100644 --- a/packages/Webkul/Admin/src/DataGrids/OrderDataGrid.php +++ b/packages/Webkul/Admin/src/DataGrids/OrderDataGrid.php @@ -32,12 +32,12 @@ class OrderDataGrid //True in case of joins else aliasing key required on all cases 'massoperations' =>[ - [ - 'route' => route('admin.datagrid.delete'), - 'method' => 'DELETE', - 'label' => 'Delete', - 'type' => 'button', - ], + // [ + // 'route' => route('admin.datagrid.delete'), + // 'method' => 'DELETE', + // 'label' => 'Delete', + // 'type' => 'button', + // ], ], 'actions' => [ @@ -47,12 +47,12 @@ class OrderDataGrid 'confirm_text' => 'Do you really want to do this?', 'icon' => 'icon pencil-lg-icon', ], - [ - 'type' => 'Delete', - 'route' => route('admin.datagrid.delete'), - 'confirm_text' => 'Do you really want to do this?', - 'icon' => 'icon trash-icon', - ], + // [ + // 'type' => 'Delete', + // 'route' => route('admin.datagrid.delete'), + // 'confirm_text' => 'Do you really want to do this?', + // 'icon' => 'icon trash-icon', + // ], ], 'join' => [], diff --git a/packages/Webkul/Customer/src/Models/Customer.php b/packages/Webkul/Customer/src/Models/Customer.php index 04afd3d3d..c7cd36242 100644 --- a/packages/Webkul/Customer/src/Models/Customer.php +++ b/packages/Webkul/Customer/src/Models/Customer.php @@ -2,7 +2,7 @@ namespace Webkul\Customer\Models; -use Laravel\Passport\HasApiTokens; +use Tymon\JWTAuth\Contracts\JWTSubject; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; use Webkul\Customer\Models\CustomerGroup; @@ -12,9 +12,9 @@ use Webkul\Customer\Models\Wishlist; use Webkul\Customer\Notifications\CustomerResetPassword; -class Customer extends Authenticatable +class Customer extends Authenticatable implements JWTSubject { - use HasApiTokens, Notifiable; + use Notifiable; protected $table = 'customers'; @@ -22,7 +22,27 @@ class Customer extends Authenticatable protected $hidden = ['password', 'remember_token']; - /** + /** + * 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 []; + } + + /** * Get the customer full name. */ public function getNameAttribute() { diff --git a/packages/Webkul/User/src/Models/Admin.php b/packages/Webkul/User/src/Models/Admin.php index 3d9d41fe6..acf5b9d6a 100644 --- a/packages/Webkul/User/src/Models/Admin.php +++ b/packages/Webkul/User/src/Models/Admin.php @@ -2,16 +2,16 @@ namespace Webkul\User\Models; -use Laravel\Passport\HasApiTokens; +use Tymon\JWTAuth\Contracts\JWTSubject; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; use Webkul\User\Models\Role; use Webkul\User\Notifications\AdminResetPassword; -class Admin extends Authenticatable +class Admin extends Authenticatable implements JWTSubject { - use HasApiTokens, Notifiable; + use Notifiable; /** * The attributes that are mass assignable. @@ -31,6 +31,26 @@ class Admin extends Authenticatable 'password', 'remember_token', ]; + /** + * 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 []; + } + /** * Get the role that owns the admin. */