Skip to content

Single sign-on

Documentation 8 min read Source on codefloe

On this page

Catodon can let people sign in through an OpenID Connect provider, such as Keycloak, Authentik, Entra ID or any other provider that publishes a discovery document. This page is for the administrator who sets it up. Nothing changes for anyone until a provider is listed in the configuration file.

Setting up a provider

Register Catodon at the provider as a confidential client (one with a client secret), using the authorization code flow. The redirect URI to register is

https://<your server>/sso/oidc/<id>/callback

where <your server> is the host of url in your configuration and <id> is the identifier you give the provider below, for example https://example.tld/sso/oidc/keycloak/callback. Catodon always uses PKCE, a state and a nonce, and checks the issuer, the signature, the audience and the expiry of every ID token.

Then add the provider to the sso section of .config/default.yml and restart the server. The commented block at the end of .config/example.yml lists every setting:

sso:
  ssoOnly: false
  oidc:
    - id: keycloak
      name: Keycloak
      issuer: https://idp.example.tld/realms/main
      clientId: catodon
      clientSecret: change-me
      #scope: openid profile email
      #linkByEmail: false
      #roleClaim: groups
      #roleMapping:
      #  catodon-moderators: ['9xyz0abc12']
  • id: lowercase letters, digits and dashes, 32 at most. It appears in the URLs and is stored with every account linked through the provider, so never change it once people have signed in.
  • name: the label of the sign-in button, as in "Sign in with Keycloak".
  • issuer: the issuer URL of the provider, exactly as its discovery document gives it. It must be HTTPS in production. A linked account is recognised by this issuer and its subject at the provider, so changing the issuer, even only in its spelling, unlinks every account linked through it.
  • clientId and clientSecret: the client you registered. The secret never leaves the server. There is no clientSecretFile key in the file; to read the secret from a file instead, see the environment variables below.
  • scope: optional, openid profile email by default. It must include openid; the e-mail address is read from the email scope.
  • linkByEmail: optional, off by default. See Linking by e-mail address.
  • roleClaim and roleMapping: optional. See Roles from the provider.

Several providers can be listed. A mistake in this section stops the server at startup with a message naming the setting, rather than failing in front of the people trying to sign in.

Every value but id and roleMapping can be overridden from the environment once the provider is listed in the file: MK_CONFIG_SSO_OIDC_0_CLIENTSECRET sets the first provider's secret, and MK_CONFIG_SSO_OIDC_0_CLIENTSECRET_FILE, set to the path of a file, reads it from that file instead (the index is the provider's position in the oidc list, from 0), and MK_CONFIG_SSO_SSOONLY sets the switch below. A provider cannot be declared from the environment alone, and roleMapping is read from the file alone: its keys are values of the provider's claim, which the environment variables have no way to name.

What people see

The sign-in and sign-up dialogs offer one button per provider. A first sign-in through a provider opens the sign-up form, with a username suggested from the provider, which the person confirms or changes. The server's sign-up rules still apply:

  • An account still waits for approval when the server requires approval.
  • When registration is closed, an invitation code is still needed, except in SSO-only mode.
  • When the server requires an e-mail address, the provider must give one it has verified. The address is stored as verified. Outside that setting, a verified address is stored the same way and an unverified one is ignored.
  • The server must be set up first. The first account, the administrator, is created on the welcome page with a password, never through a provider.

People who already have an account link a provider from Settings > Security, after giving their password (and their second factor when they have one). They can unlink it there too, unless it is their last way in. Signing in through a provider skips Catodon's second factor: the provider is the one that authenticates. For the same reason, an account with no password is not asked to set up a second factor by a role that requires one.

SSO-only mode

With ssoOnly: true, the sign-in dialog shows only the providers, and local sign-up is closed, including the Mastodon API's. Sign-up through a provider no longer needs an invitation when registration is closed, since the provider decides who belongs.

Administrators keep their way in: a discreet "Sign in with a password (administrators)" link under the provider buttons opens the password form, and the server accepts a password from administrators alone. This keeps the server reachable when the provider is down or misconfigured. Everyone else is refused before their password is even checked.

Linking by e-mail address

With linkByEmail: true on a provider, a first sign-in through it is attached to the local account that holds the same e-mail address, when both the provider and Catodon have verified that address. This saves people with an existing account a trip to the settings.

The risk is that anyone who controls an address at the provider then controls the matching account on Catodon. Turn it on only for a provider whose addresses you trust, typically your organization's own directory. An account with a second factor or a moderator role is never attached this way: it links the provider from Settings > Security instead, with its password.

Roles from the provider

By default a provider only says who someone is: roles are given in the Control Panel as they always have been, by hand or through conditional roles, and an account created through a provider starts with the same roles as any new account.

A provider can also manage roles. roleMapping maps the values of one claim to Catodon roles, and roleClaim names that claim, groups by default:

      roleClaim: groups
      roleMapping:
        catodon-moderators: ['9xyz0abc12']
        catodon-staff: ['9xyz0abc12', '9xyz0def34']

Each key is a value the provider may send in the claim, and each value lists role ids. A role's id is the end of its address in the Control Panel; quote it, or YAML may read it as a number, which the server refuses at startup. The claim is read from the ID token and from userinfo, as a string or a list of strings. A claim in any other shape, such as an object, numbers, or a distributed claim, which Entra ID sends in place of the list for someone in too many groups, changes no role at that sign-in and leaves a warning in the server log. It must be a top-level claim: in Keycloak, give the group or role mapper a token claim name without a dot, since a dot nests the claim.

At every sign-in through the provider, and when an account is created through it, each role named anywhere in roleMapping is assigned when a value of the claim grants it and unassigned when none does. So a role named in the mapping belongs to the provider: a moderator who assigns or removes it by hand sees the next sign-in undo it. Roles the mapping does not name are never touched. That includes a role taken out of roleMapping, or every role when the whole mapping is removed: everyone who holds it keeps it, so unassign it in the Control Panel. A claim the provider leaves out grants nothing, like an empty one, so make sure the scope that carries it is requested (add it to scope if the provider asks for one), or every mapped role is taken back at the next sign-in.

Only manual roles can be mapped. A conditional role, or an id that no role has, is skipped with a warning in the server log and the sign-in goes on. A role belongs to one provider at most: the server refuses to start when two providers map the same role, which each would otherwise grant and take back in turn for someone who signs in through both. An account waiting for approval gets its roles at its first sign-in once approved, and linking a provider from Settings > Security changes no role until the first sign-in through it.

Whoever controls the claim at the provider gets every role the mapping names, administrator and moderator included. Map a powerful role only from a group that only trusted people can join, and keep in mind that an account whose provider group grants the administrator role keeps it in Catodon until their next sign-in through the provider once it no longer grants it, which may never come: a session already open lasts, and signing in with a password or a passkey changes no role. The safeguard on linking by e-mail address still holds: it looks at the roles an account has before the provider speaks, so an address at the provider is still not enough to take over an existing moderator.

Known limits

  • An account created through a provider has no password, so it cannot yet do what asks for one: delete the account, change its e-mail address or set a password. A later change could re-authenticate through the provider instead.
  • Leaving the organization does not close the Catodon account: suspend or delete it separately. Roles the provider manages are taken back only at the next sign-in through it.
  • Signing out of Catodon does not sign out of the provider.
  • Only OpenID Connect is supported. Linked identities live in their own table, so SAML or CAS could be added on the same model.