title | subtitle | slug |
---|---|---|
Self-Hosting SSOReady |
How to manage SAML and SCIM entirely in your cloud or your customer's cloud |
/self-hosting-ssoready |
SSOReady is a free, MIT-licensed way to add SAML and SCIM support to your app. A free hosted version of SSOReady is available online at app.ssoready.com. This article explains how to run SSOReady on your own machines, instead of using the public, free version of SSOReady at app.ssoready.com.
This article focuses on running SSOReady in your own cloud. You can also run SSOReady in your customer's cloud, so that SSOReady becomes a component of your larger on-premises offering to your customers; the only difference is that you'll need to follow these instructions for each customer.SSOReady requires four essential components:
ssoready-app
is the webapp for SSOReady. In the free hosted version, this lives at https://app.ssoready.com.ssoready-api
is whatssoready-app
and your code (either directly, or using the SSOReady SDKs) talks to. In the free hosted version, this lives at https://api.ssoready.com.ssoready-auth
is proxy for SAML and SCIM interactions. In the free hosted version, this lives at https://auth.ssoready.com.ssoready-db
is a PostgreSQL database thatssoready-api
andssoready-auth
talk to. This is where SSOReady keeps state.
An additional fifth component is optional:
ssoready-admin
is the webapp that powers the self-serve setup UI. In the free hosted version, this lives at https://admin.ssoready.com.
To that end, SSOReady maintains four Docker containers you can use to self-host:
ssoready/ssoready-app
runs the webappssoready/ssoready-api
runs the SSOReady APIssoready/ssoready-auth
runs the public-facing SSOReady SAML and SCIM proxyssoready/ssoready-migrate
runs SSOReady's database migrations against a Postgres databasessoready/ssoready-admin
runs the public-facing self-serve setup UI
Here's an example of how you can self-host SSOReady using docker compose
. You can copy-paste this into
docker-compose.yml
and run docker compose up
to test it yourself right now.
version: "2.4"
services:
# this is an example postgres-in-docker setup, but any postgres (even outside of docker) will work
postgres:
image: postgres:15.3
environment:
POSTGRES_PASSWORD: "password"
ports:
- "5432:5432"
ssoready-auth:
image: ssoready/ssoready-auth:sha-18090f8
environment:
AUTH_SERVE_ADDR: ":80"
# Have this point at ssoready-db. In this example, that's the `postgres` docker-compose service above.
AUTH_DB: "postgres://postgres:password@postgres/postgres"
# Have this point at where this service will be accessible from the public internet.
AUTH_BASE_URL: "http://localhost:8080"
# Have this point at where the ssoready-admin service will be accessible from the public internet, followed by "/test-mode".
AUTH_DEFAULT_ADMIN_TEST_MODE_URL: "http://localhost:8083/test-mode"
# See "SAML State Signing Key" below. For initial testing, this default value is fine.
AUTH_SAML_STATE_SIGNING_KEY: "0000000000000000000000000000000000000000000000000000000000000000"
ports:
- "8080:80"
ssoready-api:
image: ssoready/ssoready-api:sha-18090f8
environment:
API_SERVE_ADDR: ":80"
# Have this point at ssoready-db. In this example, that's the `postgres` docker-compose service above.
API_DB: "postgres://postgres:password@postgres/postgres"
# Have this point at where the ssoready-auth service will be accessible from the public internet.
API_DEFAULT_AUTH_URL: "http://localhost:8080"
# (Optional -- skip if you're not using ssoready-admin)
#
# Have this point at where the ssoready-admin service will be accessible from the public internet, followed by "/setup".
API_DEFAULT_ADMIN_SETUP_URL: "http://localhost:8083/setup"
# See "Supporting Login Methods" below.
API_GOOGLE_OAUTH_CLIENT_ID: "171906208332-m8dg2p6av2f0aa7lliaj6oo0grct57p1.apps.googleusercontent.com"
# See "SAML State Signing Key" below. For initial testing, this default value is fine.
API_SAML_STATE_SIGNING_KEY: "0000000000000000000000000000000000000000000000000000000000000000"
ports:
- "8081:80"
ssoready-app:
image: ssoready/ssoready-app:sha-18090f8
environment:
APP_SERVE_PORT: "80"
# Have this point at where the ssoready-app service will be accessible from the public internet.
APP_APP_URL: "http://localhost:8082"
# Have this point at where the ssoready-api service will be accessible from the public internet,
APP_PUBLIC_API_URL: "http://localhost:8081"
# Have this point at APP_PUBLIC_API_URL, followed by "/internal/connect".
APP_API_URL: "http://localhost:8081/internal/connect"
# See "Supporting Login Methods" below.
APP_GOOGLE_OAUTH_CLIENT_ID: "171906208332-m8dg2p6av2f0aa7lliaj6oo0grct57p1.apps.googleusercontent.com"
ports:
- "8082:80"
# Optional. If you don't need the self-serve setup UI for configuring SAML and
# SCIM, you can skip ssoready-admin.
ssoready-admin:
image: ssoready/ssoready-admin:sha-18090f8
environment:
ADMIN_SERVE_PORT: "80"
# Have this point at APP_PUBLIC_API_URL, followed by "/internal/connect".
ADMIN_API_URL: "http://localhost:8081/internal/connect"
ports:
- "8083:80"
(The example above requires localhost ports 5432, 8080, 8081, 8082, and 8083.)
Now, you'll need to set up the database tables that SSOReady requires. Do so by running:
docker run --network=host ssoready/ssoready-migrate:sha-18090f8 -d 'postgres://postgres:password@localhost/postgres?sslmode=disable' up
From here, you can visit localhost:8082
, log in via Google, and then start using SSOReady as usual. To use the
SSOReady SDKs with your locally-running instance of SSOReady, see "Configuring SDKs to talk to self-hosted SSOReady
instances" below.
The example setup above is meant for use in localhost
. There are a few things you need to do to make it work in
production:
- Configure a "SAML State Signing Key"
- (Only required if you use SAML-over-OAuth) Configure an
id_token
signing key - Configure ways to log into
ssoready-app
The sections below detail how you'll do this.
In the example configuration above, ssoready-auth
and ssoready-api
both have a "SAML State Signing Key" set to all
zeroes. That's a value that's adequate for development, but in production you must use a random value. Generate this
random value by running:
openssl rand -hex 32
ssoready-auth
and ssoready-api
must use the same value for this secret. It is safe to rotate this value at any time
by using a new random, 64-digit hex number.
SSOReady's SAML-over-OAuth integration has ssoready-auth
act as an OIDC-compliant server. Such servers need to issue
id_token
values, which are RSA-signed JSON Web Tokens. ssoready-auth
by default generates a new, random RSA keypair
to sign these id_token
values. This default doesn't work well if you're running multiple ssoready-auth
instances
behind a load balancer, or if ssoready-auth
instances are frequently being recreated.
To configure an id_token
signing key, generate a new base64-encoded RSA private key like so:
openssl genrsa 2048 | base64
And then update ssoready-auth
's AUTH_OAUTH_ID_TOKEN_PRIVATE_KEY
to be that value.
To log into ssoready-app
, you need to configure at least one of the following login methods:
- Log in with Google
- Email (aka "magic links")
- Log in with Microsoft
If you don't configure any of these, then there's no way to log into SSOReady. The simplest login method to add is Google, followed by email, followed by Microsoft.
To configure Google-based social logins into ssoready-app
, you need to populate the following env vars:
APP_GOOGLE_OAUTH_CLIENT_ID
inssoready-app
API_GOOGLE_OAUTH_CLIENT_ID
inssoready-api
To do so, you need to create a Google OAuth client. Follow Google's docs for creating an OAuth
client. You'll want to configure the
same "Authorized Javascript origin" as the value you chose for APP_APP_URL
. From there, the client ID (ending in
apps.googleusercontent.com
) is the value you'll want to put in the two env vars.
To configure email-based logins into ssoready-app
, you need to populate the following env vars:
API_RESEND_API_KEY
inssoready-api
API_EMAIL_CHALLENGE_FROM
inssoready-api
API_EMAIL_VERIFICATION_ENDPOINT
inssoready-api
SSOReady uses Resend to send transactional emails. Resend's free tier is more than enough here; you will not need to pay for Resend. To get an API key, you will:
- Sign up for Resend for free
- Configure a domain to send emails from
- Create an API key. Make sure the API key has sending privileges for your domain.
From there, configure:
API_RESEND_API_KEY
to be your Resend API key. It starts withre_...
.API_EMAIL_CHALLENGE_FROM
to benoreply@DOMAIN
, whereDOMAIN
is the domain you configured in step (2) above.API_EMAIL_VERIFICATION_ENDPOINT
to be the same value you chose forAPP_APP_URL
inssoready-app
, but followed by/verify-email
.
To configure Microsoft-based logins into ssoready-app
, you need to populate the following env vars:
API_MICROSOFT_OAUTH_CLIENT_ID
inssoready-api
API_MICROSOFT_OAUTH_CLIENT_SECRET
inssoready-api
API_MICROSOFT_OAUTH_REDIRECT_URI
inssoready-api
APP_MICROSOFT_OAUTH_CLIENT_ID
inssoready-app
APP_MICROSOFT_OAUTH_REDIRECT_URI
inssoready-app
Set API_MICROSOFT_OAUTH_REDIRECT_URI
and APP_MICROSOFT_OAUTH_REDIRECT_URI
to be the same value as APP_APP_URL
in
ssoready-app
, but followed by /internal/microsoft-callback
.
To get the CLIENT_ID
and CLIENT_SECRET
vars, you need to create a Microsoft OAuth client. Follow Microsoft's docs
for creating an OAuth app.
- Choose "Accounts in any organizational directory and personal Microsoft accounts" for supported account types.
- Choose the "Web" platform type, and the redirect URL will be the same as the
REDIRECT_URI
env vars above. - The app's "Application (Client) ID", a UUID, is what you use for
API_MICROSOFT_OAUTH_CLIENT_ID
andAPP_MICROSOFT_OAUTH_CLIENT_ID
. - Create a "client secret" credential (Microsoft documents this here under the "Add a client secret" tab). The secret's value is what you put in
API_MICROSOFT_OAUTH_CLIENT_SECRET
.
The SSOReady Management API lets you programmatically automate everything that would otherwise require a human to click around in the SSOReady web application. If you don't need the Management API, skip this section.
To enable the Management API in a self-hosted instance of SSOReady, you must
first log into the SSOReady web application a first time. That will create a
app_organizations
row in the SSOReady database, upon which you can enable the
Management API.
Once you have logged into SSOReady, you can next connect to your your SSOReady
database (using psql
or any other tool you're comfortable with), and run:
update app_organizations set entitled_management_api = true where id = '...';
To determine the appropriate id
to run in the SQL command above, you may run:
select * from app_organizations;
In the common case where nobody else has logged into the SSOReady web
application, there will only be one app_organizations
row to choose from.
By default, the SSOReady SDKs expect to talk to https://api.ssoready.com
. When you're running SSOReady self-hosted,
you need to point it to your own instance of ssoready-api
. Here's how you do that, supposing your ssoready-api
lives
at localhost:8081
like in the example above:
const ssoready = new SSOReadyClient({
// add this new `environment` parameter
environment: "http://localhost:8081",
apiKey: "ssoready_sk_...",
}); ```
```python title="Python"
from ssoready.client import SSOReady
client = SSOReady(
# add this new `base_url` parameter
base_url="http://localhost:8081",
api_key="ssoready_sk_..."
)
```
SSOReady requires ssoready-db
to be a Postgres database. The ssoready/ssoready-migrate
docker image can take any
Postgres database, and set it up to have the database tables SSOReady requires. ssoready-migrate
is idempotent; it is
safe to run it multiple times or at any time.
docker run --network=host ssoready/ssoready-migrate:main -d 'postgres://...' up
The -d
(--database-url
) parameter requires a postgres://
URI, of the form:
postgres://username:password@host:port/db_name