query('token'); if (! $token) { return $this->redirectToLogin('Token SSO tidak sah.', 'token_invalid'); } $secret = config('services.mykopkb.sso_secret'); if (! $secret) { return $this->redirectToLogin( app()->environment('local', 'development') ? 'MYKOPKB_SSO_SECRET belum dikonfigurasi dalam .env.' : 'Token SSO tamat tempoh atau tidak sah.', 'token_invalid' ); } $payload = JwtVerifier::verify($token, $secret, [ 'issuer' => config('services.mykopkb.sso_issuer', 'mykopkb'), 'audience' => config('services.mykopkb.sso_audience', 'e-vote'), ]); if (! $payload) { return $this->redirectToLogin('Token SSO tamat tempoh atau tidak sah.', 'token_invalid'); } $jti = $payload['jti'] ?? null; if (! $jti || Cache::has("sso_jti:$jti")) { return $this->redirectToLogin('Token SSO telah digunakan.', 'token_used'); } $icNumber = preg_replace('/[\s-]+/', '', (string) ($payload['ic_number'] ?? '')); $memberNumber = trim((string) ($payload['member_number'] ?? '')); if ($icNumber === '' || $memberNumber === '') { return $this->redirectToLogin('Token SSO tidak sah.', 'token_invalid'); } $voter = Voter::query() ->where('no_kp', $icNumber) ->where('no_anggota', $memberNumber) ->where('election_id', Util::getCurrentElection()) ->first(); if (! $voter) { activity() ->withProperties([ 'ic_number' => $icNumber, 'member_number' => $memberNumber, 'election_id' => Util::getCurrentElection(), 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), ]) ->log('voter sso login rejected: not in current election'); return $this->redirectToLogin( 'Akaun pengundi tidak dijumpai untuk pilihan raya semasa. Sila daftar di kaunter IT atau log masuk secara manual.', 'voter_not_found' ); } Cache::put("sso_jti:$jti", true, now()->addMinutes(5)); ApiTokenLifetime::revokeActiveTokens($voter); $passportToken = $voter->createToken('SSO Login', ['vote'])->accessToken; $meta = ApiTokenLifetime::loginMeta('voter'); activity() ->performedOn($voter) ->withProperties([ 'election_id' => Util::getCurrentElection(), 'voter_id' => $voter->id, 'voter_name' => $voter->name, 'no_kp' => $voter->no_kp, 'no_anggota' => $voter->no_anggota, 'sso_sub' => $payload['sub'] ?? null, 'ip' => $request->ip(), 'user_agent' => $request->userAgent(), ]) ->log("voter sso login: {$voter->name}"); $query = http_build_query([ 'sso_token' => $passportToken, 'token_expires_at' => $meta['token_expires_at'], ]); return redirect('/login?'.$query); } private function redirectToLogin(string $message, string $code = 'sso_error') { return redirect('/login?'.http_build_query([ 'sso_error' => $message, 'sso_error_code' => $code, ])); } }