Skip to main content

Integration guide

Integrate SAML authentication with SuperTokens using SAML Jackson.

Before you start

These instructions assume that you already are familiar with SuperTokens and you have configured a demo application. If you have skipped those steps page please go through the main quickstart guide.

Using the SuperTokens dashboard

caution

This is only available with Node and Python SDKs.

1. Generate the XML metadata file from your SAML provider

Your SAML provider allows you to download a .xml file that you can upload to SAML Jackson. During this process, you need to provide it:

  • the SSO URL and;
  • the Entity ID.

You can learn more about these in the SAML Jackson docs.

In the example app, mocksaml.com serves as a free SAML provider for testing. When you navigate to the site, you see a "Download metadata" button which you should click on to get the .xml file.

2. Start the SAML Jackson service

You can run SAML Jackson with or without Docker.

docker run \
-p 5225:5225 \
-e JACKSON_API_KEYS="secret" \
-e DB_ENGINE="sql" \
-e DB_TYPE="postgres" \
-e DB_URL="postgres://postgres:postgres@postgres:5432/postgres" \
-d boxyhq/jackson

This starts the SAML Jackson server on http://localhost:5225.

important

If you are using the SuperTokens managed service, Boxy HQ hosts the server for you (contact support to activate your instance).

3. Create a new tenant in SuperTokens (if not done already)

Create Tenant

4. Configure the SAML provider for the tenant

Create Tenant

To configure SAML login with SuperTokens, ensure that you use the correct provider name in the third-party configuration. Make sure to specify provider name with one of the following:

  • Microsoft Entra ID
  • Microsoft AD FS
  • Okta
  • Auth0
  • Google
  • OneLogin
  • PingOne
  • JumpCloud
  • Rippling
  • SAML

Make sure to replace http://localhost:5225 with the correct value for where you have hosted the BoxyHQ server. If you are using the SuperTokens managed service, Boxy HQ hosts the server for you (contact support to activate your instance).

success

You have successfully configured a new tenant in SuperTokens. The next step is to wire up the frontend SDK to show the right login UI for this tenant. The specifics of this step depend on the UX that you want to provide to your users. Two common UX flows are documented in the "Common UX flows" section.

5. Adding multiple SAML connections to a single tenant

If you have one SAML connection for a tenant, then the Third Party Id for that connection can be boxy-saml. This displays a single "SAML Login" button on the pre-built UI.

If you want to add a second SAML connection for the same tenant, follow the same steps as above, but also use the Add Suffix option for the Third Party Id.

