Identity Orchestration · 8 min read

Vaults: secrets and environment settings

Keep client secrets, API keys, hosts and client IDs out of your flows. Store them in vaults, read them at run time with Load From Vault, and the same flow runs unchanged on every environment.

The rule: nothing secret or environment-specific in a flow

Anyone who can open a flow, an export or a backup sees every value typed into it. Values that differ per environment also stop a flow from moving from test to production unchanged. So keep both kinds of value in vaults:

  • Secrets: client secrets, passwords, API keys, signing keys.
  • Environment settings: the tenant host, token and service URLs, client IDs, tenant IDs.

A Load From Vault node reads them while the flow runs. Every environment has vaults and entries with the same names but its own values. A secret typed into a flow is a finding to fix, even in a flow marked test or dev.

Vaults and entries

A vault groups values. An entry is one value, identified by its path (for example identityClientSecret) and the name of its vault. There are two vault types:

Vault typeForIn the admin console
Secrets vaultsecretsThe value stays hidden. An administrator must select the show-value icon twice to reveal it.
Variable vaultnon-secret settingsThe value shows in clear.
  1. 1

    Create a vault

    Go to Flow Vaults → Vaults, select Add, enter the name, choose the type, set it Active, and select Add.

  2. 2

    Add an entry

    Go to Flow Vaults → Entries, select Add, enter the Path and Value, choose the Vault Name, set it Active, and select Add.

  3. 3

    Check a value

    In Entries, select the show-value icon next to the entry. For a secrets vault, select it again inside the popup.

  • Choose names carefully. A vault's name can't change once it has entries, and an entry's path and vault can never change. Flows refer to both by name.
  • Never put an environment or tenant name in a vault name. A flow that reads acme-test-config breaks when you promote it.
  • Flows can't read an inactive entry. Deactivate an entry instead of deleting it if you might need it again.
  • You can delete a vault only when it has no entries. Deleted vaults and entries can't be restored.

Use three vaults: secrets, esv and esvFrontend

What matters most is which values may reach the browser. Decide it once per entry, by choosing its vault, instead of in every flow:

VaultHoldsLoaded with frontend availableRead as
secretsclient secrets, passwords, API keysnever (off){{secrets.identityClientSecret}}
esvbackend settings: tenant ID, client IDs, token and service URLs, hostsoff{{esv.tokenUrl}}
esvFrontendnon-secret settings pages may show: feature flags, display settings, banner texton{{esvFrontend.showEmail}}
  1. 1

    Would it hurt if a user saw it in the browser's developer tools?

    Put it in secrets.

  2. 2

    Does a page, a response body or a browser log need it?

    Put it in esvFrontend. Everything there is public: never a secret, a client ID or an internal URL.

  3. 3

    Otherwise

    Put it in esv. If you aren't sure, choose esv; you can move it later.

Use the same three names on every environment, so flows move between them unchanged.

Read values with Load From Vault

Add one Load From Vault node per vault, early on the flow's backend path, before the first node that needs a value. Name its variable after the vault, list the entries you need, and set frontend availability as the vault requires:

{ "vault": "secrets",
  "keys_to_store": ["identityClientSecret"],
  "flow_variable": "secrets",
  "frontend_available": false }
  • Only the entries you list exist afterwards, as {{secrets.<path>}}. There is no direct vault syntax in templates.
  • The node has two outputs, Success and Failure. Connect both.
  • Libraries share variables with the flow that calls them. A Load settings library nested at the start of each journey serves every journey. It can also hold your non-secret constants (a Set Flow Constants node), so you change a limit or a flag in one place.
  • Use the same variable name and the same frontend setting in every loader of a vault. If two loaders of esv differ, backend nodes and pages see different values.

Where a loaded value works

Vault values follow the normal rule for flow variables. A value loaded with frontend availability off works on the server and renders as undefined in anything sent to the browser:

Used inFrontend available offFrontend available on
Backend - HTTP Request URL, headers and body; backend scripts; conditionsthe valuethe value
Flow Session - Update variables mapping, quoted: "{{esv.region}}"the valuethe value
Page HTML and scripts, end-node bodies, browser logsundefinedthe value
  • Keep secrets off and use them only in backend nodes.
  • If a page needs a setting, move the entry to esvFrontend. Don't copy an esv value into a browser-visible variable: that hides the decision inside one flow.
  • In a variable mapping, quote the template ("{{esv.region}}"). A bare template fails for text values.

Example: call a platform API with credentials from the vault

Load From Vault (esv) → Load From Vault (secrets) → Get token → Call the API
                                                     └ Failure ┴──── Failure → error page

The Get token node, a Backend - HTTP Request:

{ "method": "POST",
  "url": "{{esv.tokenUrl}}",
  "headers": { "Content-Type": "application/x-www-form-urlencoded" },
  "data": "grant_type=client_credentials&client_id={{esv.identityClientId}}&client_secret={{secrets.identityClientSecret}}",
  "flow_variable": "token", "keys_to_store": ["data"], "frontend_available": false }

The API call then sends Authorization: Bearer {{token.data.access_token}}. Check the body as well as the headers: a secret typed into a form-encoded body is just as exposed as one in a header. Which audiences and scopes the client needs: Access clients and Scopes.

Thales service nodes get their own token. Give them the client secret from the same loader: "config": { "clientSecret": "{{secrets.oipClientSecret}}" }.

Mistakes to avoid

MistakeWhat happensFix
A wrong vault name in Load From VaultThe run fails at that node. The failure has no output you can connect.Check the name under Flow Vaults → Vaults. Names are case-sensitive.
An entry not listed in the loader's keys{{secrets.x}} stays emptyAdd the entry's path to the loader.
secrets loaded with frontend availability onSecrets can reach the browserTurn it off. Use secrets only in backend nodes.
A vault value in a log nodeThe secret lands in the logs on every runLog that a value is present, never the value.
A secret in Set Flow ConstantsConstants are ordinary variables, visible in the flow and possibly in the browserMove it to secrets.
A vault value shown on a page renders undefinedThe loader has frontend availability offMove the entry to esvFrontend, if it is safe to show.

Related