OpenTofu and Terraform provider

The keenauth/keenauth provider manages the configuration of one KeenAuth environment as code. It calls the management API, speaks plugin protocol 6 and works with OpenTofu and Terraform. It is not published to a provider registry yet.

Authentication and target

The provider manages the environment that its credential belongs to. To manage several environments, configure one provider alias per environment. It authenticates in exactly one of two ways: with a management API key, or keylessly as a management application with private_key_jwt. Setting both is an error.

Setting Environment variable Description
api_key KEENAUTH_API_KEY Management API key. Prefer the environment variable. The key is never written to state or plan files.
client_id KEENAUTH_CLIENT_ID ID (app_…) of a management application, instead of api_key. Needs private_key and issuer.
private_key KEENAUTH_PRIVATE_KEY PEM PKCS#8 private key whose public key is registered on the application: EC P-256, EC P-384, Ed25519 or RSA of at least 2048 bits. Prefer the environment variable. Never written to state.
private_key_algorithm PS256 (default) or RS256, for an RSA key only.
issuer KEENAUTH_ISSUER The environment's default host, https://<environment>.<region>.keenauth.app. The provider gets its token from <issuer>/oauth2/token.
scopes Permissions to request with client_id. If omitted, the grant's default scopes apply.
region KEENAUTH_REGION eu (default) or us. The API address is https://api.eu.keenauth.com/v1 / https://api.us.keenauth.com/v1.
base_url KEENAUTH_BASE_URL Explicit API address. Only https://<host>.keenauth.com/v1 is accepted, plus loopback addresses for local tests.
terraform {
  required_providers {
    keenauth = { source = "keenauth/keenauth" }
  }
}

provider "keenauth" {
  region = "eu"
}

Keyless authentication with private_key_jwt

  1. Create an application with app_type = "management" and a keenauth_management_client record with the permissions the configuration needs.
  2. Register the public key of a key pair on the application (POST /v1/applications/{application_id}/credentials with type: public_key, the CLI or the dashboard).
  3. Give the provider the application ID, the private key and the issuer:
export KEENAUTH_CLIENT_ID="app_..."
export KEENAUTH_PRIVATE_KEY="$(cat ops-automation.pk8.pem)"
export KEENAUTH_ISSUER="https://acme.eu.keenauth.app"
tofu plan

For each management token the provider signs a client assertion (RFC 7523) that is valid for 60 seconds: iss and sub are the client ID, aud is the issuer, jti is random and kid is the RFC 7638 thumbprint of the key. It exchanges the assertion with the client_credentials grant for a 10-minute token and gets a new one a minute before the old one expires. The token can never have more permissions than the application's keenauth_management_client record. Endpoints that accept only an API key, such as management clients, need a provider alias configured with api_key.

Resources

Name Kind What it manages
keenauth_application resource An application (OAuth 2.0 / OpenID Connect client).
keenauth_connection resource A login connection: database, passwordless, passkey, social or enterprise.
keenauth_connection_oidc_config resource The relying party configuration of an enterprise OIDC connection, with a write-only client secret (OpenTofu or Terraform 1.11+). Import with the connection ID. Destroy only removes it from state.
keenauth_environment_settings resource Environment settings: languages, sessions, sign-up, notifications, recovery, MFA, first factors. One per environment.
keenauth_attack_protection resource Attack protection: brute force, IP throttling, credential stuffing, bots, breached passwords, sign-up, SMS fraud, risk policy. One per environment.
keenauth_branding resource The environment theme of the branding draft. One per environment.
keenauth_custom_domain resource A custom login domain.
keenauth_api_key resource A management API key: import only; destroy revokes it.
keenauth_organization resource A B2B organization: name, slug, external ID, metadata, brand colour and membership limit. Destroy deletes it immediately; status actions (disable, enable, cancel deletion) stay in the CLI and dashboard.
keenauth_organization_role resource An organization-specific role and its permission set (Business plan). Import with <org_id>/<key>.
keenauth_permission resource A permission (OAuth scope) of an API with its consent screen texts. Import with <api_id>/<value>.
keenauth_role resource An environment role and its complete permission set. User and membership assignments are not resources.
keenauth_organization_domain resource A domain of an organization for domain capture and SSO routing. Verification stays an action.
keenauth_session_policy resource An organization's session lifetimes; stricter than the environment only (Enterprise plan).
keenauth_mfa_policy resource An organization's MFA rules; stricter than the environment only.
keenauth_email_template resource The published content of one email template (family and locale); Startup plan. Import with <family>/<locale>. Destroy only removes it from state.
keenauth_management_client resource The management API permissions and IP allowlist of a management application. Import with the application ID.
keenauth_directory resource A SCIM directory of an organization and its provisioning settings; name and settings change in place, organization_id, scim_host and type replace it. Import with the directory ID.
keenauth_directory_role_mapping resource Maps a SCIM directory group to a role; any change replaces it. Import with <directory_id>/<mapping_id>.
keenauth_directory_token resource A SCIM token of a directory, kept in state only encrypted to your RSA public key; any change creates a new token, destroy revokes it. Not importable.
keenauth_webhook_endpoint resource A webhook endpoint; every argument changes in place. The signing secret is kept in state only encrypted to your RSA public key; changing the key or rotation_trigger rotates it. Import with the endpoint ID or key.
keenauth_application data source Reads one application by id or key.
keenauth_connection data source Reads one connection by id or key.
keenauth_organization data source Reads one organization by id or slug.
keenauth_role data source Reads one environment role by id or key.
keenauth_environment data source Reads the environment of the API key: ID, type, region, cell, state and plan.
resource "keenauth_connection" "google" {
  key      = "google"
  type     = "social"
  strategy = "google"
  name     = "google"
  status   = "active"
  settings = jsonencode({ scopes = ["openid", "email", "profile"] })
}

resource "keenauth_application" "storefront" {
  key            = "storefront"
  name           = "Storefront"
  app_type       = "web"
  party          = "first_party"
  redirect_uris  = ["https://shop.example.com/callback"]
  connection_ids = [keenauth_connection.google.id]
}

Import

Applications and connections can be imported by their key or their ID, a custom domain by its hostname or its ID, and an API key by its ID:

tofu import keenauth_application.storefront storefront
tofu import keenauth_connection.google conn_01j9zq3v8c5m2x7k4t6r1n0b3d
tofu import keenauth_custom_domain.login login.example.com

The environment settings, the attack protection and the branding draft exist once per environment; import them with the fixed IDs environment-settings, attack-protection and branding. Destroying one of them only removes it from state.

Safe changes

Secrets

No resource stores a secret in plain text in state. Secrets that you give to KeenAuth are write-only arguments (*_wo): the provider sends them but never writes them to state or plan files, and sends them again only when their *_wo_version changes. They need OpenTofu or Terraform 1.11 or later. Today this is the client secret of an enterprise OIDC connection (keenauth_connection_oidc_config). Application client secrets and signing keys are not managed by this version of the provider; create and rotate them in the dashboard or with the CLI. Secrets that KeenAuth generates cannot be write-only, because a write-only argument only carries a value from the configuration to the provider. The provider never creates API keys, because the raw key would end up in state: create a key with the CLI, store it in your secret manager and import it into keenauth_api_key by its ID. A directory's SCIM token is shown once by the API, so the provider revokes the token that creating a directory returns and never keeps it; keenauth_directory_token creates the token for your identity provider and keeps it in state only encrypted to your RSA public key. keenauth_webhook_endpoint keeps the webhook signing secret the same way, only encrypted to its public_key, and discards it when no key is given. Connection settings must not contain secrets; the API rejects secret-like settings.

Limits of this version