resolveSystem($code); $this->assertSystemAvailable($system); $this->assertUserEligible($user, $system); if (! $system->sso_enabled) { throw new ExternalSystemLaunchException( 'Sistem luaran tidak menyokong SSO.', 'external_system_sso_disabled', 422, ); } if (blank($system->sso_secret)) { throw new ExternalSystemLaunchException( 'Sistem luaran belum dikonfigurasi untuk SSO.', 'external_system_sso_not_configured', 503, ); } $ttl = $system->sso_token_ttl ?: 120; $issuedAt = now(); $expiresAt = $issuedAt->copy()->addSeconds($ttl); $payload = [ 'iss' => config('externalsystem.issuer', config('app.name', 'mykopkb')), 'aud' => $system->sso_audience ?: $system->code, 'sub' => $user->id, 'jti' => (string) Str::uuid(), 'iat' => $issuedAt->timestamp, 'nbf' => $issuedAt->timestamp, 'exp' => $expiresAt->timestamp, 'ic_number' => $user->ic_number, 'member_number' => $user->member_number, ]; $token = JwtSigner::sign($payload, $system->sso_secret); $launchPath = $system->sso_launch_path ?: '/sso/login'; $launchUrl = rtrim($system->url, '/').'/'.ltrim($launchPath, '/'); $launchUrl .= '?token='.urlencode($token); return [ 'launch_url' => $launchUrl, 'expires_at' => $expiresAt->toIso8601String(), ]; } protected function resolveSystem(string $code): ExternalSystem { $system = ExternalSystem::query()->where('code', $code)->first(); if (! $system) { throw new ExternalSystemLaunchException( 'Sistem luaran tidak dijumpai.', 'external_system_not_found', 404, ); } return $system; } protected function assertSystemAvailable(ExternalSystem $system): void { if (! $system->is_active) { throw new ExternalSystemLaunchException( 'Sistem luaran tidak aktif.', 'external_system_inactive', 403, ); } $now = now(); if ($system->starts_at?->isAfter($now)) { throw new ExternalSystemLaunchException( 'Sistem luaran belum tersedia.', 'external_system_upcoming', 403, ); } if ($system->ends_at?->isBefore($now)) { throw new ExternalSystemLaunchException( 'Tempoh akses sistem luaran telah tamat.', 'external_system_ended', 403, ); } } protected function assertUserEligible(User $user, ExternalSystem $system): void { if ($user->status !== 'active') { throw new ExternalSystemLaunchException( 'Hanya anggota aktif boleh membuka sistem luaran.', 'external_system_user_inactive', 403, ); } if (blank($user->ic_number)) { throw new ExternalSystemLaunchException( 'Nombor kad pengenalan diperlukan sebelum membuka sistem luaran.', 'external_system_ic_required', 422, ); } if (blank($user->member_number)) { throw new ExternalSystemLaunchException( 'Nombor anggota diperlukan sebelum membuka sistem luaran.', 'external_system_member_number_required', 422, ); } if ($system->require_onboarding && blank($user->onboarding_completed_at)) { throw new ExternalSystemLaunchException( 'Sila lengkapkan profil anda sebelum membuka sistem luaran.', 'external_system_profile_incomplete', 422, ); } } }