Legacy SAML
Learn how SAML facilitates secure information exchange between authentication servers and client applications.
Overview
The following guide shows you how to configure SAML using the legacy setup with SuperTokens.
Since version 11.3 of the core service you can use SuperTokens as a SAML client. We recommend that you use the latest version with the simplified setup.
BoxyHQ
BoxyHQ is a commercial open-source company that has a product called "SAML Jackson" which helps integrate SAML providers into your application.
SAML Jackson is a perfect fit with SuperTokens because:
- It's an OAuth provider and a SAML client. This fits with the architecture.
- For self hosted, it supports PostgreSQL and MySQL amongst other databases. Self hosted SuperTokens only supports PostgreSQL as a data source.
SAML Jackson provides an HTTP service that you can host yourself or let BoxyHQ manage. The HTTP service uses NodeJS, and is embeddable within your NodeJS backend. However, because SuperTokens supports multiple backends, the focus is on deploying SAML Jackson as a microservice.
- The user clicks on the login button and redirects to SAML Jackson's microservice at http://localhost:5225/api/oauth/authorize
- SAML Jackson redirects the user to the SAML provider's login page where the user needs to enter their credentials
- After successfully authenticating the user, the SAML provider redirects the user to SAML Jackson. Step (2) and (3) follow the SAML protocol.
- SAML Jackson redirects the user to the frontend app on the configured callback URL. The callback URL contains the one-time use auth code.
- SuperTokens' frontend SDK passes the one-time use auth code to your app's backend.
- SuperTokens' backend SDK verifies the auth code by querying SAML Jackson. On success, SAML Jackson returns the end user's information and access token.
- SuperTokens' backend SDK creates a new user in the core associated with the end user's email. New session tokens are also created
- A SuperTokens' session establishes between your app's backend and frontend - logging in the user.
An example app on GitHub with SuperTokens + SAML Jackson, for React and NodeJS express apps, is available. This uses mocksaml.com as a SAML provider
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
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.
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)
4. Configure the SAML provider for the 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).
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. The "Common UX flows" section documents two common UX flows.
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" to ensure 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.
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).
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. The "Common UX flows" section documents two common UX flows.
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" to ensure that the button on the pre-built UI shows the right name.