{"record":{"id":"7d4d2fb7d3130348","repo":"BookStackApp/BookStack","slug":"invalid-sp-metadata","errorCode":null,"errorMessage":"Invalid SP metadata: ","messagePattern":"Invalid SP metadata: ","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/Access/Saml2Service.php","lineNumber":164,"sourceCode":"        $defaultBookStackRedirect = $this->loginService->logout();\n\n        return $samlRedirect ?? $defaultBookStackRedirect;\n    }\n\n    /**\n     * Get the metadata for this service provider.\n     *\n     * @throws Error\n     */\n    public function metadata(): string\n    {\n        $toolKit = $this->getToolkit(true);\n        $settings = $toolKit->getSettings();\n        $metadata = $settings->getSPMetadata();\n        $errors = $settings->validateMetadata($metadata);\n\n        if (!empty($errors)) {\n            throw new Error(\n                'Invalid SP metadata: ' . implode(', ', $errors),\n                Error::METADATA_SP_INVALID\n            );\n        }\n\n        return $metadata;\n    }\n\n    /**\n     * Load the underlying Onelogin SAML2 toolkit.\n     *\n     * @throws Error\n     * @throws Exception\n     */\n    protected function getToolkit(bool $spOnly = false): Auth\n    {\n        $settings = $this->config['onelogin'];\n        $overrides = $this->config['onelogin_overrides'] ?? [];","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Access/Saml2Service.php#L146-L182","documentation":"Thrown by Saml2Service::metadata() when the OneLogin toolkit's Settings::validateMetadata() reports problems with the generated Service Provider (SP) XML metadata. The toolkit builds SP metadata from BookStack's SAML settings; if the resulting document fails validation (missing or invalid entityID, bad certificate format, invalid URLs), an Error with code METADATA_SP_INVALID is thrown. This endpoint is what admins feed to their IdP, so it blocks SAML setup entirely.","triggerScenarios":"Visiting the /saml2/metadata route; getToolkit(true) builds SP-only settings and getSPMetadata() produces XML that validateMetadata() rejects — e.g. missing required SAML2_* env options (entityId, SP cert/private key with strict settings), an x509 cert that isn't valid PEM, entityId or URLs that are empty/not valid URIs, or malformed user-provided settings arrays.","commonSituations":"Fresh BookStack SAML setup where the SP certificate/private key options were pasted without headers or with wrong line endings; entityId left unset or containing spaces; SAML enabled (SAML2_ENABLED=true) before all required SP options were configured; cert generated with a tool producing DER instead of PEM; trailing slashes/typos in the callback URL options.","solutions":["Read the error list in the message — validateMetadata names each invalid field; fix those specific SAML2_SP_* options (entityId, assertion URL, etc.) in .env or settings.","If SP signing is required, ensure SAML2_SP_x509 and SAML2_SP_privateKey contain full PEM blocks including '-----BEGIN CERTIFICATE-----'/'-----BEGIN PRIVATE KEY-----' headers and correct line breaks.","Regenerate a proper self-signed cert pair: 'openssl req -x509 -newkey rsa:2048 -nodes -keyout sp.key -out sp.crt -days 3650 -subj \"/CN=bookstack\"' and paste the PEM contents.","Confirm SAML2_SP_entityId is a non-empty unique URI and the ACS/SLS URLs are absolute, well-formed URLs matching your BookStack instance.","Re-fetch the metadata URL after fixes and validate the XML at the IdP before retrying login."],"exampleFix":"// before (.env, cert pasted without PEM headers)\nSAML2_SP_x509=MIIC7jCCAdOgAwIBAgIU...\n\n// after (.env, complete PEM)\nSAML2_SP_x509=-----BEGIN CERTIFICATE-----\nMIIC7jCCAdOgAwIBAgIU...\n-----END CERTIFICATE-----","handlingStrategy":"validation","validationCode":"// Validate SP SAML options before calling metadata()/enabling SAML:\n$required = ['SAML2_IDP_entityId', 'SAML2_IDP_sso', 'SAML2_SP_entityId'];\nforeach ($required as $opt) {\n    if (empty(env($opt))) {\n        throw new \\RuntimeException(\"Missing SAML option: {$opt}\");\n    }\n}\n// If SP signing enabled, require full PEM blocks:\n$cert = env('SAML2_SP_x509');\nif ($cert !== null && !str_starts_with($cert, '-----BEGIN CERTIFICATE-----')) {\n    throw new \\RuntimeException('SAML2_SP_x509 must be a full PEM certificate');\n}","typeGuard":null,"tryCatchPattern":"use OneLogin\\Saml2\\Error as Saml2Error;\n\ntry {\n    $xml = $saml2Service->metadata();\n} catch (Saml2Error $e) {\n    if ($e->getCode() === Saml2Error::METADATA_SP_INVALID) {\n        abort(500, 'SP metadata invalid: ' . $e->getMessage() . ' — check SAML2_SP_* options and PEM certs.');\n    }\n    throw $e;\n}","preventionTips":["Paste certificates as complete PEM blocks with BEGIN/END headers and real newlines","Set a unique, non-empty entityId and absolute ACS/SLS URLs before enabling SAML","Fetch /saml2/metadata and validate the XML (xmllint or the IdP import) before pointing users at SSO","Document which SAML2_* env vars are mandatory so fresh installs don't miss options","Regenerate expiring self-signed SP certs before they lapse"],"tags":["php","saml","sso","metadata","configuration"],"backgroundTag":"invalid-sp-metadata","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}