BookStackApp/BookStack · error · Error

Invalid ACS Response; Errors: {implode(', ', $errors)}; Reas

Error message

Invalid ACS Response; Errors: {implode(', ', $errors)}; Reason: {$reason}

What it means

Thrown by Saml2Service::processAcsResponse when the OneLogin php-saml toolkit's processResponse() records errors via getErrors() after consuming the SAML Response POSTed to the Assertion Consumer Service (ACS). The message aggregates all toolkit error strings plus getLastErrorReason() for detail. It means the IdP's SAML response could not be accepted/validated — the SP refuses to proceed with login, before isAuthenticated() is even checked.

Source

Thrown at app/Access/Saml2Service.php:108

     * @throws SamlException
     * @throws ValidationError
     * @throws JsonDebugException
     * @throws UserRegistrationException
     */
    public function processAcsResponse(?string $requestId, string $samlResponse): ?User
    {
        // The SAML2 toolkit expects the response to be within the $_POST superglobal
        // so we need to manually put it back there at this point.
        $_POST['SAMLResponse'] = $samlResponse;
        $toolkit = $this->getToolkit();
        $toolkit->processResponse($requestId);
        $errors = $toolkit->getErrors();

        if (!empty($errors)) {
            $reason = $toolkit->getLastErrorReason();
            $message = 'Invalid ACS Response; Errors: ' . implode(', ', $errors);
            $message .= $reason ? "; Reason: {$reason}" : '';
            throw new Error($message);
        }

        if (!$toolkit->isAuthenticated()) {
            return null;
        }

        $attrs = $toolkit->getAttributes();
        $id = $toolkit->getNameId();
        session()->put('saml2_session_index', $toolkit->getSessionIndex());

        return $this->processLoginCallback($id, $attrs);
    }

    /**
     * Process a response for the single logout service.
     *
     * @throws Error
     */

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Read the 'Reason:' suffix in the message — getLastErrorReason() names the exact validation failure; fix that specific mismatch first.
  2. Verify the IdP x509 certificate in BookStack's SAML settings matches the signing cert currently used by the IdP (re-download IdP metadata after any rotation).
  3. Sync server time (ntp/chrony) — clock skew is the most common cause of 'too old/not yet valid' SAML response errors.
  4. Confirm entityId, ACS (callback) URL, and audience values match exactly between BookStack and the IdP configuration.
  5. Enable SAML debug (SAML_DEBUG=true / toolkit debug setting) and compare the raw response against IdP logs to see which assertion constraint failed.

Example fix

// before (.env, stale IdP cert)
SAML2_IDP_x509=MIIC...old-cert...

// after (.env, refreshed from current IdP metadata)
SAML2_IDP_x509=MIIC...new-cert...
Defensive patterns

Strategy: try-catch

Validate before calling

// Preconditions to verify before attempting ACS login:
// 1. IdP cert in SP settings matches the current IdP signing cert
// 2. Server clock in sync: chronyc tracking / ntpq -p (drift < ~1 min)
// 3. entityId, ACS URL and audience match IdP-side SP configuration
// 4. A fresh AuthNRequest was issued and its requestId matches the one passed in

Try / catch

use OneLogin\Saml2\Error as Saml2Error;

try {
    $user = $saml2Service->processAcsResponse($requestId, $samlResponse);
} catch (Saml2Error $e) {
    // Message contains 'Errors: ...; Reason: ...' — log the reason for IdP support tickets
    report($e);
    abort(500, 'SAML login failed validation. Check the Reason in logs and IdP cert/clock settings.');
}

Prevention

When it happens

Trigger: processAcsResponse() is called on the ACS callback with a POSTed SAMLResponse; toolkit->processResponse($requestId) fails with validation errors such as: response signature invalid (wrong IdP x509 cert configured), clock skew ('SAML Response too old' / NotOnOrAfter in the past due to server time drift), invalid audience/destination (SP entity_id or ACS URL mismatch), missing requested NameID format, or the request-id doesn't match the pending auth request.

Common situations: IdP certificate rotated (or test/prod IdP mixed up) so signature validation fails; server clocks out of sync causing 'response is too old'; SAML settings (entityId, ACS URL, idp.entityId/idp.sso URL) mismatched between BookStack .env/IdP metadata; ADFS or Azure AD sending lowercase-encoded query params or unexpected NameID formats; replaying an old SAML response.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/dde1719d27b61bd4. Report an issue: GitHub.