For example, if a tenant has Active Directory and Okta login (both with SAML), you can create the Active Directory provider using "boxy-saml" as the Third Party Id. For Okta, you could use okta as a suffix to make the Third Party Id equal to "boxy-saml-okta". You can also give them different names - instead of "SAML Login" (that's shown above), you can use "Active Directory" and "Okta" so that the button on the pre-built UI shows the right name.

Using the BoxyHQ API

1. Generate the XML metadata file from your SAML provider

Your SAML provider allows you to download a .xml file that you can upload to SAML Jackson. During this process, you need to provide it:

  • the SSO URL and;
  • the Entity ID.

You can learn more about these in the SAML Jackson docs.

In the example app, mocksaml.com serves as a free SAML provider for testing. When you navigate to the site, you see a "Download metadata" button which you should click on to get the .xml file.

2. Convert the .xml file to base64

You can use an online base64 encoder to do this. First copy the contents of the .xml file, and then put it in the encoder tool. The output string is the base64 version of the .xml file.

For example, with an input .xml file (obtained from mocksaml.com):

<?xml version="1.0" encoding="UTF-8" standalone="no"?><EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://saml.example.com/entityid" validUntil="2026-06-22T18:39:53.000Z"><IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><KeyDescriptor use="signing"><KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><X509Data><X509Certificate>MIIC4jCCAcoCCQC33wnybT5QZDANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJV
SzEPMA0GA1UECgwGQm94eUhRMRIwEAYDVQQDDAlNb2NrIFNBTUwwIBcNMjIwMjI4
MjE0NjM4WhgPMzAyMTA3MDEyMTQ2MzhaMDIxCzAJBgNVBAYTAlVLMQ8wDQYDVQQK
DAZCb3h5SFExEjAQBgNVBAMMCU1vY2sgU0FNTDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALGfYettMsct1T6tVUwTudNJH5Pnb9GGnkXi9Zw/e6x45DD0
RuRONbFlJ2T4RjAE/uG+AjXxXQ8o2SZfb9+GgmCHuTJFNgHoZ1nFVXCmb/Hg8Hpd
4vOAGXndixaReOiq3EH5XvpMjMkJ3+8+9VYMzMZOjkgQtAqO36eAFFfNKX7dTj3V
pwLkvz6/KFCq8OAwY+AUi4eZm5J57D31GzjHwfjH9WTeX0MyndmnNB1qV75qQR3b
2/W5sGHRv+9AarggJkF+ptUkXoLtVA51wcfYm6hILptpde5FQC8RWY1YrswBWAEZ
NfyrR4JeSweElNHg4NVOs4TwGjOPwWGqzTfgTlECAwEAATANBgkqhkiG9w0BAQsF
AAOCAQEAAYRlYflSXAWoZpFfwNiCQVE5d9zZ0DPzNdWhAybXcTyMf0z5mDf6FWBW
5Gyoi9u3EMEDnzLcJNkwJAAc39Apa4I2/tml+Jy29dk8bTyX6m93ngmCgdLh5Za4
khuU3AM3L63g7VexCuO7kwkjh/+LqdcIXsVGO6XDfu2QOs1Xpe9zIzLpwm/RNYeX
UjbSj5ce/jekpAw7qyVVL4xOyh8AtUW1ek3wIw1MJvEgEPt0d16oshWJpoS1OT8L
r/22SvYEo3EmSGdTVGgk3x3s+A0qWAqTcyjr7Q4s/GKYRFfomGwz0TZ4Iw1ZN99M
m0eo2USlSRTVl7QHRTuiuSThHpLKQQ==
</X509Certificate></X509Data></KeyInfo></KeyDescriptor><NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat><SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://mocksaml.com/api/saml/sso"/><SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://mocksaml.com/api/saml/sso"/></IDPSSODescriptor></EntityDescriptor>

The base64 output is:

PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PEVudGl0eURlc2NyaXB0b3IgeG1sbnM6bWQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDptZXRhZGF0YSIgZW50aXR5SUQ9Imh0dHBzOi8vc2FtbC5leGFtcGxlLmNvbS9lbnRpdHlpZCIgdmFsaWRVbnRpbD0iMjAyNi0wNi0yMlQxODozOTo1My4wMDBaIj48SURQU1NPRGVzY3JpcHRvciBXYW50QXV0aG5SZXF1ZXN0c1NpZ25lZD0iZmFsc2UiIHByb3RvY29sU3VwcG9ydEVudW1lcmF0aW9uPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxLZXlEZXNjcmlwdG9yIHVzZT0ic2lnbmluZyI+PEtleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxYNTA5RGF0YT48WDUwOUNlcnRpZmljYXRlPk1JSUM0akNDQWNvQ0NRQzMzd255YlQ1UVpEQU5CZ2txaGtpRzl3MEJBUXNGQURBeU1Rc3dDUVlEVlFRR0V3SlYKU3pFUE1BMEdBMVVFQ2d3R1FtOTRlVWhSTVJJd0VBWURWUVFEREFsTmIyTnJJRk5CVFV3d0lCY05Nakl3TWpJNApNakUwTmpNNFdoZ1BNekF5TVRBM01ERXlNVFEyTXpoYU1ESXhDekFKQmdOVkJBWVRBbFZMTVE4d0RRWURWUVFLCkRBWkNiM2g1U0ZFeEVqQVFCZ05WQkFNTUNVMXZZMnNnVTBGTlREQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQUQKZ2dFUEFEQ0NBUW9DZ2dFQkFMR2ZZZXR0TXNjdDFUNnRWVXdUdWROSkg1UG5iOUdHbmtYaTlady9lNng0NUREMApSdVJPTmJGbEoyVDRSakFFL3VHK0FqWHhYUThvMlNaZmI5K0dnbUNIdVRKRk5nSG9aMW5GVlhDbWIvSGc4SHBkCjR2T0FHWG5kaXhhUmVPaXEzRUg1WHZwTWpNa0ozKzgrOVZZTXpNWk9qa2dRdEFxTzM2ZUFGRmZOS1g3ZFRqM1YKcHdMa3Z6Ni9LRkNxOE9Bd1krQVVpNGVabTVKNTdEMzFHempId2ZqSDlXVGVYME15bmRtbk5CMXFWNzVxUVIzYgoyL1c1c0dIUnYrOUFhcmdnSmtGK3B0VWtYb0x0VkE1MXdjZlltNmhJTHB0cGRlNUZRQzhSV1kxWXJzd0JXQUVaCk5meXJSNEplU3dlRWxOSGc0TlZPczRUd0dqT1B3V0dxelRmZ1RsRUNBd0VBQVRBTkJna3Foa2lHOXcwQkFRc0YKQUFPQ0FRRUFBWVJsWWZsU1hBV29acEZmd05pQ1FWRTVkOXpaMERQek5kV2hBeWJYY1R5TWYwejVtRGY2RldCVwo1R3lvaTl1M0VNRURuekxjSk5rd0pBQWMzOUFwYTRJMi90bWwrSnkyOWRrOGJUeVg2bTkzbmdtQ2dkTGg1WmE0CmtodVUzQU0zTDYzZzdWZXhDdU83a3dramgvK0xxZGNJWHNWR082WERmdTJRT3MxWHBlOXpJekxwd20vUk5ZZVgKVWpiU2o1Y2UvamVrcEF3N3F5VlZMNHhPeWg4QXRVVzFlazN3SXcxTUp2RWdFUHQwZDE2b3NoV0pwb1MxT1Q4TApyLzIyU3ZZRW8zRW1TR2RUVkdnazN4M3MrQTBxV0FxVGN5anI3UTRzL0dLWVJGZm9tR3d6MFRaNEl3MVpOOTlNCm0wZW8yVVNsU1JUVmw3UUhSVHVpdVNUaEhwTEtRUT09CjwvWDUwOUNlcnRpZmljYXRlPjwvWDUwOURhdGE+PC9LZXlJbmZvPjwvS2V5RGVzY3JpcHRvcj48TmFtZUlERm9ybWF0PnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzczwvTmFtZUlERm9ybWF0PjxTaW5nbGVTaWduT25TZXJ2aWNlIEJpbmRpbmc9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpiaW5kaW5nczpIVFRQLVJlZGlyZWN0IiBMb2NhdGlvbj0iaHR0cHM6Ly9tb2Nrc2FtbC5jb20vYXBpL3NhbWwvc3NvIi8+PFNpbmdsZVNpZ25PblNlcnZpY2UgQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCIgTG9jYXRpb249Imh0dHBzOi8vbW9ja3NhbWwuY29tL2FwaS9zYW1sL3NzbyIvPjwvSURQU1NPRGVzY3JpcHRvcj48L0VudGl0eURlc2NyaXB0b3I+

3. Start the SAML Jackson service

You can run SAML Jackson with or without Docker.

docker run \
-p 5225:5225 \
-e JACKSON_API_KEYS="secret" \
-e DB_ENGINE="sql" \
-e DB_TYPE="postgres" \
-e DB_URL="postgres://postgres:postgres@postgres:5432/postgres" \
-d boxyhq/jackson

This starts the SAML Jackson server on http://localhost:5225.

important

If you are using the SuperTokens managed service, Boxy HQ hosts the server for you (contact support to activate your instance).

4. Upload the base64 XML string to SAML Jackson

Take the string generated in step (2) and run:

curl --location --request POST 'http://localhost:5225/api/v1/saml/config' \
--header 'Authorization: Api-Key secret' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'encodedRawMetadata=<STRING_FROM_STEP_2>' \
--data-urlencode 'defaultRedirectUrl=<REDIRECT_URI>' \
--data-urlencode 'redirectUrl=["<REDIRECT_URI>"]' \
--data-urlencode 'tenant=<TENANT_ID>' \
--data-urlencode 'product=<PRODUCT_NAME>' \
--data-urlencode 'name=demo-config' \
--data-urlencode 'description=Demo SAML config'

You can learn more about the configuration values in the SAML Jackson docs.

For the example app, you can see this command here. This helper script, addTenant.sh, provides the command. You can run it like:

./addTenant.sh <TENANT_ID>

# example
./addTenant.sh customer1
./addTenant.sh customer2

The output of this command provides you the client_id and client_secret for this tenant. You need to provide these values to SuperTokens for this tenant when configuring this tenant's providers (see below).

5. Create a new tenant in SuperTokens (if not done already)

import Multiteancy from "supertokens-node/recipe/multitenancy";

async function createTenant() {
let resp = await Multiteancy.createOrUpdateTenant("customer1", {
firstFactors: ["thirdparty"]
});

if (resp.createdNew) {
// new tenant was created
} else {
// existing tenant's config was modified.
}
}

6. Configure the SAML provider for the tenant

import Multitenancy from "supertokens-node/recipe/multitenancy"

async function addThirdPartyConfigToTenant() {
let resp = await Multitenancy.createOrUpdateThirdPartyConfig("customer1", {
thirdPartyId: "boxy-saml",
name: "<provider-name>",
clients: [{
clientId: "<TODO: GENERATED FROM PREVIOUS STEP>",
clientSecret: "<TODO: GENERATED FROM PREVIOUS STEP>",
additionalConfig: {
"boxyURL": "http://localhost:5225",
}
}]
});

if (resp.createdNew) {
// SAML Login added to customer1
} else {
// Existing SAML Login config overwritten for customer1
}
}

To configure SAML login with SuperTokens, ensure that you use the correct provider name in the third-party configuration. Make sure to replace <provider-name> in the code snippet above with one of the following

  • Microsoft Entra ID
  • Microsoft AD FS
  • Okta
  • Auth0
  • Google
  • OneLogin
  • PingOne
  • JumpCloud
  • Rippling
  • SAML

Make sure to replace http://localhost:5225 with the correct value for where you have hosted the BoxyHQ server. If you are using the SuperTokens managed service, Boxy HQ hosts the server for you (contact support to activate your instance).

success

You have successfully configured a new tenant in SuperTokens. The next step is to wire up the frontend SDK to show the right login UI for this tenant. The specifics of this step depend on the UX that you want to provide to your users. Two common UX flows are documented in the "Common UX flows" section.

7. Adding multiple SAML connections to a single tenant

If you have one SAML connection for a tenant, then the thirdPartyId for that connection can be boxy-saml. This displays a single "SAML Login" button on the pre-built UI.

If you want to add a second SAML connection for the same tenant, follow the same steps as above. Instead of using "boxy-saml" as the thirdPartyId, set it to another value that starts with "boxy-saml" (for step 6).

For example, if a tenant has Active Directory and Okta login (both with SAML), you can create the Active Directory provider using "boxy-saml" as the thirdPartyId. For Okta, you could use "boxy-saml-okta" (it's important that the string starts with "boxy-saml"). You can also give them different names - instead of "SAML Login" (that's shown above), you can use "Active Directory" and "Okta" so that the button on the pre-built UI shows the right name.

See also