login('admin@example.test',$password); $secret=OTPHP\TOTP::generate(new MfaClock())->getSecret(); $_SESSION['mfa_pending']=['user'=>1,'secret'=>$secret,'expires'=>time()+600]; $codes=$mfa->enroll(1,$secret,OTPHP\TOTP::createFromSecret($secret,new MfaClock())->now(),$password); $base=$_SESSION;$states=[]; foreach([$codes[0],$codes[1]] as $code){$_SESSION=$base;$mfa->beginReplacement(1,$code,$password);$states[]=$_SESSION;} ob_clean();echo json_encode(['states'=>$states,'recovery'=>$codes[2]]);ob_end_flush();exit; } $payload=json_decode(trim(fgets(STDIN)),true,512,JSON_THROW_ON_ERROR);$_SESSION=$payload['state']; ob_clean();fwrite(STDOUT,"READY\n");fflush(STDOUT);fgets(STDIN); try{ if($argv[1]==='replace'){ $secret=$_SESSION['mfa_replacement']['secret']; $codes=$mfa->completeReplacement(1,OTPHP\TOTP::createFromSecret($secret,new MfaClock())->now(),$password); $ok=count($codes)===10; }else $ok=$mfa->verify(1,$payload['recovery'],$password); $result=['ok'=>$ok]; }catch(AppException $error){$result=['ok'=>false,'status'=>$error->status];} ob_clean();echo json_encode($result);ob_end_flush();