light-mode-image
Learn
Claims source

How to configure a Claims source

Introduction

A Claims source lets MATTR VII fetch credential claims from your own data system at issuance time. Two configuration decisions shape how well that integration behaves in production: how MATTR VII authorizes with your endpoint, and how it tells your endpoint which record to look up.

This guide walks through the practical configuration patterns for both. It assumes you have already read the Claims source overview and have a backing endpoint that meets the requirements. For an end-to-end walkthrough using a sample app, see the Claims source tutorial.

Prerequisites

Guide overview

Configuring a claims source involves three concerns:

  1. Authorize the request: Pick the authorization method that matches how your backing system is protected.
  2. Map request parameters: Decide which values MATTR VII should pass to your endpoint, and where they come from.
  3. Error-proof your queries with default values: Make the configuration resilient to missing or unexpected claims.

The examples below use the Configure a claims source endpoint. The same fields are available in the MATTR Portal under Credential Issuance > Claims sources.

Authorize the request

MATTR VII supports two authorization methods. Choose based on how your data system already protects access, not on which is easier to set up. Reusing your existing access control is almost always the right call.

API key

Use api-key when your endpoint is protected by a static shared secret passed in a custom header. MATTR VII sends the configured value in an x-api-key header on every request.

API key authorization
{
    "name": "Customer records",
    "url": "https://records.example.com/claims",
    "authorization": {
        "type": "api-key",
        "value": "6hrFDATxrG9w14QY9wwnmVhLE0Wg6LIvwOwUaxz761m1J"
    },
    "requestParameters": {
        "customerId": {
            "mapFrom": "claims.sub"
        }
    }
}

When this is a good fit:

  • You control the endpoint and can place it behind a key-checking gateway.
  • The endpoint serves only the claims source use case, so a single shared secret is acceptable.
  • You need a quick path to production without standing up an authorization server.

When to avoid it: if your organization already mandates OAuth for service-to-service traffic, do not introduce a parallel key-based path just for claims sources. Rotate the key periodically, and never reuse a key that protects other systems.

MATTR VII does not validate the key when you save the configuration. A typo will not surface until the first issuance attempt, which fails the flow. Test the configuration with a real issuance before rolling it out.

OAuth 2.0 client credentials

Use oauth-client-credentials when your endpoint is protected by an OAuth 2.0 authorization server (Auth0, Okta, Azure AD, Cognito, Keycloak, your own AS). MATTR VII performs the client credentials grant against your tokenEndpoint, then attaches the resulting bearer token as an Authorization: Bearer <token> header on every claims source request.

OAuth client credentials, basic auth method
{
    "name": "Internal CRM",
    "url": "https://crm.example.com/api/claims",
    "authorization": {
        "type": "oauth-client-credentials",
        "tokenEndpoint": "https://auth.example.com/oauth/token",
        "clientId": "afd16fec-8131-4f0c-8f20-1cd5d67f8e29",
        "clientSecret": "1b41186347e4cc716155155cdecbded07536d0f5",
        "tokenEndpointAuthMethod": "client_secret_basic",
        "audience": "https://crm.example.com",
        "scope": "claims:read"
    },
    "requestMethod": "POST",
    "requestParameters": {
        "customerId": {
            "mapFrom": "claims.sub"
        }
    }
}

The tokenEndpointAuthMethod controls how MATTR VII presents the client credentials to your authorization server:

  • client_secret_basic (default): credentials are sent in a Base64-encoded Authorization header on the token request. Use this with most off-the-shelf authorization servers; it is the OAuth 2.0 default.
  • client_secret_post: credentials are sent in the token request body. Use this only when your authorization server does not accept basic auth on the token endpoint (some legacy or self-hosted servers).

Two optional fields tighten the issued token to the right resource:

  • audience: set this when your authorization server scopes tokens by audience (Auth0, Azure AD). The value must match what the claims source endpoint expects in the aud claim.
  • scope: set this when your endpoint requires a specific scope (for example claims:read). Omit it if your AS issues unscoped service tokens.

When this is a good fit:

  • Your data system already lives behind your enterprise identity provider.
  • You need centralized credential rotation, revocation, and audit.
  • Different downstream systems (claims source, webhooks, internal APIs) should be authorized independently.

MATTR VII fetches a fresh token for each issuance request and does not currently cache tokens between requests. Make sure your authorization server can absorb that traffic, and use a dedicated client for the claims source so rate-limiting and rotation can be scoped to it.

Map request parameters

requestParameters is the object MATTR VII sends to your endpoint. When requestMethod is GET (default), each top-level key becomes a query string parameter. When requestMethod is POST, the object is sent as a JSON request body. The keys you choose define the contract with your endpoint; pick names that match what your endpoint expects to receive.

Each parameter is configured in one of three shapes:

ShapeFieldsBehavior
Dynamic mappingmapFromResolved at issuance time from the named path. If the path is missing, no value is sent.
Dynamic with fallbackmapFrom and defaultValueResolved from the path. If the path is missing, defaultValue is used instead.
Static valuedefaultValueThe fixed value is sent on every request, regardless of holder context.

The rest of this step shows the data sources you can map from and the situations they fit.

Map from claims

The claims object holds every claim gathered during the issuance flow:

This is the most common source: it is how you pass a holder-specific lookup key (subject ID, email, employee number, license number) to your endpoint.

Look up a holder by an authentication provider subject
{
    "requestParameters": {
        "subjectId": {
            "mapFrom": "claims.sub"
        }
    }
}
Look up a holder by a domain-specific identifier from an Interaction hook
{
    "requestParameters": {
        "employeeNumber": {
            "mapFrom": "claims.employee_number"
        }
    }
}

The path is dot-notation, so nested claims work the same way: claims.profile.country resolves to the country property of the profile claim.

The exact paths inside claims depend on how each component (Authentication provider, Interaction hook, Credential offer) is configured to expose claims. Inspect the analytics event payload from a test issuance if you are unsure what is available.

Map from authenticationProvider

This object exposes metadata about the Authentication provider used in an Authorization Code flow:

  • authenticationProvider.url: the configured provider URL.
  • authenticationProvider.subjectId: the unique identifier of the authenticated user with the provider.
  • authenticationProvider.providerId: the unique identifier of the provider configuration on your tenant.

Use it when your endpoint expects the canonical subject identifier rather than a claim returned in the ID token, or when the endpoint serves multiple tenants and needs the provider context to route the lookup.

Use the provider subject as the lookup key
{
    "requestParameters": {
        "subjectId": {
            "mapFrom": "authenticationProvider.subjectId"
        },
        "issuer": {
            "mapFrom": "authenticationProvider.url"
        }
    }
}

This source is not populated in Pre-authorized Code flows.

Map from credentialConfiguration

This object exposes the credential configuration being issued:

  • credentialConfiguration.id: unique configuration identifier.
  • credentialConfiguration.type: the credential type (for example org.iso.18013.5.1.mDL).
  • credentialConfiguration.profile: the credential format (mobile for mDocs, compact for CWT, compact-semantic for semantic CWT).

This is useful when a single claims source backs several credential types and your endpoint needs to know which record set or schema to return.

Route the query by credential type
{
    "requestParameters": {
        "holderId": {
            "mapFrom": "claims.sub"
        },
        "credentialType": {
            "mapFrom": "credentialConfiguration.type"
        }
    }
}

Your endpoint can then branch internally: returning driving privileges for an mDL request and academic transcripts for a study record request, against the same holder ID.

Mapping from credentialConfiguration is only useful if your endpoint actually understands the values. The type and profile strings come straight from your MATTR VII configuration. Coordinate the values with whoever owns the backing data system.

Map from wallet

The wallet object exposes information about the wallet making the credential request:

  • wallet.id: the wallet's client identifier. Use it to apply per-wallet logic, for example returning a richer claim set to a wallet you have a direct partnership with.
  • wallet.instanceId: the unique wallet instance identifier from the attestation JWT. Only populated when client attestation is enabled. Use it when your endpoint stores per-device state (renewal counter, device binding, instance-specific entitlements).

The same identifiers are exposed across both OID4VCI and Apple Wallet issuance flows, so a single wallet mapping works regardless of which protocol the wallet used to engage the issuer.

Per-wallet and per-instance lookup
{
    "requestParameters": {
        "holderId": {
            "mapFrom": "claims.sub"
        },
        "walletId": {
            "mapFrom": "wallet.id"
        },
        "walletInstanceId": {
            "mapFrom": "wallet.instanceId"
        }
    }
}

A legacy client object is also available, exposing client.instanceId only. It is kept for backwards compatibility with earlier configurations and is on track for removal. Use wallet in new configurations and migrate existing client.instanceId mappings to wallet.instanceId.

Map from issuanceProtocol

issuanceProtocol is a top-level string that tells your endpoint which protocol the wallet engaged the issuer over. Currently the only supported value is openid4vci, covering both the Authorization Code and Pre-authorized Code flows. Contact us if you need support for additional issuance protocols.

Map this parameter when the same credential configuration is offered through multiple protocols and your endpoint needs to vary the returned data based on the channel, for example applying protocol-specific business rules.

Branch by issuance protocol
{
    "requestParameters": {
        "holderId": {
            "mapFrom": "claims.sub"
        },
        "protocol": {
            "mapFrom": "issuanceProtocol"
        }
    }
}

Static parameters

A parameter with only a defaultValue is sent on every request, with no mapping. Use it for values that are constant for this claims source but that the endpoint still needs (API version, tenant code, fixed feature flag).

Pin an API version
{
    "requestParameters": {
        "apiVersion": {
            "defaultValue": "2024-08"
        },
        "subjectId": {
            "mapFrom": "claims.sub"
        }
    }
}

A defaultValue can also be an object or an array, which is useful when the endpoint accepts structured filters in a POST body:

Static structured value
{
    "requestMethod": "POST",
    "requestParameters": {
        "filters": {
            "defaultValue": {
                "includeArchived": false,
                "regions": ["AU", "NZ"]
            }
        },
        "subjectId": {
            "mapFrom": "claims.sub"
        }
    }
}

Combine sources

You can mix mappings from different sources in a single configuration. The example below identifies the holder from the authentication provider, scopes the query to the credential type, tags the call with the wallet instance and protocol, and pins an API version:

Combined parameters
{
    "requestMethod": "POST",
    "requestParameters": {
        "subjectId": {
            "mapFrom": "authenticationProvider.subjectId"
        },
        "credentialType": {
            "mapFrom": "credentialConfiguration.type"
        },
        "walletInstanceId": {
            "mapFrom": "wallet.instanceId"
        },
        "protocol": {
            "mapFrom": "issuanceProtocol"
        },
        "apiVersion": {
            "defaultValue": "2024-08"
        }
    }
}

Error-proof your queries with default values

Adding a defaultValue alongside mapFrom keeps issuance running when an expected claim is missing or evaluates to undefined. Without a fallback, the parameter is simply omitted from the request, which often causes the endpoint to return a 404 or an empty body, both of which can result in issuance failures depending on how your credential configuration is set up.

Two patterns are worth knowing.

Fallback to a safe value

When the mapped claim is optional, supply a defaultValue that your endpoint can interpret without error. Be careful that the fallback represents the right outcome: for a lookup key, the fallback should match a record your endpoint knows how to handle (often a "no match" placeholder), not a real user.

Fallback for an optional segment
{
    "requestParameters": {
        "subjectId": {
            "mapFrom": "claims.sub"
        },
        "segment": {
            "mapFrom": "claims.customer_segment",
            "defaultValue": "standard"
        }
    }
}

Here, every holder gets a segment value. If the authentication provider did not return one, the endpoint receives standard and can apply its default rules.

Fallback to a structured value

defaultValue accepts strings, arrays, and objects, which is helpful when the endpoint expects a fixed shape:

Structured fallback
{
    "requestMethod": "POST",
    "requestParameters": {
        "permissions": {
            "mapFrom": "claims.permissions",
            "defaultValue": []
        },
        "preferences": {
            "mapFrom": "claims.preferences",
            "defaultValue": {
                "locale": "en-US",
                "format": "standard"
            }
        }
    }
}

What a fallback cannot do

A defaultValue only fires when the mapping fails or resolves to undefined. It does not run when:

  • The claims source returns an error (non-2XX status).
  • The claims source returns an empty body or a body missing the claim you expected.
  • The claims source times out (response longer than three seconds).

For those cases, the lever is in the Credential configuration itself: set a defaultValue on optional claim mappings so issuance can complete even if the claims source returns nothing useful. The two layers compose: the claims source defaultValue keeps the outbound request well-formed, and the credential configuration defaultValue keeps the outbound credential well-formed.

Never use defaultValue to mask an authorization problem. If your endpoint can return data for an unauthenticated or unidentified caller (for example because the lookup key fell back to a static value), the wrong holder may receive someone else's claims. Fall back to safe metadata, not to identifiers.

Best practices

  • Reuse existing access control. If your data system already lives behind OAuth, use oauth-client-credentials; if it lives behind an API gateway with shared keys, use api-key. Do not introduce a parallel auth scheme just for the claims source.
  • Keep parameter names aligned with the endpoint. The keys in requestParameters are the contract with your endpoint. Treat changes as a coordinated deployment, not a configuration tweak.
  • Test with a representative holder. MATTR VII does not validate authorization values, parameter paths, or the endpoint contract at save time. A test issuance is the only way to confirm the integration works end to end.
  • Stay inside the three-second budget. The endpoint must respond within three seconds or the issuance fails. Cache, pre-compute, or move heavy work behind asynchronous processing rather than blocking the claims request.
  • Return {} when no claims are found. An empty JSON object lets issuance proceed using credential configuration defaults. Returning a 404 fails the issuance.
  • Audit fallbacks. Review every defaultValue against whether it could mask a real lookup failure for a real holder. Fallbacks are safest for non-identifying metadata.

What's next?

How would you rate this page?

Last updated on

On this page