fetchAuthData($packet->chainDataJwt); if(!Player::isValidUserName($extraData->displayName)){ $this->session->disconnectWithError(KnownTranslationFactory::disconnectionScreen_invalidName()); return true; } $clientData = $this->parseClientData($packet->clientDataJwt); try{ $skin = $this->session->getTypeConverter()->getSkinAdapter()->fromSkinData(ClientDataToSkinDataHelper::fromClientData($clientData)); }catch(\InvalidArgumentException | InvalidSkinException $e){ $this->session->getLogger()->debug("Invalid skin: " . $e->getMessage()); $this->session->disconnectWithError(KnownTranslationFactory::disconnectionScreen_invalidSkin()); return true; } if(!Uuid::isValid($extraData->identity)){ throw new PacketHandlingException("Invalid login UUID"); } $uuid = Uuid::fromString($extraData->identity); $arrClientData = (array) $clientData; $arrClientData["TitleID"] = $extraData->titleId; if($extraData->XUID !== ""){ $playerInfo = new XboxLivePlayerInfo( $extraData->XUID, $extraData->displayName, $uuid, $skin, $clientData->LanguageCode, $arrClientData ); }else{ $playerInfo = new PlayerInfo( $extraData->displayName, $uuid, $skin, $clientData->LanguageCode, $arrClientData ); } ($this->playerInfoConsumer)($playerInfo); $ev = new PlayerPreLoginEvent( $playerInfo, $this->session->getIp(), $this->session->getPort(), $this->server->requiresAuthentication() ); if($this->server->getNetwork()->getValidConnectionCount() > $this->server->getMaxPlayers()){ $ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_SERVER_FULL, KnownTranslationFactory::disconnectionScreen_serverFull()); } if(!$this->server->isWhitelisted($playerInfo->getUsername())){ $ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_SERVER_WHITELISTED, KnownTranslationFactory::pocketmine_disconnect_whitelisted()); } $banMessage = null; if(($banEntry = $this->server->getNameBans()->getEntry($playerInfo->getUsername())) !== null){ $banReason = $banEntry->getReason(); $banMessage = $banReason === "" ? KnownTranslationFactory::pocketmine_disconnect_ban_noReason() : KnownTranslationFactory::pocketmine_disconnect_ban($banReason); }elseif(($banEntry = $this->server->getIPBans()->getEntry($this->session->getIp())) !== null){ $banReason = $banEntry->getReason(); $banMessage = KnownTranslationFactory::pocketmine_disconnect_ban($banReason !== "" ? $banReason : KnownTranslationFactory::pocketmine_disconnect_ban_ip()); } if($banMessage !== null){ $ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_BANNED, $banMessage); } $ev->call(); if(!$ev->isAllowed()){ $this->session->disconnect($ev->getFinalDisconnectReason(), $ev->getFinalDisconnectScreenMessage()); return true; } $this->processLogin($packet, $ev->isAuthRequired()); return true; } /** * @throws PacketHandlingException */ protected function fetchAuthData(JwtChain $chain) : AuthenticationData{ /** @var AuthenticationData|null $extraData */ $extraData = null; foreach($chain->chain as $k => $jwt){ //validate every chain element try{ [, $claims, ] = JwtUtils::parse($jwt); }catch(JwtException $e){ throw PacketHandlingException::wrap($e); } if(isset($claims["extraData"])){ if($extraData !== null){ throw new PacketHandlingException("Found 'extraData' more than once in chainData"); } if(!is_array($claims["extraData"])){ throw new PacketHandlingException("'extraData' key should be an array"); } $mapper = new \JsonMapper(); $mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models $mapper->bExceptionOnMissingData = true; $mapper->bExceptionOnUndefinedProperty = true; try{ /** @var AuthenticationData $extraData */ $extraData = $mapper->map($claims["extraData"], new AuthenticationData()); }catch(\JsonMapper_Exception $e){ throw PacketHandlingException::wrap($e); } } } if($extraData === null){ throw new PacketHandlingException("'extraData' not found in chain data"); } return $extraData; } /** * @throws PacketHandlingException */ protected function parseClientData(string $clientDataJwt) : ClientData{ try{ [, $clientDataClaims, ] = JwtUtils::parse($clientDataJwt); }catch(JwtException $e){ throw PacketHandlingException::wrap($e); } $mapper = new \JsonMapper(); $mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models $mapper->bExceptionOnMissingData = true; $mapper->bExceptionOnUndefinedProperty = true; try{ $clientData = $mapper->map($clientDataClaims, new ClientData()); }catch(\JsonMapper_Exception $e){ throw PacketHandlingException::wrap($e); } return $clientData; } /** * TODO: This is separated for the purposes of allowing plugins (like Specter) to hack it and bypass authentication. * In the future this won't be necessary. * * @throws \InvalidArgumentException */ protected function processLogin(LoginPacket $packet, bool $authRequired) : void{ $this->server->getAsyncPool()->submitTask(new ProcessLoginTask($packet->chainDataJwt->chain, $packet->clientDataJwt, $authRequired, $this->authCallback)); $this->session->setHandler(null); //drop packets received during login verification } }