Enriching Vault OIDC Tokens with SPIFFE Identity Metadata using Terraform
Source: Dev.to

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, andenvironment.
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 = .
}