howdy/auth/connections

SSO connection management. Every operation here is privileged: authorize the caller first, as with howdy/auth/groups. None is exposed over HTTP. Enable connections with auth.with_sso.

Disabling or deleting a connection stops new sign-ins through it. Sessions it already issued live on until they expire; end them with auth.revoke_sessions when a customer is offboarded.

Values

pub fn create(
  identity: auth.Auth,
  group group_id: String,
  name name: String,
  protocol protocol: connection.Protocol,
  domains domains: List(String),
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)

Create a connection with a generated id. Its users sign in to group; under Single that is group.default_id.

pub fn create_with_id(
  identity: auth.Auth,
  id id: String,
  group group_id: String,
  name name: String,
  protocol protocol: connection.Protocol,
  domains domains: List(String),
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)

Create a connection under an id of your choosing, such as the customer’s slug: 1 to 64 of letters, digits, hyphen and underscore. The id is part of the URLs the customer configures at their provider, so it cannot change.

pub fn delete(
  identity: auth.Auth,
  id: String,
  by actor: user.Actor,
) -> Result(Nil, service.Error)

Delete the connection and the identities signed in through it. The user accounts remain, with whatever other ways to sign in they have.

pub fn disable(
  identity: auth.Auth,
  id: String,
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)

Refuse new sign-ins without forgetting the configuration or its users.

pub fn enable(
  identity: auth.Auth,
  id: String,
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)
pub fn enforce(
  identity: auth.Auth,
  id: String,
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)

Require the members the connection covers to sign in through it: those of its group whose address is in one of its domains. Every other way in is refused for them (email tokens, passwords, passkeys, built-in providers), and they are signed out now, so that the next sign-in is the provider’s. Members outside the domains, such as guests, are unaffected.

A covered member with an existing account is taken up by their first SSO sign-in; nobody needs to link beforehand. Enforcement lapses while the connection is disabled. If the customer’s provider breaks, stop_enforcing is the way back in: keep an operator account outside the domains.

pub fn get(
  identity: auth.Auth,
  id: String,
) -> Result(connection.Connection, service.Error)
pub fn in_group(
  identity: auth.Auth,
  group_id: String,
) -> Result(List(connection.Connection), service.Error)

One group’s connections, by name.

pub fn list(
  identity: auth.Auth,
) -> Result(List(connection.Connection), service.Error)

Every connection, by name.

pub fn rename(
  identity: auth.Auth,
  id: String,
  to name: String,
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)
pub fn require_local_mfa(
  identity: auth.Auth,
  id: String,
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)

Ask for Howdy’s second factor after this connection again.

pub fn reseal(identity: auth.Auth) -> Result(Int, service.Error)

Re-encrypt every connection with the key given to connection.config: the last step of a rotation (see connection.with_decryption_keys). Returns how many needed it; once every node seals with the new key, a rerun returns 0 and the old key can be dropped. Safe to run while serving. A connection no configured key opens stops it with an error naming the connection.

pub fn set_domains(
  identity: auth.Auth,
  id: String,
  to domains: List(String),
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)

Replace the connection’s email domains. Conflict if another connection holds one: confirm the customer controls a domain before adding it. On an enforced connection the members now covered are signed out, as by enforce.

pub fn set_protocol(
  identity: auth.Auth,
  id: String,
  to protocol: connection.Protocol,
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)

Replace what the customer’s provider handed over. Rotating a client secret or a signing certificate leaves users as they were. Pointing the connection at a different provider (another OIDC issuer or SAML entity ID) forgets the identities signed in through it, because the new provider’s subjects name different people: accounts remain, and each user links again.

pub fn stop_enforcing(
  identity: auth.Auth,
  id: String,
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)
pub fn trust_provider_mfa(
  identity: auth.Auth,
  id: String,
  by actor: user.Actor,
) -> Result(connection.Connection, service.Error)

Let a sign-in through this connection stand without Howdy’s own second factor, for a customer whose provider already demands one. Off by default.

This is trust, not verification: providers report how a user authenticated too inconsistently to check, so Howdy takes the customer’s word that their policy requires MFA. It also means the provider’s administrator can reach an account in the connection’s domains and group that has a Howdy factor, without that factor. Each such sign-in is audited as mfa.provider_trusted.

Such a session has not proven the Howdy factor, so it cannot disable it or regenerate recovery codes. Other ways to sign in still ask for it.

✨ Search Document