apereo/cas · warning

SP SSODescriptor in the metadata has expired at

Error message

SP SSODescriptor in the metadata has expired at [{}]

What it means

Both the EntityDescriptor and its SPSSODescriptor were located, but the SPSSODescriptor's own validUntil timestamp is before now (UTC). getAdaptor() logs a warning and returns Optional.empty(), refusing to hand back an adaptor whose endpoint/key validity window has closed.

Solutions

  1. Refresh the metadata from the SP so the SPSSODescriptor carries a current validUntil, then reload it in CAS.
  2. Inspect the XML: check <SPSSODescriptor validUntil=...> (not just <EntityDescriptor>) to see which level expired.
  3. Switch from a snapshot file to the SP's live metadata URL so refreshes happen automatically.
  4. Have the SP administrator republish metadata without a restrictive validUntil if the window is unnecessarily short.
  5. Purge CAS's cached metadata for this service so the renewed document is picked up immediately.

Example fix

// before (in SP metadata)
// <SPSSODescriptor validUntil="2024-06-01T00:00:00Z" ...>
// after
// <SPSSODescriptor validUntil="2030-01-01T00:00:00Z" ...>  (or omit validUntil)
Defensive patterns

Strategy: validation

Validate before calling

var descriptor = entityDescriptor != null ? entityDescriptor.getSPSSODescriptor(supportedNameIdFormat) : null;
if (descriptor != null && descriptor.getValidUntil() != null) {
    var validUntil = DateTimeUtils.zonedDateTimeOf(descriptor.getValidUntil());
    if (validUntil.isBefore(ZonedDateTime.now(ZoneOffset.UTC))) {
        throw new IllegalStateException("SPSSODescriptor expired at " + validUntil
            + " — republish SP metadata");
    }
}

Type guard

boolean ssoDescriptorCurrent(SPSSODescriptor sso) {
    if (sso == null || sso.getValidUntil() == null) return true;
    return DateTimeUtils.zonedDateTimeOf(sso.getValidUntil()).isAfter(ZonedDateTime.now(ZoneOffset.UTC));
}

Try / catch

adaptorResolver.get(registeredService, entityID)
    .filter(adaptor -> adaptor.getValidUntil() == null
        || adaptor.getValidUntil().isAfter(ZonedDateTime.now(ZoneOffset.UTC)))
    .orElseThrow(() -> new SamlException("SP SSODescriptor expired — refresh metadata for " + entityID));

Prevention

When it happens

Trigger: Calling get(entityID, ...) when the resolved SPSSODescriptor (entityDescriptor.getSPSSODescriptor(supportedNameIdFormat)) has a non-null validUntil earlier than ZonedDateTime.now(ZoneOffset.UTC).

Common situations: SP re-publishes metadata with short validity periods but the IdP cached the old document; a static metadata export was taken days ago and its descriptor-level validUntil has since passed; mismatch between entity-level (fine) and role-descriptor-level (expired) validity, which is easy to miss when eyeballing the XML.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/79d5ded4c930e743. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-saml-idp-core/src/main/java/org/apereo/cas/support/saml/services/idp/metadata/SamlRegisteredServiceMetadataAdaptor.java:140

        } catch (final Exception e) {
            LoggingUtils.error(LOGGER, e);
        }
        return Optional.empty();
    }

    private static Optional<SamlRegisteredServiceMetadataAdaptor> getAdaptor(
        final String entityID,
        final MetadataResolver chainingMetadataResolver,
        final EntityDescriptor entityDescriptor) {
        val ssoDescriptor = entityDescriptor.getSPSSODescriptor(SAMLConstants.SAML20P_NS);
        if (ssoDescriptor != null) {
            LOGGER.debug("Located SP SSODescriptor in metadata for [{}]. Metadata is valid until [{}]", entityID,
                ObjectUtils.getIfNull(ssoDescriptor.getValidUntil(), "forever"));
            if (ssoDescriptor.getValidUntil() != null) {
                val validUntil = DateTimeUtils.zonedDateTimeOf(ssoDescriptor.getValidUntil());
                val expired = validUntil.isBefore(ZonedDateTime.now(ZoneOffset.UTC));
                if (expired) {
                    LOGGER.warn("SP SSODescriptor in the metadata has expired at [{}]", ssoDescriptor.getValidUntil());
                    return Optional.empty();
                }
            }
            return Optional.of(new SamlRegisteredServiceMetadataAdaptor(ssoDescriptor,
                entityDescriptor, chainingMetadataResolver));
        }
        LOGGER.warn("Could not locate SP SSODescriptor in the metadata for [{}]", entityID);
        return Optional.empty();
    }

    public ZonedDateTime getValidUntil() {
        return DateTimeUtils.zonedDateTimeOf(this.ssoDescriptor.getValidUntil());
    }

    public Organization getOrganization() {
        return this.ssoDescriptor.getOrganization();
    }

View on GitHub (pinned to e7288fc434)