Single sign-on
SQL Server Health Monitor supports OpenID Connect SSO alongside local username/password auth. The two are not mutually exclusive — local accounts keep working as a break-glass path if the IdP is down.
Works with any standards-compliant OIDC provider: Microsoft Entra ID (formerly Azure AD), Okta, Google Workspace, Keycloak, Auth0, …
Setup overview
- Register the app at your IdP.
- Copy the IdP-issued Client ID + Client Secret into
appsettings.jsonor user secrets. - Restart the app.
- The login page shows a "Sign in with SSO" button.
Optional:
- Auto-provision unknown SSO users (default: on, role = Viewer).
- Map an IdP group claim to the Admin role.
Step-by-step: Microsoft Entra ID
- Azure portal → Microsoft Entra ID → App registrations → New registration.
- Name:
SQL Server Health Monitor. Supported account types: typically "Accounts in this organisational directory only". - Redirect URI: Web →
https://your-monitor-host/signin-oidc - After creation:
- Note the Application (client) ID and the Directory (tenant) ID.
- Under Certificates & secrets → New client secret → copy the Value (only shown once).
- Under API permissions: the default
User.Readis enough; the app only needsopenid profile email.
- (Optional, for Admin group mapping) Under Token configuration → Add
groups claim → pick "Security groups" → save. This makes Entra ID include
a
groupsclaim with object IDs of the user's groups in the access token. - Edit
appsettings.json:
{
"OidcSettings": {
"Enabled": true,
"DisplayName": "Sign in with Entra ID",
"Authority": "https://login.microsoftonline.com/<tenant-id>/v2.0",
"ClientId": "<application-id>",
"ClientSecret": "<client-secret-value>",
"Scopes": [ "openid", "profile", "email" ],
"AutoCreateUsers": true,
"AdminGroupClaim": "groups",
"AdminGroupValue": "<DBA-group-object-id>"
}
}
- Restart. The login page now shows "Sign in with Entra ID".
Step-by-step: Okta
- Okta admin → Applications → Create App Integration → OIDC — OpenID Connect → Web Application.
- Sign-in redirect URI:
https://your-monitor-host/signin-oidc - Sign-out redirect URI:
https://your-monitor-host/signout-callback-oidc - Note the Client ID + Client Secret + your Okta domain.
- Edit
appsettings.json:
{
"OidcSettings": {
"Enabled": true,
"DisplayName": "Sign in with Okta",
"Authority": "https://<your-okta-domain>/oauth2/default",
"ClientId": "...",
"ClientSecret": "...",
"Scopes": [ "openid", "profile", "email" ],
"AutoCreateUsers": true,
"AdminGroupClaim": "groups",
"AdminGroupValue": "SqlMonitorAdmins"
}
}
For the groups claim, configure an "Authorization Server" custom claim of name
groups and include only the groups you care about (otherwise the token gets
huge).
Auto-provisioning rules
When a user signs in via SSO for the first time:
- The app looks for an existing local user whose
EmailorUserNameequals the IdP-provided email claim. If found, the IdP identity is linked to that local user — they can sign in either way from now on. - If not found AND
AutoCreateUsersistrue: a new local user is created withUserName = Email,EmailConfirmed = true, role =Viewer. An admin can promote later via/Settings/Users. - If not found AND
AutoCreateUsersisfalse: sign-in is rejected with an error message instructing the user to ask an admin to create the account first.
Admin role mapping
The AdminGroupClaim / AdminGroupValue pair is optional but powerful:
- On every SSO sign-in, the app checks whether the user's IdP claims include
one of type
AdminGroupClaimwith valueAdminGroupValue. - If yes and the user isn't already in the local Admin role → promoted to Admin.
- If no and the user is in the local Admin role → revoked from Admin.
This means removing someone from the IdP DBA group automatically revokes their Admin role here on next sign-in. No manual cleanup required.
To disable the mapping entirely, leave both fields empty.
Sign-out behaviour
Default: clicking "Sign out" clears the local cookie only. The user can then hit the SSO button again and immediately re-authenticate (the IdP session is still live).
If you want RP-initiated logout (which closes the IdP session too, requiring
re-entering the IdP password on next sign-in), set
OidcSettings:SignOutFromIdP to true. Not the default because most B2B ops
prefer the local-only behaviour.
Troubleshooting
"SSO provider did not return an email claim"
The user authenticated successfully but the IdP didn't include an email
claim in the token. Check that:
- The
emailscope is requested (default isopenid profile email). - The user has an email address set in the IdP.
- For Entra ID: the user is a member, not a guest — guest accounts sometimes need explicit consent grants.
Login spinner just sits there
The IdP redirect-back to /signin-oidc is failing. Check:
- The redirect URI registered at the IdP matches the app's URL exactly (trailing slash, scheme, port).
- HTTPS is enforced — most IdPs reject HTTP redirect URIs.
- Time skew between the app host and the IdP. OIDC tokens have short validity windows; > 5min skew breaks signature validation.
"Failed to provision local account: …"
Identity refused the user creation. Most common cause: the email is invalid
or fails the configured User validators. Tighten or relax
IdentityOptions.User in Program.cs.
"Failed to link SSO identity: A user with this login already exists"
The IdP-issued subject was already linked to a different local account at some
point. Either delete the orphaned link from AspNetUserLogins directly, or
delete the duplicate local account.
Disabling SSO
Set OidcSettings:Enabled to false and restart. Users that signed in via SSO
keep their local accounts and can still log in with username/password (after
an admin sets a password on the account via /Settings/Users → "Reset password",
since auto-provisioned accounts have no local password by default).