From 839a0f9c5dbda7b02acb5b07b15d548f7769c129 Mon Sep 17 00:00:00 2001 From: aconite33 Date: Tue, 8 Sep 2026 14:08:54 -0600 Subject: [PATCH 1/3] SAML: configurable disableRequestedAuthnContext and serialized callback errors Pass cfg.disableRequestedAuthnContext through to node-saml so identity providers that enforce multi-factor authentication or passwordless sign-in (for example, Entra ID) can opt out of the default requested authentication context. The value defaults to node-saml's existing behavior, so current deployments are unaffected unless they set it. Log Util.serializeError(err) in ERROR_SAML_CALLBACK instead of the raw error object. The raw object serialized to an empty object and hid the failure reason. --- protocols/saml.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/protocols/saml.js b/protocols/saml.js index 02603e1..43c4799 100644 --- a/protocols/saml.js +++ b/protocols/saml.js @@ -12,7 +12,8 @@ module.exports = (SSOUtils) => { issuer: cfg.issuer, idpCert: cfg.cert, privateKey: cfg.privateKey, - publicCert: cfg.signingCert + publicCert: cfg.signingCert, + disableRequestedAuthnContext: cfg.disableRequestedAuthnContext }); cb(void 0, saml); }; @@ -61,7 +62,7 @@ module.exports = (SSOUtils) => { idpData: {} }); }).catch(err => { - Env.Log.error('ERROR_SAML_CALLBACK', err); + Env.Log.error('ERROR_SAML_CALLBACK', Util.serializeError(err)); return void cb('EINVAL'); }); }); From f1ea24e8c38caffccc908cf83eb5413fe5d296eb Mon Sep 17 00:00:00 2001 From: aconite33 Date: Tue, 8 Sep 2026 14:23:30 -0600 Subject: [PATCH 2/3] SAML: allow opting out of the required response signature Pass cfg.wantAuthnResponseSigned through to node-saml. In node-saml v5, this option defaults to true, which rejects the assertion with an "Invalid document signature" error when the response wrapper is unsigned. Identity providers such as Entra ID sign only the assertion by default, so this option allows an operator to accept assertion-only signing. The assertion signature stays required, because wantAssertionsSigned still defaults to true, so this change never accepts an unsigned assertion. The value defaults to node-saml's behavior, so current deployments are unaffected unless they set it. --- protocols/saml.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/saml.js b/protocols/saml.js index 43c4799..28677a7 100644 --- a/protocols/saml.js +++ b/protocols/saml.js @@ -13,7 +13,8 @@ module.exports = (SSOUtils) => { idpCert: cfg.cert, privateKey: cfg.privateKey, publicCert: cfg.signingCert, - disableRequestedAuthnContext: cfg.disableRequestedAuthnContext + disableRequestedAuthnContext: cfg.disableRequestedAuthnContext, + wantAuthnResponseSigned: cfg.wantAuthnResponseSigned }); cb(void 0, saml); }; From 502aec3f97475e1cdf4197143ff6c5c3627817ad Mon Sep 17 00:00:00 2001 From: aconite33 Date: Tue, 8 Sep 2026 14:38:59 -0600 Subject: [PATCH 3/3] docs: document SAML options and Microsoft Entra ID setup Describe the SAML provider fields, including the new disableRequestedAuthnContext and wantAuthnResponseSigned options, and add a Microsoft Entra ID section. The section covers the Entity ID and Reply URL to register, the signing certificate format, and the two Entra defaults that need attention. --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/README.md b/README.md index 73d9463..d1d2a87 100644 --- a/README.md +++ b/README.md @@ -121,8 +121,49 @@ module.exports = { cert: String or fs.readFileSync("./your/cert/location", "utf-8"), privateKey: fs.readFileSync("./your/private/key/location", "utf-8"), signingCert: fs.readFileSync("./your/signing/cert/location", "utf-8"), + disableRequestedAuthnContext: true, (optional, see SAML configuration below) + wantAuthnResponseSigned: false, (optional, see SAML configuration below) } */ ] }; ``` + +## SAML configuration + +A SAML provider entry accepts the following fields: + +- `name`: A short identifier for the provider. +- `type`: Set to `saml`. +- `url`: The identity provider single sign-on endpoint that CryptPad redirects users to. +- `issuer`: The service provider entity identifier for this CryptPad instance. +- `cert`: The identity provider signing certificate, as a PEM string or an array of PEM strings. CryptPad uses it to verify the signature on the SAML response. +- `privateKey` and `signingCert`: An optional service provider key pair, used only when the identity provider requires CryptPad to sign its authentication requests. Most deployments omit both. +- `disableRequestedAuthnContext`: Optional. Set to `true` to stop CryptPad from requesting a specific authentication context. When unset, node-saml requests the `PasswordProtectedTransport` context. +- `wantAuthnResponseSigned`: Optional. Set to `false` to accept a signed assertion inside an unsigned response. When unset, node-saml requires the response itself to be signed. + +The certificate value must be valid PEM or base64. When you paste a certificate inline, keep every line flush against the left margin, because leading whitespace from indenting the block to match the surrounding code prevents the certificate from parsing. + +### Microsoft Entra ID + +In the Entra enterprise application, set the Identifier (Entity ID) to the same value as the `issuer` field, for example `https://your-domain/saml/metadata`, and set the Reply URL (Assertion Consumer Service URL) to `https://your-domain/ssoauth`. Download the Base64 signing certificate from the same application and use it as the `cert` value. + +Two Entra defaults commonly need attention. First, node-saml requests the `PasswordProtectedTransport` authentication context with exact matching. Entra rejects the sign-in with error AADSTS75011 when the user completes multi-factor or passwordless authentication, because the method no longer matches the requested context. Setting `disableRequestedAuthnContext: true` allows Entra to apply its own policy. + +Second, Entra signs the assertion but not the response wrapper by default, while node-saml requires a signed response and otherwise returns the `Invalid document signature` error. Set the Entra Signing Option to "Sign SAML response and assertion" to keep the stronger posture, or set `wantAuthnResponseSigned: false` to accept assertion-only signing. The assertion signature stays required in both cases. + +A working Entra provider entry looks like this: + +``` +{ + name: 'entra', + type: 'saml', + url: 'https://login.microsoftonline.com//saml2', + issuer: 'https://your-domain/saml/metadata', + cert: `-----BEGIN CERTIFICATE----- + +-----END CERTIFICATE-----`, + disableRequestedAuthnContext: true, + wantAuthnResponseSigned: false, +} +```