diff --git a/Modules/Primaya/Routes/api.php b/Modules/Primaya/Routes/api.php index c12bd935..a90a9e7b 100644 --- a/Modules/Primaya/Routes/api.php +++ b/Modules/Primaya/Routes/api.php @@ -1,5 +1,6 @@ group(function () { Route::prefix('primaya')->group(function () { // LOGIN (pakai corporate key) - Route::middleware(['corporate.key'])->group(function () { - Route::post('login', [AuthController::class, 'loginJwt']); - }); + // Route::middleware(['corporate.key'])->group(function () { + // Route::post('login', [AuthController::class, 'loginJwt']); + // }); - // JWT Protected - Route::middleware(['auth:corporate-api'])->group(function () { + // JWT Protected key digenerate oleh client + Route::middleware([JwtMiddleware::class])->group(function () { Route::middleware(Authorization::class)->group(function () { Route::post('search-member', [MemberController::class, 'search']); diff --git a/app/Http/Middleware/JwtMiddleware.php b/app/Http/Middleware/JwtMiddleware.php new file mode 100644 index 00000000..f6c0cdc1 --- /dev/null +++ b/app/Http/Middleware/JwtMiddleware.php @@ -0,0 +1,47 @@ +bearerToken(); + if (!$token) { + return response()->json(['message' => 'Unauthorized!'], 401); + } + + $decoded = AuthService::verifyClientToken($token); + if (!$decoded) { + return response()->json(['message' => 'Invalid Token'], 401); + } + + // Identify client by sub claim + $clientId = $decoded->sub ?? null; + if (!$clientId) { + return response()->json(['message' => 'Invalid client in token'], 401); + } + $clients = config('api_clients.clients');; + $client = collect($clients)->where('api_key', $clientId)->first(); + if (!$client || ($client->is_revoked ?? false)) { + return response()->json(['message' => 'Client not found or revoked'], 401); + } + // Attach client info to request + $request->attributes->set('client', $client); + return $next($request); + } +} + diff --git a/app/Services/AuthService.php b/app/Services/AuthService.php new file mode 100644 index 00000000..b72e7836 --- /dev/null +++ b/app/Services/AuthService.php @@ -0,0 +1,115 @@ + config('app.url'), + 'sub' => $client->client_id, + 'aud' => $aud, + 'iat' => $now, + 'exp' => $exp, + 'jti' => \Illuminate\Support\Str::uuid()->toString(), + 'scope' => $client->scopes, + 'client_db_id' => $client->id, + ]; + return \Firebase\JWT\JWT::encode($payload, $privateKey, 'RS256'); + } + + /** + * Issue a refresh token for the client (random string, store in DB as needed) + */ + public static function issueRefreshToken($client) + { + return false; + // $rawToken = \Illuminate\Support\Str::random(64); + // $hashedToken = hash('sha256', $rawToken); // or use bcrypt if you prefer + + // $refresh = RefreshToken::create([ + // 'client_id' => $client->id, + // 'token' => $hashedToken, + // 'expires_at' => now()->addDays(30), + // 'revoked' => false, + // ]); + + // // Return the raw token to the client + // return $rawToken; + } + + /** + * Verify JWT token and return decoded payload or false + */ + public static function verifyToken($token) + { + $publicKeyPath = env('JWT_PUBLIC_KEY_PATH', storage_path('keys/public.pem')); + if (!file_exists($publicKeyPath)) { + throw new \Exception('JWT public key not found'); + } + $publicKey = file_get_contents($publicKeyPath); + try { + return \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key($publicKey, 'RS256')); + } catch (\Exception $e) { + return false; + } + } + + public static function verifyClientToken($token) + { + try { + $parts = explode('.', $token); + if (count($parts) !== 3) { + return false; + } + $payload = json_decode(base64_decode(strtr($parts[1], '-_', '+/'))); + $clientId = $payload->sub ?? null; + + if (!$clientId) { + return false; + } + + $clients = config('api_clients.clients'); + $client = collect($clients)->where('api_key', $clientId)->first(); + + if (!$client || !isset($client['api_secret'])) { + return false; + } + + return \Firebase\JWT\JWT::decode($token, new \Firebase\JWT\Key($client['api_secret'], 'HS256')); + } catch (\Exception $e) { + return false; + } + } +} \ No newline at end of file diff --git a/config/api_clients.php b/config/api_clients.php new file mode 100644 index 00000000..d4d05cf2 --- /dev/null +++ b/config/api_clients.php @@ -0,0 +1,8 @@ + [[ + 'api_key' => env('LMS_API_KEY', 'lms'), + 'api_secret' => env('LMS_API_SECRET', '8f3d6c1a9e2b5f4d8c7a6b0e9d1f2a3c4b5e6f7d8a9c0b1e2d3f4a5b6c7d8e9f'), + ]] +]; \ No newline at end of file