Skip to content

ServiceProvider() silently parses IdP metadata — no role validation #630

Description

@PrasanthVijayy

Hi @tngan,

I want notify you something really helpful.

Summary

When IdP metadata (an <EntityDescriptor> containing <IDPSSODescriptor>) is passed to ServiceProvider({ metadata }), the instance is constructed successfully and entityMeta.getEntityID() returns a
value. There is no error, warning, or way to detect that the supplied metadata is for the wrong role. This makes it easy to silently mis-ingest IdP metadata in an SP-metadata upload flow.

Environment

  • samlify: 2.13.1
  • Node: 24.x
  • Schema validator: @authenio/samlify-xsd-schema-validator

Steps to reproduce

import * as saml from 'samlify';

// This is IdP metadata (contains <IDPSSODescriptor>, no <SPSSODescriptor>)
const idpMetadataXml = `<?xml version="1.0"?>
<EntityDescriptor entityID="https://idp.example.com/metadata" xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
  <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
                         Location="https://idp.example.com/sso"/>
  </IDPSSODescriptor>
</EntityDescriptor>`;

const sp = saml.ServiceProvider({ metadata: idpMetadataXml });

console.log(sp.entityMeta.getEntityID());                       // "https://idp.example.com/metadata" ✅ returns
console.log(sp.entityMeta.getAssertionConsumerService('post')); // undefined

Expected

One of:

  • Construction throws / surfaces a clear error when the supplied metadata does not contain an SPSSODescriptor, or
  • A way to introspect the role of parsed metadata (e.g. getMetadataType() / hasSPSSODescriptor() / a documented note) so callers can validate input themselves.

Actual

  • ServiceProvider is built without error from IdP metadata.

    • getEntityID() returns a value (it reads the root <EntityDescriptor>, which is shared by both roles).
    • SP-specific getters (getAssertionConsumerService(...)) return undefined.
    • No signal distinguishes "valid SP metadata with no ACS configured" from "wrong-role metadata supplied".

    Root cause (from reading the source)

    • SpMetadata (build/src/metadata-sp.js) extracts via fixed localPaths targeting SPSSODescriptor only. On IdP metadata those paths match nothing → fields are undefined. The XML is well-formed, so
      no parse error occurs.
    • IDPSSODescriptor extraction exists only in IdpMetadata (localPath: ['EntityDescriptor','IDPSSODescriptor', ...]); SpMetadata never inspects it.
    • getEntityID() is on the base Metadata class and reads the shared root, so it returns regardless of role.
    • The XSD validator only checks SAML-metadata schema conformance — IdP metadata is valid SAML metadata, so it passes.

    Proposed enhancement

    A non-breaking option would be ideal, since some entities legitimately publish a single <EntityDescriptor> with both SPSSODescriptor and IDPSSODescriptor:

    • Add a role-introspection helper on Metadata/SpMetadata/IdpMetadata (e.g. getMetadataType() returning the descriptors present), and/or
    • An opt-in strict flag (e.g. ServiceProvider({ metadata, requireSPSSODescriptor: true })) that throws when the expected descriptor is absent.

    Current workaround

    Detect the descriptor manually before constructing the entity:

    if (!xml.includes('SPSSODescriptor')) {
      throw new Error(
        xml.includes('IDPSSODescriptor')
          ? 'IdP metadata supplied — SP metadata required'
          : 'Not SP metadata — SPSSODescriptor missing'
      );
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions