How to add a claim mapper preset in PPE
Versions marked “overview” do not contain this page. Selecting one opens that version’s documentation overview.
On this page
Adding a preset is data-driven; no new mapper implementation or resolver branch is needed.
1. Verify the provider’s claim contract
Use provider documentation and representative access tokens to determine:
- Exact claim paths and value types.
- Which claims are optional or require provider configuration.
- Candidate precedence.
- Fields that must remain unmapped because their meaning differs.
- Which roles the preset supports:
subject,client, and optionallyworkload.
Avoid guessing mappings. Presets intentionally leave uncertain fields empty.
2. Prototype the mapping inline
Test the intended mapping using claim_map first. It uses exactly the same schema as a preset.
Supported destination fields are listed in
claim_map_config.rs.
3. Add the preset JSON
Create:
crates/builtins/src/plugins/identity_jwt/presets/<name>.json
A typical provider preset looks like:
{
"description": "Describe supported claims, opt-in claims, precedence, and deliberate omissions in detail.",
"claim_map": {
"subject": {
"id": "sub",
"roles": [
{
"path": "vendor.roles",
"array_only": true
}
],
"permissions": {
"paths": [
{
"path": "scope",
"string_only": true
}
],
"split": "whitespace"
}
},
"client": {
"client_id": [
{
"path": "client_id",
"stop_if_present": true
},
"azp"
],
"authorized_scopes": {
"paths": [
{
"path": "scope",
"string_only": true
}
],
"split": "whitespace"
}
}
}
}
The three field forms and their options are documented in
claim_map_config.rs:
"id": "sub"— one path."teams": ["teams", "groups"]— ordered candidates."roles": {"paths": [...], "merge": "union"}— candidates plus options.
Important options:
array_only: accept only arrays.string_only: accept only strings.stop_if_present: do not fall through if the claim exists with an unusable value; scalar fields only.split: "whitespace": split a string such as OAuthscope.merge: "union": combine collection candidates.on_missing: "deny": reject instead of leaving the field empty.
In JSON paths, a literal dot requires \\.. Colons do not need escaping.
4. Register it alphabetically
Add one entry to PRESETS in
presets.rs:
("vendor", include_str!("presets/vendor.json")),
Keep the table alphabetically sorted. include_str! embeds the preset into the binary.
Do not change DEFAULT_PRESET unless intentionally making a breaking behavioral change.
5. Pin the exact mapping in tests
Extend every_provider_preset_declares_the_candidates_it_is_documented_to in
presets.rs
with every important destination field and its ordered paths.
Then add tests covering:
- A representative provider token.
- Candidate precedence and fallbacks.
- String-versus-array behavior.
- Every deliberate omission.
- Any provider-specific legacy claim names.
on_missingor anchor behavior where relevant.
Several table-driven tests automatically check every registry entry for parsing, compilation, unique naming, resolver construction, and factory construction.
6. Update hard-coded provider assumptions
Review these tests when adding another provider:
- Provider presets without workload mappings in
presets.rs. - The explicit provider-name loop in
presets.rs. - Fields intentionally absent from providers in
presets.rs. - Resolver workload-role expectations in
resolver.rs.
Current generic tests expect every preset to support both subject and client. If the new
preset legitimately supports fewer roles, adjust those invariants explicitly instead of adding
speculative mappings.
7. Add an end-to-end case
Add a signed-token test to
claim_map_e2e.rs using:
{"claim_mapper": "vendor"}
This verifies registry lookup, JWT validation, and identity construction together.
8. Update user-facing preset lists
Update:
- Module documentation in
mod.rs. - Identity documentation in
identity-delegation.md.
Add a new changelog entry if appropriate. Do not rewrite an older release note merely because it historically says “four presets.”
9. Validate
cargo nextest run -p praxis-policy-builtins --features jwt --lib
cargo nextest run -p praxis-policy-builtins --features jwt --test jwt
make check
make ci
The minimal code change is the JSON file plus registry entry. Tests, documented omissions, and user-facing lists make it production-ready.