diff --git a/Modules/Primaya/Http/Controllers/Api/AuthController.php b/Modules/Primaya/Http/Controllers/Api/AuthController.php index 378328bc..beb0dff0 100644 --- a/Modules/Primaya/Http/Controllers/Api/AuthController.php +++ b/Modules/Primaya/Http/Controllers/Api/AuthController.php @@ -16,280 +16,103 @@ use Illuminate\Support\Facades\Validator; use Modules\Primaya\Helpers\ApiResponse; use Illuminate\Support\Facades\DB; use App\Helpers\Helper; +use App\Models\Corporate; use Illuminate\Support\Facades\View; +use Tymon\JWTAuth\Facades\JWTAuth; +use Tymon\JWTAuth\Exceptions\JWTException; class AuthController extends Controller { - public function login(Request $request) + public function loginJwt(Request $request) { $data = [ 'email' => $request->email, 'password' => $request->password ]; + $validator = Validator::make($request->all(), [ 'email' => 'required|email', 'password' => 'required' - ], [ - 'email.required' => trans('Validation.required',['attribute' => 'Email']), - 'email.email' => trans('Validation.email'), - 'password.required' => trans('Validation.required',['attribute' => 'Password']), ]); - if ($validator->fails()) - { - return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); - } - else - { - $user = User::where('email', $request->email)->first(); - if (!$user) { - return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); - } - - if (!Hash::check($request->password, $user->password)) { - return ApiResponse::apiResponse('Bad Request', $data, trans('Message.password'), 400); - } - - $res_data = [ - 'user' => $user, - 'token' => $user->createToken('app')->plainTextToken - ]; - - return ApiResponse::apiResponse("Success", $res_data, trans('Message.success'), 200); - } - } - - public function logout(Request $request) - { - $request->user()->tokens()->delete(); - - return ApiResponse::apiResponse('Success', [], trans('Message.logout'), 200); - } - - public function resetPassword(Request $request) - { - $user = Auth::user(); - - $request->validate([ - 'old_password' => 'required', - 'new_password' => 'required', - 'confirm_new_password' => 'required' - ]); - - if (!Hash::check($request['old_password'], $user->password)) { - return response(['Message' => 'Password Salah'], 403); + if ($validator->fails()) { + return ApiResponse::apiResponse( + 'Bad Request', + $data, + $validator->errors(), + 400 + ); } - if ($request["new_password"] != $request["confirm_new_password"]) { - return response([ - 'Message' => "Password Tidak Sama" - ]); + // 🔥 1️⃣ Ambil header + $apiKey = $request->header('X-API-KEY'); + $apiSecret = $request->header('X-API-SECRET'); + + if (empty($apiKey) || empty($apiSecret)) { + return ApiResponse::apiResponse( + 'Unauthorized', + null, + 'API Key dan Secret wajib diisi', + 401 + ); } - $user->update([ - 'password' => Hash::make($request->confirm_new_password), - ]); - return response()->json($user); - } - - public function verifyEmail(Request $request) - { - $data = [ - 'email' => $request->email, - ]; - - $validator = Validator::make($request->all(), [ - 'email' => 'required|email', - ], [ - 'email.required' => trans('Validation.required',['attribute' => 'Email']), - 'email.email' => trans('Validation.email'), - ]); - - if ($validator->fails()) - { - return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); - } - else - { - $user = User::where('email', $request->email)->first(); - if (!$user) { - return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); - } - - //send email - // Insert data notifications - $emailTo = $request->email; - $dataNotif = [ - 'user_id' => $user->id, - 'email' => $emailTo, - 'title' => 'Forgot Password', - 'description' => 'Request forgot password from Hospital Portal', - 'type' => 1, - 'isUnRead' => true, - 'created_by' => auth()->check() ? auth()->user()->id : null, - 'created_at' => date('Y-m-d H:i:s'), - 'updated_at' => date('Y-m-d H:i:s'), - ]; - $sendNotif = Helper::insertNotification($dataNotif); - //Insert data password reset - $token = mt_rand(100000, 999999); // Menghasilkan angka acak antara 100000 dan 999999 - $p_resets = DB::table('password_resets') - ->insert([ - 'email' => $request->email, - 'token' => $token, - 'created_at' => date('Y-m-d H:i:s'), - ]); - // Send Email after insert notifications - if($sendNotif && $p_resets) - { - //send to alarm - $nameTo = 'User'; - $dataEmail = [ - 'email' => $emailTo, - 'name' => $nameTo, - 'subject' => 'Request Forgot Password from Hospital Portal Date '. date('Y-m-d H:i:s'), - 'body' => View::make('email/forgot_password', ['token' => $token])->render(), - ]; - Helper::sendEmail($dataEmail); - - $res = DB::table('password_resets') - ->where('email', '=', $request->email) - ->where('token', '=', $token) - ->first(); - - return ApiResponse::apiResponse("Success", $res, trans('Message.success'), 200); - } - else - { - return ApiResponse::apiResponse("Internal Server Error", $data, trans('Message.server_error'), 500); - } - } - } - - public function verifCode(Request $request) - { - $data = [ - 'email' => $request->email, - 'token' => $request->token, - ]; - - $validator = Validator::make($request->all(), [ - 'email' => 'required|email', - 'token' => 'required|numeric', - ], [ - 'email.required' => trans('Validation.required',['attribute' => 'Email']), - 'email.email' => trans('Validation.email'), - 'token.required' => trans('Validation.required',['attribute' => 'Token']), - 'token.numeric' => trans('Validation.required',['attribute' => 'Code Numeric']), - ]); - - if ($validator->fails()) - { - return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); - } - else - { - //Check Time - $check = DB::table('password_resets') - ->where('email', '=', $request->email) - ->where('token', '=', $request->token) - ->select('created_at') + // 🔥 2️⃣ Validasi corporate + $corporate = Corporate::where('api_key', $apiKey) + ->where('api_secret', $apiSecret) ->first(); - if($check) - { - $created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp - $now = time(); // Waktu sekarang dalam UNIX timestamp - - // Hitung selisih waktu dalam menit - $diffInMinutes = ($now - $created_at) / 60; - - if ($diffInMinutes > 60) { - return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); - } else { - // Lanjutkan dengan proses pemulihan kata sandi - return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); - } - } - else - { - return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); - } + if (!$corporate) { + return ApiResponse::apiResponse( + 'Unauthorized', + null, + 'Invalid API Key', + 401 + ); } - } - public function forgetPassword(Request $request) - { - $data = [ - 'email' => $request->email, - 'token' => $request->token, - 'new_password' => $request->new_password - ]; - - $validator = Validator::make($request->all(), [ - 'email' => 'required|email', - 'token' => 'required|numeric', - 'new_password' => [ - 'required', - 'min:8', - 'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/' - ] - ], [ - 'email.required' => trans('Validation.required',['attribute' => 'Email']), - 'email.email' => trans('Validation.email'), - 'token.required' => trans('Validation.required',['attribute' => 'Token']), - 'new_password.required' => trans('Validation.required',['attribute' => 'New Password']), - 'new_password.min' => trans('Validation.min',['attribute' => 'New Password']), - 'new_password.regex' => trans('Validation.regex',['attribute' => 'New Password']), - ]); - - if($request->new_password != $request->confirm_new_password) - { - return ApiResponse::apiResponse('Bad Request', $data, 'Confirm password is not the same', 400); - } - else if ($validator->fails()) - { - return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); - } - else - { - //Check Time - $check = DB::table('password_resets') - ->where('email', '=', $request->email) - ->where('token', '=', $request->token) - ->select('created_at') + // 🔥 3️⃣ Cari user sesuai corporate + $user = User::where('email', $request->email) + ->where('corporate_id', $corporate->id) ->first(); - if($check) - { - $created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp - $now = time(); // Waktu sekarang dalam UNIX timestamp - - // Hitung selisih waktu dalam menit - $diffInMinutes = ($now - $created_at) / 60; - - if ($diffInMinutes > 60) { - return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); - } else { - // Lanjutkan dengan proses pemulihan kata sandi - $user = User::where('email', $request->email)->first(); - if ($user) - { - $newPassword = Hash::make($request->new_password); - $user->password = $newPassword; - $user->save(); - return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); - } - else - { - return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); - } - } - } - else - { - return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); - } + if (!$user || !Hash::check($request->password, $user->password)) { + return ApiResponse::apiResponse( + 'Unauthorized', + $data, + 'Email atau password salah', + 401 + ); } + + try { + // 🔥 4️⃣ Generate JWT dengan claim corporate_id + $token = auth('corporate-api')->claims([ + 'corporate_id' => $corporate->id + ])->login($user); + + } catch (JWTException $e) { + return ApiResponse::apiResponse( + 'Error', + null, + 'Gagal membuat token', + 500 + ); + } + + $res_data = [ + 'user' => $user, + 'corporate_id' => $corporate->id, + 'token' => $token, + 'type' => 'Bearer', + 'expires_in' => auth('corporate-api')->factory()->getTTL() * 60 + ]; + + return ApiResponse::apiResponse( + "Success", + $res_data, + 'Login berhasil', + 200 + ); } } diff --git a/Modules/Primaya/Http/Middleware/Authorization.php b/Modules/Primaya/Http/Middleware/Authorization.php index 0673424f..1276e106 100644 --- a/Modules/Primaya/Http/Middleware/Authorization.php +++ b/Modules/Primaya/Http/Middleware/Authorization.php @@ -22,50 +22,40 @@ class Authorization $acceptHeader = $request->header('Accept'); $contentType = $request->header('Content-Type'); $locale = $request->header('Accept-Language'); - $authorization = $request->header('Authorization'); - // Add language - if(!$locale) - { - return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept-Language']), 401); + if (!$locale) { + return ApiResponse::apiResponse( + 'Unauthorized', + null, + trans('Validation.required', ['attribute' => 'Accept-Language']), + 401 + ); } - if($locale !== 'en-US' && $locale !== 'id-ID') - { - return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept-Language']), 400); - } - if ($locale === 'en-US') - { + + if ($locale === 'en-US') { App::setLocale('en'); - } elseif ($locale === 'id-ID') - { + } elseif ($locale === 'id-ID') { App::setLocale('id'); - } else - { - App::setLocale('en'); } - // Validate authorization - if (empty($authorization) || strpos($authorization, 'Bearer ') !== 0) { - return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Authorization']), 401); + if ($acceptHeader !== 'application/json') { + return ApiResponse::apiResponse( + 'Bad Request', + null, + trans('Validation.invalid', ['attribute' => 'Accept']), + 400 + ); } - // Validate type accept & content type - if (!$acceptHeader) - { - return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept']), 401); - } - if (!$contentType && $request->isMethod('post')) - { - return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Content-Type']), 401); - } - if ($acceptHeader !== 'application/json') - { - return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept']), 400); - } - if($contentType !== 'application/json' && $request->isMethod('post')) - { - return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Content-Type']), 400); + if ($request->isMethod('post') && $contentType !== 'application/json') { + return ApiResponse::apiResponse( + 'Bad Request', + null, + trans('Validation.invalid', ['attribute' => 'Content-Type']), + 400 + ); } + return $next($request); } } diff --git a/Modules/Primaya/Http/Middleware/CheckCorporateKey.php b/Modules/Primaya/Http/Middleware/CheckCorporateKey.php new file mode 100644 index 00000000..0117251e --- /dev/null +++ b/Modules/Primaya/Http/Middleware/CheckCorporateKey.php @@ -0,0 +1,36 @@ +header('X-API-KEY'); + $apiSecret = $request->header('X-API-SECRET'); + + // 🔥 WAJIB: Cegah null atau kosong + if (empty($apiKey) || empty($apiSecret)) { + return response()->json([ + 'message' => 'API Key dan Secret wajib diisi' + ], 401); + } + + $corporate = Corporate::where('api_key', $apiKey) + ->where('api_secret', $apiSecret) + ->first(); + + if (!$corporate) { + return response()->json([ + 'message' => 'Invalid API Key' + ], 401); + } + + return $next($request); + } +} \ No newline at end of file diff --git a/Modules/Primaya/Routes/api.php b/Modules/Primaya/Routes/api.php index 5062e587..88f96d85 100644 --- a/Modules/Primaya/Routes/api.php +++ b/Modules/Primaya/Routes/api.php @@ -16,25 +16,24 @@ use Modules\Primaya\Http\Middleware\Authorization; | is assigned the "api" middleware group. Enjoy building your API! | */ -Route::prefix('v1')->group(function() { +Route::prefix('v1')->group(function () { + Route::prefix('primaya')->group(function () { - Route::middleware(Authentication::class)->group(function () { - Route::controller(AuthController::class)->group(function () { - Route::post('login', 'login'); - }); + // LOGIN (pakai corporate key) + Route::middleware(['corporate.key'])->group(function () { + Route::post('login', [AuthController::class, 'loginJwt']); }); - Route::middleware('auth:sanctum')->group(function () { + // JWT Protected + Route::middleware(['auth:corporate-api'])->group(function () { Route::middleware(Authorization::class)->group(function () { - //Search Member - Route::controller(MemberController::class)->group(function () { - Route::post('search-member', 'search'); - }); + Route::post('search-member', [MemberController::class, 'search']); }); + }); - }); + }); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index d3d14d34..376f4e36 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -67,11 +67,12 @@ class Kernel extends HttpKernel 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'linksehat.old.auth' => \App\Http\Middleware\LinksehatOldAuthMiddleware::class, + 'corporate.key' => \Modules\Primaya\Http\Middleware\CheckCorporateKey::class, // Role 'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class, 'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class, 'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class, - + ]; } diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 46888585..03bc6807 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -23,10 +23,19 @@ class Authenticate extends Middleware public function handle($request, Closure $next, ...$guards) { - if (Auth::guard('sanctum')->guest()) { - return response()->json(['error' => 'Bearer Authorization is required'], 401); + // Kalau tidak ada guard dikirim dari route + if (empty($guards)) { + $guards = [null]; } - return parent::handle($request, $next, ...$guards); + foreach ($guards as $guard) { + if (Auth::guard($guard)->check()) { + return $next($request); + } + } + + return response()->json([ + 'error' => 'Unauthorized' + ], 401); } } diff --git a/app/Models/Corporate.php b/app/Models/Corporate.php index ea1b69ed..24dc9919 100644 --- a/app/Models/Corporate.php +++ b/app/Models/Corporate.php @@ -1,7 +1,7 @@ notificationTokens()->orderBy('created_at', 'desc')->pluck('token')->toArray(); } + + public function getJWTIdentifier() + { + return $this->getKey(); + } + + public function getJWTCustomClaims() + { + return []; + } } diff --git a/config/auth.php b/config/auth.php index d8c6cee7..752a7d5f 100644 --- a/config/auth.php +++ b/config/auth.php @@ -40,6 +40,10 @@ return [ 'driver' => 'session', 'provider' => 'users', ], + 'corporate-api' => [ + 'driver' => 'jwt', + 'provider' => 'users', + ], ], /* diff --git a/config/jwt.php b/config/jwt.php new file mode 100644 index 00000000..f83234d1 --- /dev/null +++ b/config/jwt.php @@ -0,0 +1,301 @@ + + * + * 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 in your .env file, as it will be used to sign + | your tokens. A helper command is provided for this: + | `php artisan jwt:secret` + | + | Note: This will be used for Symmetric algorithms only (HMAC), + | since RSA and ECDSA use a private/public key combo (See below). + | + */ + + 'secret' => env('JWT_SECRET'), + + /* + |-------------------------------------------------------------------------- + | JWT Authentication Keys + |-------------------------------------------------------------------------- + | + | The algorithm you are using, will determine whether your tokens are + | signed with a random string (defined in `JWT_SECRET`) or using the + | following public & private keys. + | + | Symmetric Algorithms: + | HS256, HS384 & HS512 will use `JWT_SECRET`. + | + | Asymmetric Algorithms: + | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below. + | + */ + + 'keys' => [ + + /* + |-------------------------------------------------------------------------- + | Public Key + |-------------------------------------------------------------------------- + | + | A path or resource to your public key. + | + | E.g. 'file://path/to/public/key' + | + */ + + 'public' => env('JWT_PUBLIC_KEY'), + + /* + |-------------------------------------------------------------------------- + | Private Key + |-------------------------------------------------------------------------- + | + | A path or resource to your private key. + | + | E.g. 'file://path/to/private/key' + | + */ + + 'private' => env('JWT_PRIVATE_KEY'), + + /* + |-------------------------------------------------------------------------- + | Passphrase + |-------------------------------------------------------------------------- + | + | The passphrase for your private key. Can be null if none set. + | + */ + + 'passphrase' => env('JWT_PASSPHRASE'), + + ], + + /* + |-------------------------------------------------------------------------- + | JWT time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token will be valid for. + | Defaults to 1 hour. + | + | You can also set this to null, to yield a never expiring token. + | Some people may want this behaviour for e.g. a mobile app. + | This is not particularly recommended, so make sure you have appropriate + | systems in place to revoke the token if necessary. + | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list. + | + */ + + 'ttl' => env('JWT_TTL', 60), + + /* + |-------------------------------------------------------------------------- + | 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. + | + | You can also set this to null, to yield an infinite refresh time. + | Some may want this instead of never expiring tokens for e.g. a mobile app. + | This is not particularly recommended, so make sure you have appropriate + | systems in place to revoke the token if necessary. + | + */ + + 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), + + /* + |-------------------------------------------------------------------------- + | JWT hashing algorithm + |-------------------------------------------------------------------------- + | + | Specify the hashing algorithm that will be used to sign the token. + | + */ + + 'algo' => env('JWT_ALGO', Tymon\JWTAuth\Providers\JWT\Provider::ALGO_HS256), + + /* + |-------------------------------------------------------------------------- + | 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', + ], + + /* + |-------------------------------------------------------------------------- + | Persistent Claims + |-------------------------------------------------------------------------- + | + | Specify the claim keys to be persisted when refreshing a token. + | `sub` and `iat` will automatically be persisted, in + | addition to the these claims. + | + | Note: If a claim does not exist then it will be ignored. + | + */ + + 'persistent_claims' => [ + // 'foo', + // 'bar', + ], + + /* + |-------------------------------------------------------------------------- + | Lock Subject + |-------------------------------------------------------------------------- + | + | This will determine whether a `prv` claim is automatically added to + | the token. The purpose of this is to ensure that if you have multiple + | authentication models e.g. `App\User` & `App\OtherPerson`, then we + | should prevent one authentication request from impersonating another, + | if 2 tokens happen to have the same id across the 2 different models. + | + | Under specific circumstances, you may want to disable this behaviour + | e.g. if you only have one authentication model, then you would save + | a little on token size. + | + */ + + 'lock_subject' => true, + + /* + |-------------------------------------------------------------------------- + | Leeway + |-------------------------------------------------------------------------- + | + | This property gives the jwt timestamp claims some "leeway". + | Meaning that if you have any unavoidable slight clock skew on + | any of your servers then this will afford you some level of cushioning. + | + | This applies to the claims `iat`, `nbf` and `exp`. + | + | Specify in seconds - only if you know you need it. + | + */ + + 'leeway' => env('JWT_LEEWAY', 0), + + /* + |-------------------------------------------------------------------------- + | 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), + + /* + | ------------------------------------------------------------------------- + | Blacklist Grace Period + | ------------------------------------------------------------------------- + | + | When multiple concurrent requests are made with the same JWT, + | it is possible that some of them fail, due to token regeneration + | on every request. + | + | Set grace period in seconds to prevent parallel request failure. + | + */ + + 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), + + /* + |-------------------------------------------------------------------------- + | Cookies encryption + |-------------------------------------------------------------------------- + | + | By default Laravel encrypt cookies for security reason. + | If you decide to not decrypt cookies, you will have to configure Laravel + | to not encrypt your cookie token by adding its name into the $except + | array available in the middleware "EncryptCookies" provided by Laravel. + | see https://laravel.com/docs/master/responses#cookies-and-encryption + | for details. + | + | Set it to true if you want to decrypt cookies. + | + */ + + 'decrypt_cookies' => false, + + /* + |-------------------------------------------------------------------------- + | Providers + |-------------------------------------------------------------------------- + | + | Specify the various providers used throughout the package. + | + */ + + 'providers' => [ + + /* + |-------------------------------------------------------------------------- + | JWT Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to create and decode the tokens. + | + */ + + 'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class, + + /* + |-------------------------------------------------------------------------- + | Authentication Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to authenticate users. + | + */ + + 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class, + + /* + |-------------------------------------------------------------------------- + | Storage Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to store tokens in the blacklist. + | + */ + + 'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class, + + ], + +]; diff --git a/database/migrations/2026_02_23_142514_add_api_credentials_to_corporates_table.php b/database/migrations/2026_02_23_142514_add_api_credentials_to_corporates_table.php new file mode 100644 index 00000000..6404ae2d --- /dev/null +++ b/database/migrations/2026_02_23_142514_add_api_credentials_to_corporates_table.php @@ -0,0 +1,39 @@ +string('api_key', 64) + ->unique() + ->nullable() + ->after('linking_rules'); + + $table->string('api_secret', 128) + ->nullable() + ->after('api_key'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('corporates', function (Blueprint $table) { + $table->dropColumn(['api_key', 'api_secret']); + }); + } +};