The converted accounts */ public function migrate(mixed $migrationPayload) : Collection { $json = json_decode(htmlspecialchars_decode($migrationPayload), true); if (is_null($json)) { Log::error('2FAuth JSON migration data cannot be read'); throw new InvalidMigrationDataException('2FAuth'); } $twofaccounts = []; foreach ($json['data'] as $key => $otp_parameters) { $parameters = []; $parameters['otp_type'] = $otp_parameters['otp_type']; $parameters['service'] = $otp_parameters['service']; $parameters['account'] = $otp_parameters['account']; $parameters['secret'] = $this->padToValidBase32Secret($otp_parameters['secret']); $parameters['algorithm'] = $otp_parameters['algorithm'] ?? null; $parameters['digits'] = $otp_parameters['digits'] ?? null; $parameters['legacy_uri'] = $otp_parameters['legacy_uri']; $parameters['counter'] = strtolower($parameters['otp_type']) === 'hotp' && $otp_parameters['counter'] > 0 ? $otp_parameters['counter'] : null; $parameters['period'] = strtolower($parameters['otp_type']) === 'totp' && $otp_parameters['period'] > 0 ? $otp_parameters['period'] : null; try { if (Arr::has($otp_parameters, 'icon_file') && Arr::has($otp_parameters, 'icon_mime')) { switch ($otp_parameters['icon_mime']) { case 'image/svg+xml': $parameters['iconExt'] = 'svg'; break; case 'image/png': $parameters['iconExt'] = 'png'; break; case 'image/jpeg': $parameters['iconExt'] = 'jpg'; break; case 'image/bmp': $parameters['iconExt'] = 'bmp'; break; case 'image/x-ms-bmp': $parameters['iconExt'] = 'bmp'; break; case 'image/webp': $parameters['iconExt'] = 'webp'; break; default: throw new \Exception(); } $parameters['icon_file'] = base64_decode($otp_parameters['icon_file']); } } catch (\Exception) { // we do nothing } try { $twofaccounts[$key] = new TwoFAccount; $twofaccounts[$key]->fillWithOtpParameters($parameters, Arr::has($parameters, 'iconExt')); if (Arr::has($parameters, 'iconExt')) { $twofaccounts[$key]->setIcon($parameters['icon_file'], $parameters['iconExt']); } } catch (\Exception $exception) { Log::error(sprintf('Cannot instanciate a TwoFAccount object with 2FAS imported item #%s', $key)); Log::debug($exception->getMessage()); // The token failed to generate a valid account so we create a fake account to be returned. $fakeAccount = new TwoFAccount(); $fakeAccount->id = TwoFAccount::FAKE_ID; $fakeAccount->otp_type = $otp_parameters['otp']['tokenType'] ?? TwoFAccount::TOTP; // Only basic fields are filled to limit the risk of another exception. $fakeAccount->account = $otp_parameters['otp']['account'] ?? __('twofaccounts.import.invalid_account'); $fakeAccount->service = $otp_parameters['name'] ?? __('twofaccounts.import.invalid_service'); // The secret field is used to pass the error, not very clean but will do the job for now. $fakeAccount->secret = $exception->getMessage(); $twofaccounts[$key] = $fakeAccount; } } return collect($twofaccounts); } }