Single Sign-On (SSO)
Modulo supports Single Sign-On (SSO) via OpenID Connect (OIDC) and SAML 2.0. When an employee signs in for the first time through an identity provider (IdP), Modulo creates their account and organisation membership automatically (Just-In-Time, or JIT, provisioning).
SSO requires a Team-tier license (MODULO_LICENSE_KEY). The Community tier does not include SSO. Configuration is admin-only at /settings/sso.
Prerequisites
Two environment variables control where SSO redirects land. Both must be correct before you register redirect URIs at your IdP.
MODULO_PUBLIC_URL
The externally reachable URL of your Modulo instance (for example, https://your-modulo.example.com). Modulo builds the OIDC callback URL from this value:
https://<host>/api/v1/auth/oidc/<provider-slug>/callback
The IdP must accept redirects to this exact URL. If MODULO_PUBLIC_URL is wrong, every login attempt will fail with a redirect error.
MODULO_FRONTEND_URL
Where the browser lands after login (the /auth/callback route). Defaults to MODULO_PUBLIC_URL. You only need to set this if the SPA (single-page application) is served from a different origin than the API. In the standard self-hosted setup (nginx serves the SPA and the API on the same origin), no extra setting is needed.
The provider slug
When you create an SSO provider in the Modulo UI, there is no “provider ID” or “slug” field. The slug is derived automatically from the provider name:
- Lowercase the name
- Replace every run of non-alphanumeric characters with a single hyphen
- Strip leading/trailing hyphens
- Truncate to 58 characters
- If the slug already exists, append
-2,-3, and so on (globally unique across all organisations)
Examples:
| Provider name | Slug |
|---|---|
Google |
google |
Azure AD |
azure-ad |
Okta (Production) |
okta-production |
My Long Provider Name Here 12345 |
my-long-provider-name-here-12345 |
This slug determines the redirect URI you must register at your IdP. The callback URL for a provider named Google is:
https://your-modulo.example.com/api/v1/auth/oidc/google/callback
A mismatch between the slug and the redirect URI registered at the IdP is the most common cause of the redirect_uri_mismatch error.
Google Workspace worked example
This walks through configuring Google as an OIDC provider, step by step.
Google Cloud Console setup
- Go to Google Cloud Console and create a new project, or select an existing one.
- Navigate to APIs & Services > OAuth consent screen.
- For Google Workspace organisations, choose Internal (no verification needed).
- For external users, choose External and add test users until the app is verified.
- On the consent screen, add the scopes:
openid,email,profile. - Navigate to APIs & Services > Credentials.
- Click Create Credentials > OAuth client ID.
- Select Web application as the application type.
- Under Authorized JavaScript origins, add your Modulo URL:
https://your-modulo.example.com. - Under Authorized redirect URIs, add the callback URL:
https://your-modulo.example.com/api/v1/auth/oidc/google/callback. - Click Create. Copy the Client ID and Client secret.
Modulo setup
- Log in to Modulo as an admin and go to
/settings/sso. - Click Add Provider and select OIDC.
- Fill in the fields:
- Name:
Google(this determines the sluggoogle) - Client ID: paste the Client ID from Google
- Client secret: paste the Client secret from Google
- Discovery URL:
https://accounts.google.com/.well-known/openid-configuration - Scopes:
openid email profile
- Name:
- Click Create.
- Click Test on the newly created provider. The test should report the discovered endpoints (authorization endpoint, token endpoint, userinfo endpoint).
- Ensure the provider is enabled (the toggle should be on).
Verify the flow
- Log out of Modulo.
- The login page should now show a Google button.
- Click the button, complete the Google login, and confirm you land back in the Modulo app.
Login experience
When a user visits the login page, Modulo fetches enabled SSO providers via GET /api/v1/auth/sso/providers. The response lists each enabled OIDC provider (by slug) and whether SAML is enabled. Each OIDC provider becomes a button linking to /api/v1/auth/oidc/<slug>/login.
Provisioning and roles
On first login, Modulo creates the user’s account and organisation membership. The organisation role comes from the MODULO_SSO_DEFAULT_ROLE environment variable (default: runner). Valid values are operator and runner.
The default_role and auto_provision fields visible on the provider form in the UI are not applied at sign-in time. They are stored on the provider record but do not change the runtime provisioning behaviour (tracked as FAR-839).
Group to team mappings
You can map IdP groups to Modulo teams. When a user authenticates, their group memberships are checked against the mappings. If a match is found, the user is added to (or their role is updated in) the corresponding Modulo team.
Each mapping has three fields:
| Field | Description |
|---|---|
idp_group |
The group name or claim value from the IdP |
team_id |
The UUID of the Modulo team |
team_role |
The role to assign within the team (default: viewer) |
For this to work, the IdP must emit a groups claim in the OIDC ID token. For SAML providers, the claim can be groups, memberOf, or Group.
Google limitation: Google’s standard OIDC ID token does not include a groups claim. To use group mappings with Google, you need extra claim provisioning at the IdP (for example, through a custom claim in Google Workspace or an intermediate service). Group mapping does not work out of the box with plain Google OIDC.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Redirect lands on the wrong host | MODULO_FRONTEND_URL or MODULO_PUBLIC_URL is incorrect |
Set both variables to the correct externally reachable URL |
redirect_uri_mismatch at the IdP |
The redirect URI registered at the IdP does not match the slug-based callback URL | Check the provider name in Modulo, derive the slug, and update the redirect URI at the IdP to match exactly |
| “Invalid state parameter” error | Expired or blocked cookie, or a stale login tab | Clear cookies, close stale tabs, and try again |
| No SSO button on the login page, or 402 error | SSO feature not enabled (Community tier or missing license) | Set MODULO_LICENSE_KEY to a Team-tier license |
| Connection test fails | Discovery URL unreachable from the Modulo server | Verify network access from the server to the IdP’s discovery endpoint |
| Users are provisioned but not added to teams | Group mappings not configured, or the IdP does not emit a groups claim |
Configure group mappings and verify the IdP sends group information |
Related
- SCIM Provisioning: automated user and group lifecycle management
- Configuration: environment variables including
MODULO_LICENSE_KEY - Secrets: credential encryption and the client secret at rest