Enriching Vault OIDC Tokens with SPIFFE Identity Metadata using Terraform

Published: (December 3, 2025 at 02:39 PM EST)
2 min read
Source: Dev.to

Source: Dev.to

Cover image for Enriching Vault OIDC Tokens with SPIFFE Identity Metadata using Terraform

The Goal

We want an application (e.g., a “ChatBot”) to:

  • Authenticate to Vault using the AppRole method.
  • Request an OIDC token.
  • Receive a token containing custom claims such as spiffe_id, business_unit, and environment.

Step 1: Define the Identity Entity

First, define the “who”. In Vault, an Entity represents a unique identity and stores the metadata that will later be injected into the token.

# identities.tf

resource "vault_identity_entity" "application" {
  for_each = local.application_identities_map
  name     = each.key

  # This metadata is what we'll inject into the token later
  metadata = {
    environment   = each.value.identity.environment
    business_unit = each.value.identity.business_unit
    spiffe_id     = "spiffe://vault/application/${each.value.identity.environment}/${each.value.identity.business_unit}/${each.value.identity.name}"
  }
}

Step 2: Configure AppRole Authentication

Next, configure the AppRole auth backend—the standard way for machines to authenticate.

# approle.tf

resource "vault_approle_auth_backend_role" "applications" {
  for_each       = local.application_identities_map
  backend        = vault_auth_backend.approle.path
  role_name      = each.key
  token_ttl      = 3600
  bind_secret_id = true
}

The “Secret Sauce”: Binding AppRole to the Entity

By default, logging in with AppRole creates a generic entity for that role. To use the custom metadata defined in Step 1, explicitly bind the AppRole to the specific Entity via an Entity Alias.

Important Gotcha: When creating an alias for AppRole, the name of the alias must be the Role ID, not the Role Name.

# approle.tf

resource "vault_identity_entity_alias" "approle_applications" {
  for_each = local.application_identities_map

  # CRITICAL: Use role_id, not role_name
  name           = vault_approle_auth_backend_role.applications[each.key].role_id
  mount_accessor = vault_auth_backend.approle.accessor
  canonical_id   = vault_identity_entity.application[each.key].id
}

Ensure the AppRole inherits the entity’s properties by configuring a generic endpoint:

resource "vault_generic_endpoint" "approle_entity_inherit" {
  for_each   = local.application_identities_map
  depends_on = [vault_approle_auth_backend_role.applications]
  path       = "auth/approle/role/${each.key}"

  data_json = jsonencode({
    entity_alias_sole_inherit = true
  })
}

Step 3: Configure the OIDC Template

Finally, set up the OIDC provider and role. The template field injects metadata into the token payload using Vault’s template syntax.

# identity_tokens.tf

resource "vault_identity_oidc_role" "application_identity" {
  name      = "application_identity"
  key       = vault_identity_oidc_key.application_identity.name
  client_id = "spiffe://vault.darkedges.au/gateway"
  ttl       = 86400

  # The Template: Injecting metadata into the JSON payload
  template = .
}

Inspiration

Back to Blog

Related posts

Read more »