Radiator Server Documentation — v10.34.2

oidc blocks enable an experimental feature. Its behavior and configuration may change in a later release. Configuration checks report it once for all providers, and the server logs an Experimental feature enabled message. About experimental features.

Add oidc blocks inside the http block to let users sign in to Radiator UI with an OpenID Connect identity provider, such as Microsoft Entra ID, Keycloak, or Okta. The login page shows a "Sign in with ..." button for each provider. Password sign-in stays available, for example for break-glass accounts.

Configure each provider with an oidc block inside the management http block. Single sign-on requires a management policy that uses the block form of http_management_authentication.

Identity Provider TLS

Radiator connects to the identity provider for discovery, signing keys, the token exchange, and userinfo. Without a tls block, Radiator verifies the provider certificate with the native trust store of the operating system.

Add a tls block when the provider certificate is issued by a private CA, for example a self-hosted Keycloak or Pocket ID. server_ca_certificate names a CA certificate from the certificates section:

certificates {
    x509 "HOME_CA" {
        filename "/etc/radiator/certs/home-ca.pem";
    }
}

management {
    http {
        # ... listen, clients, and TLS settings ...
        policy "MANAGEMENT";

        oidc "pocket-id" {
            title "Pocket ID";
            issuer "https://id.home.example";
            client_id "APPLICATION_CLIENT_ID";
            client_secret env.POCKET_ID_CLIENT_SECRET;
            redirect_url "https://radiator.home.example:8443/api/v1/auth/oidc/pocket-id/callback";
            identity_claim "sub";

            tls {
                server_ca_certificate "HOME_CA";
            }
        }
    }
}

The tls block uses the same settings as other outgoing TLS connections, such as HTTP backends and cluster members:

  • server_ca_certificate selects the CA certificates that verify the provider. Repeat it to trust several CAs. The configured CAs replace the native trust store for this provider, so a provider with a public CA certificate is no longer trusted unless that CA is also configured.
  • certificate and certificate_key select a client certificate and key for mutual TLS with the provider.
  • min_protocol_version, max_protocol_version, crl, and negotiation work as for other TLS connections. See TLS configuration.

A tls block without server_ca_certificate verifies the provider with the native trust store. The @verification pipeline is not supported for identity provider connections. A missing certificate name fails the configuration load. When the named certificates change, Radiator uses the new certificates for the next request to the provider.

Choose the Identity Claim

identity_claim has no default. Choose a claim that the identity provider never changes for one user and never reuses for another, because it becomes the session username and the aaa.identity that the management policy authorizes:

  • sub works with every provider. The issuer never reassigns it to another user.
  • For Microsoft Entra ID, use oid, the object ID of the user. Microsoft documents preferred_username as changeable and not suitable for authorization.
  • Use preferred_username or email only when the provider prevents users from changing them.

Sign-In Flow

  1. The user selects the provider on the login page. Radiator sends the browser to the provider's authorization endpoint with the authorization code flow, PKCE (S256), a state, and a nonce.
  2. The provider returns the browser to redirect_url. Radiator exchanges the code at the token endpoint and validates the ID token: signature with the provider's published keys, iss, aud, exp, iat, nbf, nonce, and azp. Radiator allows 60 seconds of clock difference. aud must name only client_id, and tokens whose header has crit or cty, or a typ other than JWT, are rejected.
  3. Radiator runs the management policy with aaa.identity set from identity_claim and the claims available as oidc.claims. http_management_authentication runs @resolve but not @verify, because the provider verified the credentials.
  4. When @resolve accepts the user and sets user.username, Radiator creates the normal session and opens the page the user started from.

Radiator keeps the sign-in secrets in a signed radiator_oidc_login cookie with SameSite=Lax. The sign-in must finish within 10 minutes, and each login state completes at most one sign-in. Radiator signs the cookie with a random key of the running process, so the callback must reach the node that started the sign-in, and a restart cancels sign-ins in progress. Management sessions are also local to one node, so use sticky sessions when several Radiator nodes serve Radiator UI behind a load balancer.

The radiator_oidc_login cookie uses the Secure attribute like the session cookie, and is then named __Host-radiator_oidc_login, so that other hosts in the same domain cannot set it. Behind a reverse proxy that terminates TLS, the Radiator listener is plain HTTP, so the cookie is not marked Secure unless secure_session_cookie true; is set.

Radiator fetches the provider metadata when the configuration loads and logs a warning if it fails. Password sign-in is not affected. Radiator fetches the signing keys on first use, again after one hour, and again when a token names an unknown key, at most once every 30 seconds.

Map Claims to Privileges

Set user.username and map the claims in @resolve. Radiator stores the claims in the session, so a refresh after resolve_cache_timeout maps them again:

http_management_authentication {
    @resolve {
        map oidc.provider {
            "entra" => {
                user.username = aaa.identity;
                user.group += oidc.claims.groups;

                # The first matching value decides, so list the strongest first.
                map oidc.claims.roles {
                    "Radiator.Admin" => {
                        user.privilege = "all";
                        user.radiatordb_role = "admin";
                    }
                    "Radiator.Operator" => {
                        user.privilege = "write";
                    }
                    "Radiator.Monitor" => {
                        user.privilege = "monitor";
                    }
                    any => {
                        reject "No Radiator UI role";
                    }
                }
            }
            none => {
                # Password users, such as a local break-glass account.
                backend {
                    name "MGMT_USERS";
                    query "FIND_USER";
                }
            }
            any => {
                reject "Unknown single sign-on provider";
            }
        }
    }

    @verify {
        http_basic_auth;
    }
}

When @resolve rejects the user, the login page reports that the account is not allowed to use Radiator UI. @resolve can also call backends with the identity or the claims, for example to read group memberships from Microsoft Graph when the ID token has too many groups to include them.

Look up local users only when oidc.provider is none, as in the example. @verify does not run for single sign-on, so a policy that looks up an identity provider user in a local user backend would give that user the privileges of a local user with the same name.

When Radiator preserves management sessions across an in-process restart, it ends single sign-on sessions, because their claims are not preserved. Their users sign in again.

Sign-Out

Sign-out always ends the Radiator session. With logout_redirect true;, Radiator UI then opens the provider's end_session_endpoint with client_id and id_token_hint, which ends the provider session too. The browser stays on the provider's page. Radiator keeps the ID token in memory only for this purpose.

Set logout_redirect to a URL to return the browser to it after the provider ends its session. Radiator sends the URL as post_logout_redirect_uri. Most providers accept only registered URLs, so register it at the identity provider too.

logout_redirect "https://radiator.example.com:8443/ui/login";