light-mode-image
Learn
Credential issuance

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:

How it works

Client Attestation operates through a certificate-based trust chain between three parties:

  1. Client Attester (Wallet Backend): Manages certificates and issues attestation JWTs to trusted wallet instances.
  2. 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.
  3. 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:

  1. 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.
  2. Certificate Chain: When the wallet backend creates an Attestation JWT, it includes a certificate chain of end-entity (and possibly intermediate certificates) in the x5c header. This chain must verify back to one of the configured root certificates.
  3. Signature Verification: MATTR VII validates that the Attestation JWT is signed by the private key corresponding to the first certificate included in the x5c certificate 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:

  1. 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
  2. Share root certificates: Provide the root certificates from your wallet backend's certificate infrastructure to MATTR.

  3. 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

Header
{
  "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 requested client_id.
Payload
{
  "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 ID
  • iat, exp: Standard JWT time claims for issued at and expiration
  • client_instance_id: Optional unique identifier for a specific wallet instance
  • cnf.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:

  1. Signature verification: The JWT must be signed by the key from the first certificate in the x5c array
  2. Certificate chain validation: The certificate chain in x5c must verify up to one of the root certificates configured for this wallet
  3. Time validation: Standard JWT time claim validation (iat, exp)
  4. Client matching: The sub claim 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

Header
{
  "alg": "ES256",
  "typ": "dpop+jwt",
  "jwk": {
    "kty": "EC",
    "x": "YcZGQuz_FdAQnLc9065EbC3oOCwYwIVgHRPQR_DwiuM",
    "y": "ujp88e0cXJ4JFcydBdKzrQZk35_jn9ecjxZM7dlZeyM",
    "crv": "P-256"
  }
}
Payload
{
  "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 called
  • htm: The HTTP method (e.g., POST)
  • jti: Unique identifier for this proof (prevents replay attacks)
  • iat: Time issued
  • ath: Optional base64url-encoded SHA-256 hash of the access_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:

  1. Validates the Attestation JWT as described above
  2. Validates that the public key in the DPoP proof's jwk header matches the public key in the Attestation JWT's cnf.jwk
  3. Validates the DPoP proof signature
  4. 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

Combined Mode (with DPoP)
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:

Successful response
{
  "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9...",
  "token_type": "DPoP",
  "expires_in": 86400
}

If attestation is invalid or missing when required, an error is returned:

Error response
{
  "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_id when 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:

  1. Unique identifier generated: A unique identifier for a specific wallet instance is generated (e.g., a UUID or device-specific identifier)
  2. Include in Attestation JWT: The wallet backend includes this identifier in the client_instance_id claim when creating the Attestation JWT
  3. MATTR VII extracts and uses the identifier: When processing the token request, MATTR VII:
    • Extracts the client_instance_id from 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

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.
Attestation JWT payload
{
  "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_id is 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_id is 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:

Data available for claims source request 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:

Claims source configuration example
{
  "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-446655440000

The 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 jti in 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 iat and exp claims 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_id is not encrypted in the JWT access token, so avoid including sensitive information in the identifier value

How would you rate this page?

On this page