Single sign-on (OIDC & LDAP)
MIRA can delegate login to an identity provider you already run, so people sign in with their existing company or Google account instead of a MIRA-specific password. Two mechanisms are supported, both off by default:
- OIDC (OpenID Connect) — a “Sign in with …” button for any standard provider: Google, Microsoft Entra ID, Keycloak, Authentik, Okta, and more.
- LDAP / Active Directory — username/password checked against your directory.
The most important guarantee first: local login always works. SSO and LDAP are added alongside local username/password — never instead of it. A bootstrap admin account or a directory outage can never lock everyone out, because a local password login is always available.
Provider changes take effect on a service restart. Plan a brief restart when you add or reconfigure a provider.
For adding the accounts these identities map onto, see Manage users & access.
OIDC (Google, Entra, Keycloak, Okta, …)
Section titled “OIDC (Google, Entra, Keycloak, Okta, …)”OIDC is generic and discovery-driven — one configuration shape covers any compliant provider. You don’t pick a provider type; you point MIRA at the provider’s issuer URL and it discovers the rest.
1. Register MIRA at your provider
Section titled “1. Register MIRA at your provider”In your identity provider, create an OAuth/OIDC application (a confidential client). You’ll get a client id and client secret. Set the redirect URI to:
<your-mira-base-url>/api/auth/oidc/callbackIt must match exactly. MIRA builds this from auth.oidc.public_base_url — the
origin (scheme + host + port) browsers reach MIRA on — so set that to your real
public URL (e.g. https://mira.example.com). If left empty it defaults to
http://127.0.0.1:<server.port>, which is fine for local testing only.
2. Configure the provider in MIRA
Section titled “2. Configure the provider in MIRA”Under auth.oidc (Settings → Server, or mira_config.json):
{ "auth": { "oidc": { "enabled": true, "public_base_url": "https://mira.example.com", "providers": [ { "id": "google", "display_name": "Google", "issuer": "https://accounts.google.com", "client_id": "…apps.googleusercontent.com", "client_secret": "…", "auto_provision": false, "allowed_domains": [] } ] } }}The fields:
enabled— master switch. Even with providers listed, OIDC is inert until this istrue.id— a stable slug used in URLs and identity binding (e.g.google).display_name— the button label (e.g.Google,Company SSO).issuer— the discovery base URL. MIRA fetches<issuer>/.well-known/openid-configurationto resolve the authorization, token, and userinfo endpoints.client_id/client_secret— the credentials from step 1.scopes— requested scopes; defaults toopenid email profile.auto_provision/allowed_domains/default_role— see Linking and auto-provisioning.
After a restart, the web login page automatically shows a “Sign in with …” button for each configured provider. List several providers to show several buttons.
How the login flow works
Section titled “How the login flow works”The flow is server-mediated (Authorization Code + PKCE): MIRA redirects the user to the IdP, then exchanges the code and reads the IdP’s userinfo endpoint server-side. No token ever rides the redirect URL. After the round-trip MIRA issues its own session — the same JWT access token and refresh cookie a password login produces — so SSO users get the same session lifecycle (including Sign out everywhere) as everyone else.
Linking and auto-provisioning
Section titled “Linking and auto-provisioning”MIRA resolves the incoming identity to a MIRA account in this order:
- By
(issuer, subject)— the stable identity MIRA recorded last time this person signed in via this provider. - By email — if no identity match, MIRA links the SSO identity to an existing local account with the same email.
- Auto-provision — if there’s still no match and
auto_provisionis on and the email’s domain is inallowed_domains, MIRA creates a new account on first login, with the role fromdefault_role(userby default).
auto_provision is off by default (link-to-existing-email only). Before
turning it on, set allowed_domains (lowercase, no @) so only your own
domains can self-create accounts — otherwise anyone the IdP authenticates could
provision an account.
LDAP / Active Directory
Section titled “LDAP / Active Directory”LDAP login is tried as a fallback when local password auth fails. This is what keeps local accounts working: MIRA checks the local password first, and only if that fails does it try the directory. A directory outage therefore never blocks the bootstrap admin or any local account.
Configure it under auth.ldap:
{ "auth": { "ldap": { "enabled": true, "url": "ldaps://dc.example.com:636", "starttls": false, "bind_dn": "cn=svc-mira,ou=service,dc=example,dc=com", "bind_password": "…", "user_base_dn": "ou=people,dc=example,dc=com", "user_filter": "(sAMAccountName={username})", "required_group": "", "auto_provision": false } }}The key fields:
enabled— master switch.url— directory URL, e.g.ldap://dc.example.com:389orldaps://dc.example.com:636.starttls— upgrade a plaintextldap://connection to TLS before binding (ignored forldaps://).bind_dn/bind_password— a service account used to search for the user before binding as them. Emptybind_dnmeans anonymous search.user_base_dn— the base DN to search under, e.g.ou=people,dc=example,dc=com.user_filter— the search filter;{username}is substituted (LDAP-escaped). Default(uid={username}); Active Directory usually wants(sAMAccountName={username}).required_group— if set, the user must be a member of this group DN (checked viamemberOf). Empty means no group requirement.attr_email/attr_display_name— attributes to read for email and display name (defaultsmailandcn).auto_provision/allowed_domains/default_role— same as OIDC: create a MIRA account on first successful LDAP login when there’s no existing match. Off by default; scope it withallowed_domains.
MIRA authenticates with search-then-bind: it binds as the service account
(or anonymously), searches user_base_dn with user_filter to find the user’s
DN, then binds as that user with the supplied password. On success the directory
identity is mapped to a MIRA user the same way OIDC does — link by
username/email, or auto-provision.
Troubleshooting
Section titled “Troubleshooting”- No “Sign in with …” button appears. Confirm
auth.oidc.enabledistrue, at least one provider is configured, and you’ve restarted the service. - Redirect URI mismatch at the IdP. The URI registered at the provider must
exactly equal
<public_base_url>/api/auth/oidc/callback. Checkpublic_base_urlmatches your real public origin (scheme, host, and port). - SSO/LDAP user can’t get in but should. With
auto_provisionoff, the person needs a pre-existing MIRA account with a matching email to link to. Either add the account first (see Manage users) or enableauto_provisionwith the rightallowed_domains. - LDAP login fails for everyone. Verify
user_filtermatches your directory (uid=for OpenLDAP,sAMAccountName=for AD) and that thebind_dnservice account can searchuser_base_dn. Local logins still work regardless.
Related
Section titled “Related”- Manage users & access — accounts, invites, roles, and capability RBAC.
- Security & multi-user — where SSO fits in MIRA’s identity and isolation model.