context = $context; $this->serial = base64_encode(random_bytes(9)); } public static function create(Context $context): ?static { return new static($context); } public abstract function getInputs(): array; public abstract function validate( AuthProvider $provider, array $inputData ): ChallengeResult; public function addAsHtml(\SimpleXMLElement $parent): void { $form = $parent->addChild('form'); $form->addAttribute('method', 'post'); foreach ($this->getInputs() as $input) { $input->addAsHtml($form); $this->inputs[$input->serial] = $input; } $serial = $form->addChild('input'); $serial->addAttribute('type', 'hidden'); $serial->addAttribute('name', 'challenge'); $serial->addAttribute('value', $this->serial); $submit = $form->addChild('button', L('challenge.continue')); } public function findInput(string $serial): ?Input { return $this->inputs[$serial] ?? null; } protected static function requireInterface( AuthProvider $provider, string $interface ): void { if (!($provider instanceof $interface)) { throw new \InvalidArgumentException( "Provider must implement $interface" ); }; } protected static function requireKnownUser(Context $context): void { if (!isset($context->user)) { throw new \RuntimeException( 'Unknown user, but known user required for ' . static::class ); } } }