Client Attestation
Client Attestation is offered as a closed beta preview feature and is not generally available yet. If you are interested in trying this feature, please contact us.
Overview
Client Attestation enables credential issuers to restrict credential issuance to trusted wallet applications only. This capability provides strong authentication of wallet instances by allowing wallets to cryptographically prove their authenticity before claiming credentials.
This mechanism is particularly valuable when:
- Enforcing wallet policies: Ensuring that only authorized and trusted wallet applications can receive credentials from your issuer.
- Protecting against unauthorized access: Preventing credential theft by verifying the wallet's identity before issuance.
- Supporting platform-based attestation: Leveraging platform-specific security mechanisms (such as mobile app attestation) while presenting them in a standardized format to the issuer.
Client Attestation provides authentication for wallet applications. The holder (wallet) application developer must implement the necessary cryptographic operations to provide the required attestation proofs. For implementation guidance, please contact us.
Standards and specifications
Client Attestation in MATTR VII is based on the following standards:
- OAuth 2.0 Attestation-Based Client Authentication - The core specification for client attestation
- RFC 9449: DPoP (Demonstrating Proof-of-Possession) - DPoP specification
- DPoP Optimization (Combined Mode) - Based on proposed improvements to the attestation specification
How it works
Client Attestation operates through a certificate-based trust chain between three parties:
- Client Attester (Wallet Backend): Manages certificates and issues attestation JWTs to trusted wallet instances.
- Client Instance (Wallet Application): The individual wallet app on a user's device that requests and claims credentials, holds a key pair, and shares its public key so that MATTR VII can validate proof of possession.
- MATTR VII (Credential Issuer): Validates the attestation and issues credentials only to trusted wallet instances. If validation fails, MATTR VII does not issue a credential and returns an error to the wallet.
The following sequence shows a typical client attestation flow:
Certificate trust chain
The trust model relies on certificates:
- Root Certificates: The wallet backend establishes root certificates that MATTR VII must trust. Currently these root certificates are shared with MATTR through an out-of-band configuration process.
- Certificate Chain: When the wallet backend creates an Attestation JWT, it includes a certificate chain of end-entity (and possibly intermediate certificates) in the
x5cheader. This chain must verify back to one of the configured root certificates. - Signature Verification: MATTR VII validates that the Attestation JWT is signed by the private key corresponding to the first certificate included in the
x5ccertificate chain, and that the chain verifies to a configured root certificate.
Setting up client attestation
To enable client attestation for your issuer tenant, you need to:
-
Configure trusted wallet applications: Configure which wallet applications are trusted by your tenant. This includes configuring:
- The wallet's client ID
- Root certificates that the wallet backend uses
- Whether DPoP is required for this wallet
-
Share root certificates: Provide the root certificates from your wallet backend's certificate infrastructure to MATTR.
-
Configure attestation requirements: Determine whether client attestation is required or optional for each wallet application.
Currently, client attestation configuration is managed through out-of-band processes. APIs for wallet trust configuration are planned for future releases.
Attestation proofs
When requesting tokens from MATTR VII, the wallet must include attestation proofs in the request headers. The proofs consist of:
- Attestation JWT: A JWT issued by the wallet backend that attests to the wallet instance's identity and key possession.
- Attestation DPoP: A proof that the wallet instance possesses the private key corresponding to the public key in the Attestation JWT.
Attestation JWT
The Attestation JWT is issued by the wallet backend (Client Attester) and attests that a specific wallet instance possesses a particular cryptographic key.
Structure
{
"alg": "ES256",
"typ": "oauth-client-attestation+jwt",
"x5c": [
"MIIC3zCCAcWgAwIBAgIUJ9R0z5r3e7...base64-cert-leaf...",
"MIIDdzCCAl+gAwIBAgIUFp93k2m9a8...base64-intermediate..."
]
}x5c: Certificate chain that must verify back to a configured root certificate for the requestedclient_id.
{
"sub": "https://wallet-client.example.com",
"iat": 1300814780,
"exp": 1300819380,
"client_instance_id": "unique-instance-identifier",
"cnf": {
"jwk": {
"kty": "EC",
"use": "sig",
"crv": "P-256",
"x": "18wHLeIgW9wVN6VD1Txgpqy2LszYkMf6J8njVAibvhM",
"y": "-V4dS4UaLMgP_4fY4j8ir7cl1TXlFdAgcx55o7TkcSA"
}
}
}sub: The wallet client IDiat,exp: Standard JWT time claims for issued at and expirationclient_instance_id: Optional unique identifier for a specific wallet instancecnf.jwk: The public key of the Client Instance Key Pair. Used to verify the signature of DPoP proofs.
How MATTR VII validates it
MATTR VII performs the following validations on the Attestation JWT:
- Signature verification: The JWT must be signed by the key from the first certificate in the
x5carray - Certificate chain validation: The certificate chain in
x5cmust verify up to one of the root certificates configured for this wallet - Time validation: Standard JWT time claim validation (
iat,exp) - Client matching: The
subclaim must match the client ID making the request
Demonstrating Proof-of-Possession (DPoP)
When a wallet uses DPoP (Demonstrating Proof-of-Possession) for token binding, it can leverage Combined Mode to simplify attestation. In this mode:
- The wallet generates its DPoP proof using the same key pair referenced in the Attestation JWT's
cnf.jwk - The DPoP proof serves dual purposes:
- Proving possession of the attestation key
- Binding tokens to the wallet
- No separate Attestation PoP is needed, reducing complexity
Structure
{
"alg": "ES256",
"typ": "dpop+jwt",
"jwk": {
"kty": "EC",
"x": "YcZGQuz_FdAQnLc9065EbC3oOCwYwIVgHRPQR_DwiuM",
"y": "ujp88e0cXJ4JFcydBdKzrQZk35_jn9ecjxZM7dlZeyM",
"crv": "P-256"
}
}{
"htu": "https://your-tenant.vii.mattr.global/v1/oauth/token",
"htm": "POST",
"jti": "4441dede-e4a8-420a-95ee-2f6716bb384d",
"iat": 1764210264,
"ath": "uU0nuZNNPgilLlLX2n2r-sSE7-N6U4DukIj3rOLvzek",
"htcd": "sha-256=:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=:"
}htu: The HTTP URL of the token endpoint being calledhtm: The HTTP method (e.g.,POST)jti: Unique identifier for this proof (prevents replay attacks)iat: Time issuedath: Optional base64url-encoded SHA-256 hash of theaccess_token. Required when using the access token with resource servers; optional for initial token requests.htcd: Optional base64-encoded SHA-256 hash (content digest) of the HTTP request payload used to validate integrity.
How MATTR VII validates it in Combined Mode
When using Combined Mode, MATTR VII:
- Validates the Attestation JWT as described above
- Validates that the public key in the DPoP proof's
jwkheader matches the public key in the Attestation JWT'scnf.jwk - Validates the DPoP proof signature
- Performs standard DPoP validations (URL, method, time, replay detection and optionally request body integrity)
Making token requests with client attestation
Wallets configured to require client attestation must include the attestation proofs as HTTP headers when calling the Token endpoint.
Request headers
POST /v1/oauth/token HTTP/1.1
Host: your-tenant.vii.mattr.global
Content-Type: application/x-www-form-urlencoded
OAuth-Client-Attestation: eyJhbGciOiJFUzI1NiIsInR5cCI6Im9hdXRoLWNsaWVudC1hdHRlc3RhdGlvbitqd3QiLCJ4NWMiOlsuLi5dfQ...
DPoP: eyJhbGciOiJFUzI1NiIsInR5cCI6ImRwb3Arand0IiwiandrIjp7Imt0eSI6IkVDIiwieCI6Ii4uLiIsInkiOiIuLi4ifX0...
grant_type=authorization_code&code=...Response
If the attestation is valid, MATTR VII returns an access token:
{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9...",
"token_type": "DPoP",
"expires_in": 86400
}If attestation is invalid or missing when required, an error is returned:
{
"error": "invalid_client",
"error_description": "Client attestation validation failed"
}Client instance identification
The client_instance_id claim in the Attestation JWT enables wallet applications to include a unique identifier for the specific wallet instance requesting tokens. This identifier is then included in the issued JWT access token.
Why use client instance identification
By including a unique client_instance_id in the Attestation JWT, the wallet can:
- Enable instance tracking: Maintain a correlation between specific wallet instances and their issued credentials
- Enable instance-specific credential data: MATTR VII can use the
client_instance_idwhen querying configured claims sources, allowing issuers to retrieve instance-specific data from existing data sources for more refined and personalized issuance journeys
This mechanism is particularly valuable in environments where:
- Multiple wallet instances operate simultaneously (e.g., on different devices).
- Additional verification of the token issuance flow is required.
- Issuers need to customize credential content based on the specific device or application instance receiving the credential.
How it works
The client_instance_id flow operates as follows:
- Unique identifier generated: A unique identifier for a specific wallet instance is generated (e.g., a UUID or device-specific identifier)
- Include in Attestation JWT: The wallet backend includes this identifier in the
client_instance_idclaim when creating the Attestation JWT - MATTR VII extracts and uses the identifier: When processing the token request, MATTR VII:
- Extracts the
client_instance_idfrom the Attestation JWT - Includes it in the issued JWT access token
- Can use it when querying configured claims sources to retrieve instance-specific credential data
- Extracts the
By making the client_instance_id available during credential issuance, MATTR VII enables issuers to tailor credential content based on the specific wallet instance. For example, claims sources can return different data sets, permissions, or attributes based on which device or application instance is receiving the credential, supporting use cases like device-specific entitlements or instance-aware access control.
Technical details
The client_instance_id claim is optional in the Attestation JWT. When present:
- The Attestation JWT payload includes the following claims:
sub: The subject of the Attestation JWT (for example, the wallet client application).exp: Expiration time of the JWT.client_instance_id: A unique identifier for the specific wallet instance (for example, a UUID).cnf.jwk: The public key material that the wallet proves possession of when requesting tokens.
{
"sub": "https://wallet-client.example.com",
"client_instance_id": "550e8400-e29b-41d4-a716-446655440000",
"exp": 1300819380,
"iat": 1300814780,
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "base64url-encoded-x-coordinate",
"y": "base64url-encoded-y-coordinate",
"kid": "wallet-key-id"
}
}
}-
And the JWT access token will contain the same value:
JWT Access Token payload { "sub": "user-subject-identifier", "client_id": "https://wallet-client.example.com", "client_instance_id": "550e8400-e29b-41d4-a716-446655440000", "exp": 1300819380, "iat": 1300814780 } -
JWT access tokens only: The
client_instance_idis included only in JWT-format access tokens. JWT access tokens are issued when credential refresh (refresh tokens) is enabled for the tenant; if your tenant uses opaque access tokens (no credential refresh), this claim will not be present.
Requirements and scope
Client instance identification is available under the following conditions:
- This feature applies only to the Authorization Code flow and is not supported for the Pre-authorized Code flow.
- JWT access tokens only: The
client_instance_idis included only in JWT-format access tokens (when credential refresh is enabled for the tenant), not in opaque tokens. - Client attestation enabled: The wallet must be configured for client attestation with the appropriate feature flags and configuration.
Using client instance ID with claims sources
When the client_instance_id is provided through client attestation, MATTR VII makes it available for use in Claims source requests during credential issuance. This enables issuers to retrieve instance-specific data from external systems based on which specific wallet instance is claiming the credential and supports several use cases:
- Device-specific entitlements: Return different credential claims or permissions based on which device is receiving the credential
- Instance-aware data filtering: Filter or customize credential data based on the specific wallet instance
- Audit and tracking: Track which wallet instances have claimed credentials for compliance or security monitoring
- Multi-device management: Manage credentials across multiple devices belonging to the same user, with instance-specific attributes
During the credential endpoint request, MATTR VII queries configured claims sources to retrieve additional credential data. When client attestation is in use and a client_instance_id is present in the access token, MATTR VII includes this information in the data available for request parameter mapping:
{
"authenticationProvider": {
"url": "https://auth-provider.example.com",
"subject": "user-123"
},
"claims": {
// ... user persisted claims
},
"credentialConfiguration": {
"id": "credential-config-id",
"type": "VerifiableCredential",
"profile": "mso_mdoc"
},
"client": {
"instanceId": "550e8400-e29b-41d4-a716-446655440000"
}
}When creating or configuring a claims source, you can map the client.instanceId to request parameters that will be sent to your claims source server:
{
"url": "https://claims.example.com/api/credentials",
"requestParameters": {
"accountType": {
"mapFrom": "claims.accountType"
},
"sub": {
"mapFrom": "authenticationProvider.subject"
},
"deviceId": {
"mapFrom": "client.instanceId"
}
}
}With this configuration, when MATTR VII queries the claims source during credential issuance, it will send a request like:
GET https://claims.example.com/api/credentials?accountType=premium&sub=user-123&deviceId=550e8400-e29b-41d4-a716-446655440000The client.instanceId will only be available when client attestation is enabled and the wallet backend includes the client_instance_id in the Attestation JWT. If the client.instanceId is not available (e.g., when client attestation is not in use), the mapped parameter will be omitted from the claims source request. Design your claims source endpoints to handle requests both with and without the instance ID parameter, unless you exclusively use client attestation for all issuance flows.
Implementation considerations
Certificate management
- Root certificates should be protected and rotated according to your organization's security policies
- Multiple root certificates can be configured to support certificate rotation without service interruption
- Expired or compromised certificates should be removed from the trusted configuration immediately
Key management
- Client Instance Key Pairs should be generated and stored securely on the wallet device
- Private keys should never leave the device or be transmitted to any server
- Consider using hardware-backed keystores or secure enclaves when available
Replay protection
- Always include a unique
jtiin Attestation PoP and DPoP proofs - MATTR VII maintains replay detection to prevent reuse of proofs
- Attestation JWTs can be reused within their validity period, but Attestation PoP proofs cannot
Time synchronization
- Ensure wallet devices have accurate time synchronization
- Clock skew can cause valid attestations to be rejected
- Use reasonable validity windows in
iatandexpclaims to account for minor time differences
Client Instance Validation
When implementing client instance identification:
- Unique identifiers: Use truly unique identifiers for each wallet instance to prevent collisions
- Identifier format: While any string value is supported, UUIDs or similar globally unique identifiers are recommended
- Security implications: The
client_instance_idis not encrypted in the JWT access token, so avoid including sensitive information in the identifier value
How would you rate this page?