BookStackApp/BookStack · error · SamlException

errors.saml_no_email_address

Error message

errors.saml_no_email_address

What it means

SamlException thrown by BookStack's SAML2 login flow when the IdP response contains user details but no email address. BookStack requires an email to find or register the local user matching the SAML identity, so a response lacking the email attribute is rejected. The check runs in processLoginCallback after the SAML attributes have been parsed into $userDetails.

Source

Thrown at app/Access/Saml2Service.php:361

    public function processLoginCallback(string $samlID, array $samlAttributes): User
    {
        $userDetails = $this->getUserDetails($samlID, $samlAttributes);
        $isLoggedIn = auth()->check();

        if ($this->shouldSyncGroups()) {
            $userDetails['groups'] = $this->getUserGroups($samlAttributes);
        }

        if ($this->config['dump_user_details']) {
            throw new JsonDebugException([
                'id_from_idp'         => $samlID,
                'attrs_from_idp'      => $samlAttributes,
                'attrs_after_parsing' => $userDetails,
            ]);
        }

        if (empty($userDetails['email'])) {
            throw new SamlException(trans('errors.saml_no_email_address'));
        }

        if ($isLoggedIn) {
            throw new SamlException(trans('errors.saml_already_logged_in'), '/login');
        }

        $user = $this->registrationService->findOrRegister(
            $userDetails['name'],
            $userDetails['email'],
            $userDetails['external_id']
        );

        if ($this->shouldSyncGroups()) {
            $this->groupSyncService->syncUserWithFoundGroups($user, $userDetails['groups'], $this->config['remove_from_groups']);
        }

        $this->loginService->login($user, 'saml2');

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Check the email attribute name configured for the SAML2 IdP in BookStack (Admin > SAML2 settings / 'External user id' style option mapping) and set it to the exact attribute the IdP releases (e.g. http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress or mail)
  2. Verify in the IdP admin console that the email attribute/claim is included in the assertion for this service provider
  3. Inspect the debug dump (attrs_from_idp / attrs_after_parsing logged when debug is enabled) to see which attributes actually arrived and adjust the mapping
  4. If the IdP cannot send email, enable the 'email is determined from username/ID' style fallback by mapping email to an attribute that is always present

Example fix

// before (BookStack saml2 option: email attribute set to a claim the IdP doesn't send)
$emailAttributeName = 'email';
// after (match the IdP's actual claim name)
$emailAttributeName = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress';
Defensive patterns

Strategy: validation

Validate before calling

// Before invoking the SAML login flow, ensure the IdP attribute mapping yields an email
// (e.g. run a test decode of a sample assertion or verify mapping config)
$attrs = $samlAttributes; // attributes released by IdP
$emailKey = config('saml2.email_attribute');
if (empty($attrs[$emailKey][0] ?? null)) {
    throw new \RuntimeException("IdP does not release the '{$emailKey}' attribute; fix attribute mapping/release policy before login.");
}

Type guard

function samlDetailsHaveEmail(array $userDetails): bool
{
    return isset($userDetails['email']) && filter_var($userDetails['email'], FILTER_VALIDATE_EMAIL) !== false;
}

Try / catch

use BookStack\Exceptions\SamlException;

try {
    $saml->processLoginCallback();
} catch (SamlException $e) {
    if ($e->getMessage() === trans('errors.saml_no_email_address')) {
        Log::warning('SAML login failed: IdP sent no email attribute', ['attributes' => $samlAttributes]);
        return redirect('/login')->with('error', 'Your identity provider did not share an email address.');
    }
    throw $e;
}

Prevention

When it happens

Trigger: The IdP sends a successful assertion whose attribute statement does not include an email attribute (e.g. the configured email attribute name in BookStack's saml2 idp settings does not match any attribute the IdP actually releases, or the IdP releases no attributes at all).

Common situations: IdP attribute mapping misconfiguration (email attribute name changed or set to a custom claim the IdP never sends), IdP attribute release policy filtering email for privacy, switching IdPs (e.g. Azure AD sends 'email' only for guest-allowed accounts) or upgrading an IdP that renamed its claims.

Related errors


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