Skip to content

Concepts

SAML encrypted assertions

Signing proves an assertion came from the identity provider and was not altered. Encryption additionally hides its contents from everything the assertion passes through — which, since it travels through the user's browser, is a longer list than it first appears. Some customers require it, and their security review will ask.

Signing and encryption solve different problems

Every SAML assertion is signed. The signature is computed by the identity provider with its private key and verified by the service provider against a certificate exchanged during setup. That gives integrity and authenticity: the document is genuine and unmodified. It gives no confidentiality at all — a signed assertion is plain XML, and anything that handles it can read every attribute inside.

Assertion encryption closes that gap. The identity provider encrypts the assertion with the service provider's public key before signing the envelope around it, so only the holder of the matching private key can read the contents. In the middle sits the user's browser, which POSTs the assertion from one host to the other, along with whatever browser extensions, proxies and corporate inspection appliances are present on the way.

Whether that matters depends on what the assertion carries. An email address and a display name are usually not sensitive. An employee number, a cost centre, a job grade or a list of internal group names sometimes are, and a customer whose directory attributes describe their org chart will treat the assertion as confidential even when you would not.

What each mechanism gives you

PropertySigning onlySigning plus encryption
Assertion is genuineYesYes
Assertion is unmodifiedYesYes
Attributes hidden from the browserNoYes
Attributes hidden from a TLS-terminating proxyNoYes
Keys to manageThe provider's signing certificateThat, plus your own encryption key pair
Failure mode when a key expiresAll sign-ins refusedAll sign-ins refused, with a less obvious error

The last row is the honest cost. Encryption doubles the number of certificates in play, and the failure it produces is harder to read: a decryption failure looks like a malformed assertion rather than a rejected signature, and the log line that explains it lives on your side rather than the customer's.

Which keys are involved

There are two independent key pairs, moving in opposite directions, and confusing them is the usual reason a first attempt fails.

  • The identity provider's signing key pair. The provider holds the private half and signs assertions with it; you hold the public certificate and verify against it. This exists whether or not encryption is enabled.
  • The service provider's encryption key pair. You hold the private half and decrypt with it; the provider holds your public certificate and encrypts to it. This exists only when encryption is enabled.
  • Both certificates expire, usually on different schedules, and neither provider warns the other. A calendar entry is not sophisticated but it is what actually prevents the outage.

With Paycux the encryption key pair is generated and held on our side, and the public certificate is published in the connection metadata the customer imports. You never handle a private key, and rotation happens without a coordinated deploy.

Turning it on for a connection

Encryption is configured per connection, because it is the customer's requirement rather than yours. Enabling it globally would break every existing connection whose provider has not been told to encrypt.

  1. 1Open the connection in the dashboard and enable assertion encryption. The connection metadata now includes an encryption certificate alongside the existing values.
  2. 2Send the customer the updated metadata URL, or the certificate itself if their provider does not consume metadata.
  3. 3Ask them to enable assertion encryption on their side and select that certificate. In most providers this is a single checkbox plus a certificate picker.
  4. 4Test with a real sign-in before telling them it is done. A provider that has been told to encrypt but has not picked up the certificate sends an assertion nothing can read.
  5. 5Confirm the profile that arrives still contains the attributes you expect. Some providers quietly drop optional attributes when re-saving the application configuration.

When it fails

A connection that worked yesterday and fails today, immediately after encryption was enabled, is almost always one of three things: the provider is encrypting to an old certificate, the provider is not encrypting at all while your side requires it, or the provider is encrypting the whole response rather than the assertion inside it. The error surfaces on the connection's recent sign-ins in the dashboard, which is the first place to look rather than the last.

The snippet below is what a failed decryption looks like in a callback. Handle it as a connection-level failure rather than a user-level one — the person signing in cannot fix it, and asking them to try again wastes everybody's afternoon.

app/callback/sso/route.ts
1const params = new URL(request.url).searchParams;
2const error = params.get("error");
3
4if (error === "assertion_decryption_failed") {
5 // Kullanicinin yapabilecegi bir sey yok — baglanti sahibine haber ver.
6 await alerts.connectionBroken(params.get("connection_id"), error);
7 return Response.redirect("/sign-in?error=sso_unavailable");
8}

If a customer asks whether you support encrypted assertions during a security review, the answer is yes and it is configured per connection. If they ask which cipher suites are used, point them at the connection metadata, which states them explicitly rather than requiring anyone to take a written claim on trust.