Skip to main content

saml-auth

The saml-auth plugin enables APISIX and API7 Gateway to act as a SAML 2.0 service provider (SP). When a user requests a protected route, the gateway redirects the browser to an identity provider (IdP). The gateway validates the signed SAML response and creates an authenticated session before proxying the request upstream.

The plugin supports the HTTP-Redirect binding by default, the HTTP-POST binding, service provider-initiated single logout, and session-secret rotation. Authenticated user data is available in ctx.external_user for other plugins on the same request.

Response Validation​

Starting in API7 Enterprise 3.9.21 and APISIX 3.19.0, the plugin checks the following on every login response without additional configuration:

  • An assertion that carries an AudienceRestriction names sp_issuer or one of the values in sp_audiences.
  • The assertion is used within its NotBefore and NotOnOrAfter window, with a tolerance of clock_skew seconds.
  • The Recipient of the assertion, and the Destination of the response if present, match the assertion consumer service URL.
  • If the assertion names the authentication request it answers (InResponseTo), that request is the one started by the same browser session.

Unless sp_acs_url is set, the assertion consumer service URL is built from the scheme and host of the callback request. If the gateway runs behind a proxy that terminates TLS without setting X-Forwarded-Proto, or that rewrites the host, set sp_acs_url to the public callback URL. Otherwise, the URL built by the gateway differs from the one the IdP writes into Recipient, and logins are refused.

These response-validation controls and replay-tracking options are not available in API7 Enterprise 3.10.7.

Replay tracking is optional in the releases that support it. Set replay_dict to plugin-saml-auth-replay to record accepted assertions on each gateway instance. Records are not shared across instances, and a full dictionary can allow a login without recording it. See the configuration reference for retention and capacity considerations.

Example​

The following example configures SAML single sign-on and single logout with Keycloak. It uses HTTP for local testing and the default HTTP-Redirect binding. Use HTTPS for the gateway and Keycloak in production.

Before proceeding:

Start Keycloak​

Start Keycloak in the environment that matches the gateway deployment.

Start Keycloak in development mode:

docker run -d --name apisix-saml-keycloak \
-e 'KC_BOOTSTRAP_ADMIN_USERNAME=quickstart-admin' \
-e 'KC_BOOTSTRAP_ADMIN_PASSWORD=quickstart-admin-pass' \
-p 127.0.0.1:8080:8080 \
quay.io/keycloak/keycloak:26.7.3 start-dev

The development server and example administrator credentials are intended only for local testing. For a production deployment, use Keycloak production mode with HTTPS, a production database, and appropriately managed administrator credentials.

Create the Service Provider Certificate​

Generate the certificate and private key that the gateway will use to sign SAML requests:

openssl req -x509 -newkey rsa:2048 -nodes \
-keyout sp-private-key.pem \
-out sp-cert.pem \
-days 365 \
-subj "/CN=APISIX SAML"

Store the private key securely. Replace the self-signed certificate with a certificate that follows the organization's certificate policy for production use.

Configure Keycloak​

Open http://localhost:8080/admin/ and sign in with username quickstart-admin and password quickstart-admin-pass.

Create a realm for the example:

  1. Select Manage realms → Create realm.
  2. Enter apisix-saml as the realm name and select Create.

Create a user who can authenticate through the IdP:

  1. Select Users → Add user.
  2. Enter alice as the username, complete the required profile fields, and select Create.
  3. Open the Credentials tab and select Set password.
  4. Enter alice-pass, turn off Temporary, and save the password.

