RocketChat/Rocket.Chat · error · Error
An assertion with the same ID cannot be used more than once.
Error message
An assertion with the same ID cannot be used more than once.
What it means
SAML assertions must be consumed exactly once. Rocket.Chat records each assertion id + issuer in the SamlUsedAssertions collection with an expiry derived from the assertion lifetime plus allowed clock drift; when markUsed returns false the id was already recorded, so the response is treated as a replay: 'SAML assertion replay detected' is logged and this Error is thrown, aborting the login.
Source
Thrown at apps/meteor/server/lib/saml/lib/SAML.ts:528
throw new Error('Unable to validate response url');
}
if (!profile) {
throw new Error('No user data collected from IdP response.');
}
const baseExpireAt = profile.expireAt instanceof Date ? profile.expireAt : new Date(Date.now() + 300000);
const safeExpireAt = new Date(baseExpireAt.getTime() + (service.allowedClockDrift || 0));
if (!profile.assertionId || !profile.issuer) {
SAMLUtils.error({ msg: 'Invalid SAML response: missing Assertion ID or Issuer', profile });
throw new Error('Invalid SAML response: missing Assertion ID or Issuer.');
}
if (!(await SamlUsedAssertions.markUsed(profile.assertionId, profile.issuer, safeExpireAt))) {
SAMLUtils.warn({ msg: 'SAML assertion replay detected', issuer: profile.issuer, assertionId: profile.assertionId });
throw new Error('An assertion with the same ID cannot be used more than once.');
}
// create a random token to store the login result
// to test an IdP initiated login on localhost, use the following URL (assuming SimpleSAMLPHP on localhost:8080):
// http://localhost:8080/simplesaml/saml2/idp/SSOService.php?spentityid=http://localhost:3000/_saml/metadata/test-sp
const credentialToken = Random.id();
const loginResult = {
profile,
};
await this.storeCredential(credentialToken, loginResult);
let redirectPath = SAMLUtils.getValidationActionRedirectPath(credentialToken);
if (loginClient) {
redirectPath += `&loginClient=${loginClient}`;
}
View on GitHub (pinned to 2a7de45707)
Solutions
- Start a fresh login by redirecting to the SAML login endpoint (/saml/login/<provider>) instead of replaying the callback POST
- Fix clock drift: verify NTP on the Rocket.Chat server and the IdP so drift stays below SAML_Allowed_Clock_Drift
- If the IdP itself reuses assertion IDs, correct it on the IdP side — IDs must be unique per issuer
- In automated tests, request a new assertion per attempt rather than replaying a recorded one
Defensive patterns
Strategy: try-catch
Try / catch
try {
const result = await validateSamlLogin(samlResponse);
// proceed with credentialToken
} catch (error) {
if (error instanceof Error && error.message.includes('assertion with the same ID')) {
// replayed response: do NOT retry with the same payload — restart the SAML flow
res.redirect(302, `/saml/login/${provider}`);
return;
}
throw error;
} Prevention
- Never refresh or bookmark the SAML ACS/callback URL — always restart login from /saml/login/<provider>
- Keep server and IdP clocks NTP-synced so drift stays below the allowed clock drift setting
- In tests, fetch a fresh assertion for every attempt; never replay a recorded one
- If an IdP recycles assertion IDs, fix it there — uniqueness is the IdP's contract
When it happens
Trigger: The same SAMLResponse POST is submitted twice — refresh of the callback URL, duplicate form submit, back-button replay; an IdP or intermediary re-issuing an assertion with the same ID; clock drift large enough that a re-check lands inside the NotOnOrAfter + allowedClockDrift window.
Common situations: Users bookmarking or refreshing the ACS endpoint; browser autofill/double-click on the IdP login form; aggressive proxies or browsers re-POSTing; test harnesses reusing a captured assertion; misconfigured IdPs recycling assertion IDs.
Related errors
- registration-disabled-authentication-services
- SAML Provider not loaded due to invalid configuration
- SLO redirect not configured
- Unauthorized redirect origin
- Unauthorized redirect path
AI-assisted analysis of RocketChat/Rocket.Chat@2a7de45707 (2026-08-18).
Data as JSON: /api/errors/3fc14c8f5eb19fc3.
Report an issue: GitHub.