{"record":{"id":"958b0db1f738c758","repo":"ellite/Wallos","slug":"unsupported-s-otp-type","errorCode":null,"errorMessage":"Unsupported \"%s\" OTP type","messagePattern":"Unsupported \"(.+?)\" OTP type","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"libs/OTPHP/Factory.php","lineNumber":88,"sourceCode":"\n        $otp->setIssuer($result[0]);\n    }\n\n    private static function createOTP(Url $parsed_url, ClockInterface $clock): OTPInterface\n    {\n        switch ($parsed_url->getHost()) {\n            case 'totp':\n                $totp = TOTP::createFromSecret($parsed_url->getSecret(), $clock);\n                $totp->setLabel(self::getLabel($parsed_url->getPath()));\n\n                return $totp;\n            case 'hotp':\n                $hotp = HOTP::createFromSecret($parsed_url->getSecret());\n                $hotp->setLabel(self::getLabel($parsed_url->getPath()));\n\n                return $hotp;\n            default:\n                throw new InvalidArgumentException(sprintf('Unsupported \"%s\" OTP type', $parsed_url->getHost()));\n        }\n    }\n\n    /**\n     * @param non-empty-string $data\n     * @return non-empty-string\n     */\n    private static function getLabel(string $data): string\n    {\n        $result = explode(':', rawurldecode(mb_substr($data, 1)));\n        $label = count($result) === 2 ? $result[1] : $result[0];\n        assert($label !== '');\n\n        return $label;\n    }\n}\n","sourceCodeStart":70,"sourceCodeEnd":105,"githubUrl":"https://github.com/ellite/Wallos/blob/52820e87ca5a6e105fdbb7f1c0c681bc0cfee2fd/libs/OTPHP/Factory.php#L70-L105","documentation":"Factory::createOTP only supports the 'totp' and 'hotp' otpauth URI types (the URL host). When a provisioning URI's host is anything else, an InvalidArgumentException is thrown. This guards against malformed or non-standard otpauth:// URIs.","triggerScenarios":"loadFromProvisioningUri() is given a URI whose host part (the OTP type segment, e.g. otpauth://foo/...) is not exactly 'totp' or 'hotp' — e.g. typos like 'TOTP' (case handled?) or 'otp', or a URI missing the type segment entirely.","commonSituations":"Hand-edited provisioning URIs, URIs generated by third-party authenticator tools using non-standard type names, copying URIs from QR decoders that mangle the scheme/host, or passing a plain secret instead of a full otpauth URI.","solutions":["Fix the provisioning URI so the type segment is exactly 'totp' or 'hotp' (lowercase): otpauth://totp/Label?secret=...","If you only have a secret, construct the OTP directly with TOTP::create($secret) or HOTP::createFromSecret($secret) instead of parsing a URI.","Validate/normalize the URI with a regex like /^otpauth:\\/\\/(totp|hotp)\\// before calling the factory.","Check the URI was not URL-decoded/mangled upstream (e.g. scheme stripped so the host becomes something unexpected)."],"exampleFix":"// before\n$otp = $factory->loadFromProvisioningUri('otpauth://TOTP/alice?secret=JBSW...');\n// after\n$otp = $factory->loadFromProvisioningUri('otpauth://totp/alice?secret=JBSW...');","handlingStrategy":"validation","validationCode":"if (!preg_match('#^otpauth://(totp|hotp)/#', $uri)) {\n    throw new InvalidArgumentException('Provisioning URI must be otpauth://totp/ or otpauth://hotp/');\n}","typeGuard":"function isSupportedOtpType(string $uri): bool {\n    $host = parse_url($uri, PHP_URL_HOST);\n    return $host === 'totp' || $host === 'hotp';\n}","tryCatchPattern":"try {\n    $otp = $factory->loadFromProvisioningUri($uri);\n} catch (InvalidArgumentException $e) {\n    log_error('Unsupported OTP type in URI', ['uri' => $uri]);\n    throw new InvalidProvisioningUriException($uri, $e);\n}","preventionTips":["Only generate URIs via the library's own getProvisioningUri()","Regex-validate otpauth URIs from external sources before parsing","Lowercase the type segment before passing to the factory","Never hand-build otpauth:// URIs with string concatenation"],"tags":["php","otp","provisioning-uri","invalid-input"],"backgroundTag":"invalid-enum-value","analyzedSha":"52820e87ca5a6e105fdbb7f1c0c681bc0cfee2fd","analyzedAt":"2026-09-13T14:09:30.873Z","contentChangedAt":"2026-09-13T14:09:30.873Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}