Register the gateway as a SAML client:

  1. Select Clients → Create client.
  2. Select SAML as the client type, enter apisix-saml as the client ID, and select Save. The client ID must match the sp_issuer value configured on the plugin.
  3. In Access settings, add these valid redirect URIs:
    • http://127.0.0.1:9080/anything/login_callback
    • http://127.0.0.1:9080/anything/logout_callback
  4. In SAML capabilities, keep Force POST binding off.
  5. In Signature and encryption, turn on Sign documents and Sign assertions, and select RSA_SHA256 as the signature algorithm.
  6. In Advanced → Fine Grain SAML Endpoint Configuration, set Logout Service Redirect Binding URL to http://127.0.0.1:9080/anything/logout_callback.
  7. Save the client.
  8. Open the Keys tab, turn on Client signature required, and import sp-cert.pem as a certificate in PEM format.

The plugin signs authentication and logout requests with sp-private-key.pem. Keycloak verifies those signatures with the imported service provider certificate. The plugin also rejects unsigned Keycloak responses, so Keycloak must sign the SAML documents returned to the gateway.

Save the Keycloak Certificate​

Open http://localhost:8080/realms/apisix-saml/protocol/saml/descriptor. Copy the signing certificate from the X509Certificate element and save it as idp-cert.pem with the PEM header and footer:

idp-cert.pem
-----BEGIN CERTIFICATE-----
replace-with-the-keycloak-signing-certificate
-----END CERTIFICATE-----

The metadata also lists the SAML single sign-on endpoint. This example uses http://localhost:8080/realms/apisix-saml/protocol/saml, which the user's browser can reach.

Generate a session secret and load the certificates into environment variables for the gateway configuration:

export SAML_SESSION_SECRET="$(openssl rand -hex 16)"
export IDP_CERT="$(cat idp-cert.pem)"
export SP_CERT="$(cat sp-cert.pem)"
export SP_PRIVATE_KEY="$(cat sp-private-key.pem)"

Use the same session secret on every gateway node. During secret rotation, move the previous value to secret_fallbacks so existing sessions remain readable.

Configure the Gateway​

The configuration protects /anything/* and leaves /anything/logout-complete public so that users have a landing page after single logout.

Choose the API used to configure the routes.

Create the public logout destination:

curl "http://127.0.0.1:9180/apisix/admin/routes/saml-logout-complete" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"uri": "/anything/logout-complete",
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

Create the protected route. jq reads the certificate and private-key files without changing their PEM formatting:

jq -n \
--arg secret "$SAML_SESSION_SECRET" \
--rawfile idp_cert idp-cert.pem \
--rawfile sp_cert sp-cert.pem \
--rawfile sp_private_key sp-private-key.pem \
'{
"uri": "/anything/*",
"plugins": {
"saml-auth": {
"secret": $secret,
"sp_issuer": "apisix-saml",
"idp_uri": "http://localhost:8080/realms/apisix-saml/protocol/saml",
"login_callback_uri": "/anything/login_callback",
"logout_uri": "/anything/logout",
"logout_callback_uri": "/anything/logout_callback",
"logout_redirect_uri": "/anything/logout-complete",
"idp_cert": $idp_cert,
"sp_cert": $sp_cert,
"sp_private_key": $sp_private_key
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}' | \
curl "http://127.0.0.1:9180/apisix/admin/routes/saml-auth" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @-

Verify Single Sign-On and Logout​

Open http://127.0.0.1:9080/anything/saml-test in a browser. Keycloak redirects the browser to its login page. Sign in with username alice and password alice-pass.

After authentication, the browser returns to the protected route. The upstream response contains the request details and a saml_session cookie similar to the following:

{
"headers": {
"Cookie": "saml_session=...",
"Host": "127.0.0.1",
"X-Forwarded-Host": "127.0.0.1:9080"
},
"method": "GET",
"url": "http://127.0.0.1/anything/saml-test"
}

Open http://127.0.0.1:9080/anything/logout in the same browser. The gateway sends a signed logout request to Keycloak, processes the signed logout response at /anything/logout_callback, clears the SAML session, and redirects the browser to /anything/logout-complete.

Return to http://127.0.0.1:9080/anything/saml-test. Keycloak should prompt for credentials again, confirming that the gateway and Keycloak sessions were terminated.