Skip to content

France Identitรฉ Partenaires โ€” OID4VP 1.0 presentation

โ† Back to the Partenaires app overview

This guide describes how a relying party or verifier can test remote presentations from the France Identitรฉ Partenaires app. It covers age verification and OID4VP 1.0 over the remote presentation flow.

ISO 18013 reference

For live access to the latest published versions of the ISO/IEC 18013 documents, use the OpenWallet Foundation's mdoc.online.

Age verification credential

The namespace of the Age Verification credential is eu.europa.ec.av.1. An Age Verification credential is issued together with the PID.

Attribute Value
age_over_18 true
age_over_21 true
issuing_country FR
expiry_date date of the issuance + 3 months

Age verification integration

Use the OID4VP 1.0 flow when a relying party needs to verify that a user is over a required age threshold without collecting the full PID.

End-to-end flow

  1. Create a verification transaction in the relying-party backend.
  2. Generate a fresh state and nonce, and store them server-side with a short expiration time.
  3. Build a signed OID4VP request object with a DCQL query for the age credential.
  4. Publish the request object at an HTTPS request_uri.
  5. Render a QR code or deep link using the openid4vp:// invocation.
  6. The user scans the request with France Identitรฉ and approves the presentation.
  7. France Identitรฉ posts the encrypted authorization response to the response_uri.
  8. The relying-party backend decrypts the response, validates state, validates the wallet presentation, and extracts the age claim.
  9. The relying-party backend returns an age-verification decision to the end-application.
sequenceDiagram
    participant RP as Relying party
    participant User as User device
    participant Wallet as France Identitรฉ
    RP->>RP: Create transaction, nonce, state
    RP->>User: Show QR code / openid4vp deep link
    User->>Wallet: Scan or open request
    Wallet->>RP: Fetch request object from request_uri
    Wallet->>User: Request consent
    Wallet->>RP: POST encrypted direct_post.jwt response
    RP->>RP: Decrypt, validate, verify mdoc, decide
    RP->>User: Continue or deny age-gated flow

For an over-18 check, request only the Age Verification credential:

Field Value
Credential namespace eu.europa.ec.av.1
Doctype eu.europa.ec.av.1
Claim age_over_18
Expected value true
Format mso_mdoc
Holder binding Required

For data minimization, request age_over_18 instead of PID attributes such as name, birth date, or portrait. Set intent_to_retain according to the applicable legal basis and retention policy.

OID4VP 1.0 integration profile

France Identitรฉ supports OID4VP 1.0 remote presentation with signed request objects, DCQL queries, encrypted authorization responses, and the direct_post.jwt response mode.

Verifier responsibilities

The verifier backend is responsible for the complete presentation lifecycle:

  • Serve the signed request object over HTTPS from the request_uri.
  • Bind each request to a single server-side transaction using state and nonce.
  • Accept the authorization response only on the registered response_uri.
  • Decrypt the direct_post.jwt response with the private key matching the public key advertised in client_metadata.jwks.
  • Reject replayed, expired, or unknown state values.
  • Verify the mdoc issuer chain against the correct France Identitรฉ environment certificate.
  • Verify the mdoc device signature / holder binding.
  • Return a business decision from the verified claim, not from client-side data.

The response_uri endpoint is expected to accept only HTTPS POST requests and to return a minimal success or error status. Decrypted claims, tokens, and certificate internals must not be exposed in browser-visible error messages.

Request URI methods

The wallet supports both request object retrieval methods:

  • request_uri_method=get
  • request_uri_method=post

When request_uri_method=post is present, the wallet retrieves the request object by sending an HTTP POST request to the request_uri.

Invocation example

openid4vp://?client_id=x509_san_dns%3Aapi.playground.france-identite.gouv.fr&request_uri=https%3A%2F%2Fapi.playground.france-identite.gouv.fr%2Fv2%2Fidakto%2Fopenid4vp%2Fvp1%2Fauthorization%2Frequest.jwt%2FW6cM6C9VCKoCMp27bjm7fg&request_uri_method=post

Request object profile

Claim Expected value
aud Wallet-facing audience, for example https://api.playground.france-identite.gouv.fr
iss Same verifier identifier as client_id
client_id x509_san_dns:<verifier-dns-name>
response_type vp_token
response_mode direct_post.jwt
response_uri HTTPS endpoint receiving the encrypted authorization response
nonce Fresh, high-entropy value generated per transaction
state Opaque transaction state generated by the verifier
client_metadata.authorization_encrypted_response_alg ECDH-ES, others are supported
client_metadata.authorization_encrypted_response_enc A256GCM, others are supported
client_metadata.jwks Verifier encryption keys.
client_metadata.vp_formats_supported.mso_mdoc Supported mdoc issuer-auth and device-auth algorithms
dcql_query DCQL presentation query. The reference request asks for eu.europa.ec.av.1 / age_over_18.

The verifier is not whitelisted by France Identitรฉ for this OID4VP 1.0 flow. Trust is established from the request object and the verifier certificate material, including the x509_san_dns client identifier.

Request object validation checklist

Before a request is displayed to the user, verify that:

  • client_id and iss use the same x509_san_dns identifier.
  • The DNS name in client_id matches the verifier signing certificate SAN.
  • request_uri and response_uri use HTTPS.
  • nonce is unique per transaction and cannot be reused.
  • state is high entropy and mapped to one pending transaction.
  • response_mode is direct_post.jwt.
  • client_metadata.jwks contains the encryption public key corresponding to the response decryption private key.
  • The DCQL query requests only the claims required for the age decision.

Minimal age-verification DCQL example

{
  "credentials": [
    {
      "id": "age_over_18",
      "format": "mso_mdoc",
      "meta": {
        "doctype_value": "eu.europa.ec.av.1"
      },
      "claims": [
        {
          "path": [
            "eu.europa.ec.av.1",
            "age_over_18"
          ],
          "intent_to_retain": true
        }
      ],
      "multiple": false,
      "require_cryptographic_holder_binding": true
    }
  ]
}

Authorization response handling

France Identitรฉ sends the presentation result to the response_uri using direct_post.jwt. Treat the incoming value as an encrypted authorization response, not as a plain JSON body.

At a minimum, the response endpoint must:

  1. Parse the posted response parameter.
  2. Decrypt the JWE using the verifier private key associated with the request object's jwks entry.
  3. Validate state against the transaction created before the QR code or deep link was displayed.
  4. Validate nonce binding inside the presentation according to the OID4VP and mdoc verification rules.
  5. Extract the vp_token.
  6. Verify the mdoc issuer authentication and device authentication.
  7. Validate the issuer certificate chain against the right environment certificate in the Issuer certificates and trust material section.
  8. Read eu.europa.ec.av.1.age_over_18.

The verifier is expected to fail closed. If decryption, state validation, nonce validation, certificate validation, holder binding, or claim extraction fails, the age-verification decision must be negative.

Reference request object

The following request object illustrates the supported OID4VP 1.0 profile.

OID4VP 1.0 request object payload
{
  "aud": "https://api.playground.france-identite.gouv.fr",
  "client_id": "x509_san_dns:api.playground.france-identite.gouv.fr",
  "client_metadata": {
    "authorization_encrypted_response_alg": "ECDH-ES",
    "authorization_encrypted_response_enc": "A256GCM",
    "client_uri": "https://api.playground.france-identite.gouv.fr/v2/idakto/openid4vp",
    "contacts": [
      ""
    ],
    "jwks": {
      "keys": [
        {
          "alg": "ECDH-ES",
          "crv": "BP-320",
          "kid": "74947646-4c9f-11f1-9044-c69e37a7b878",
          "kty": "EC",
          "use": "enc",
          "x": "CEYs0pvwYF3SSTM6aGsn0RtXH57yigpKxXVaKenXVh4_JZsLAo-lew",
          "y": "F-oFm59AccGVRQsQgo37x_g1ktE-MgbIbybKWyt-eAgfmFvyboHZ_g"
        },
        {
          "alg": "ECDH-ES",
          "crv": "BP-384",
          "kid": "74954a2d-4c9f-11f1-9044-c69e37a7b878",
          "kty": "EC",
          "use": "enc",
          "x": "eSx71sD518B8TEc-7E7DG9xmWO44TWG-dapyYLROR6Y31XXrESD6ydGLpHb79tFu",
          "y": "do-4v0YFdGFaCQqC2eMYUA71Qo4WmwDnbr7F8ZC_PGU8R75jgYcHQyhPXntPs4If"
        },
        {
          "alg": "ECDH-ES",
          "crv": "BP-512",
          "kid": "74962304-4c9f-11f1-9044-c69e37a7b878",
          "kty": "EC",
          "use": "enc",
          "x": "R1vFUbydFHaW2NAp93QbXQgJ6gaTeLHYv-uLXu79emOGtY3ObLWINEuVUNpQChbrRa-FDRcxQDt9KHbQcic05A",
          "y": "lUVYKHB2oAysg77P_4BAbxrDLbGj1vF90aB548Ra97Whz0bwgnp0F4WCYaSrpHQAffSNXjP93zhHzW6iB6ERZQ"
        },
        {
          "alg": "ECDH-ES",
          "crv": "P-256",
          "kid": "74962680-4c9f-11f1-9044-c69e37a7b878",
          "kty": "EC",
          "use": "enc",
          "x": "lvLAevmcYq4ZjPqPccKaJsZj2Vel4ufyKLzW820JQKE",
          "y": "se-XRsppIRI7OSN_-03N4MWQC_ZAEzmaPnb5UPibrwE"
        },
        {
          "alg": "ECDH-ES",
          "crv": "P-384",
          "kid": "74963099-4c9f-11f1-9044-c69e37a7b878",
          "kty": "EC",
          "use": "enc",
          "x": "LMcUEdJQud2tIFxTMw7Q7vtU4aWrlyBczGvhQa2uKU_MzYPQah13myUZayZsr_7V",
          "y": "sY3ghVJklRJoLMw6wQmaZvFDUoHxok_U6J4Z3Sie49L7jNB9ymqrPO8BNo56SWaE"
        },
        {
          "alg": "ECDH-ES",
          "crv": "P-521",
          "kid": "749649b6-4c9f-11f1-9044-c69e37a7b878",
          "kty": "EC",
          "use": "enc",
          "x": "ANr0_-kfE2v73zsyL2wYUpdHlajnRq5ITJfcLd-cfJjsjHe8S6gVXXjcTpxxdvdVAY7wvv4gi-3Sxf3iBNlJh9pe",
          "y": "AKlkOMkeLbG1VE4GGRmbUkarcelummfRsgMmoe6BkUjXOLyb6oAfLOAWQ-qgFssssvA7axHy-hdxnQx3vA2R9Klx"
        },
        {
          "alg": "ECDH-ES",
          "crv": "BP-256",
          "kid": "7496abb0-4c9f-11f1-9044-c69e37a7b878",
          "kty": "EC",
          "use": "enc",
          "x": "Bf1Bf8eSvZoR-S6QX1Hmcca-nkxh5yf8nh0QCjf8M80",
          "y": "SQOBZ9_IgblK-JgaJiQgsE4RmkaWl-qtlKnNDyqx1SY"
        }
      ]
    },
    "response_types": [
      "vp_token"
    ],
    "software_id": "64c1db22-b726-468a-aaaa-24fc9e632233",
    "software_version": "1.1.0-SNAPSHOT",
    "vp_formats_supported": {
      "dc+sd-jwt": {
        "kb-jwt_alg_values": [
          "ES256",
          "ES384",
          "ES512"
        ],
        "sd-jwt_alg_values": [
          "ES256",
          "ES384",
          "ES512"
        ]
      },
      "mso_mdoc": {
        "deviceauth_alg_values": [
          "ES256",
          -7,
          "ESP256",
          -9,
          "ES384",
          -35,
          "ESP384",
          -51,
          "ES512",
          -36,
          "ESP512",
          -52,
          "ESB256",
          -261,
          "ESB320",
          -262,
          "ESB384",
          -263,
          "ESB512",
          -264,
          -65537,
          -65538,
          -65539,
          -65542,
          -65543,
          -65544,
          -65545
        ],
        "issuerauth_alg_values": [
          "ES256",
          -7,
          "ESP256",
          -9,
          "ES384",
          -35,
          "ESP384",
          -51,
          "ES512",
          -36,
          "ESP512",
          -52,
          "ESB256",
          -261,
          "ESB320",
          -262,
          "ESB384",
          -263,
          "ESB512",
          -264
        ]
      }
    }
  },
  "dcql_query": {
    "credentials": [
      {
        "claims": [
          {
            "intent_to_retain": true,
            "path": [
              "eu.europa.ec.av.1",
              "age_over_18"
            ]
          }
        ],
        "format": "mso_mdoc",
        "id": "A3DiaCvAGOGD6UypKzi08w",
        "meta": {
          "doctype_value": "eu.europa.ec.av.1"
        },
        "multiple": false,
        "require_cryptographic_holder_binding": true
      }
    ]
  },
  "iss": "x509_san_dns:api.playground.france-identite.gouv.fr",
  "nonce": "8l2Kep-ga_lU_Qmj-4Fv-1ZIu7TMcsqfTabKLDHOVgs",
  "response_mode": "direct_post.jwt",
  "response_type": "vp_token",
  "response_uri": "https://api.playground.france-identite.gouv.fr/v2/idakto/openid4vp/vp1/authorization/response.jwt",
  "state": "cPsfZvOptPHUJSxTW8eTNts_mphcAnuvhv5TDgqM-lc"
}

Testing remote presentation (ISO 18013-7 Annex B and OID4VP 1.0)

  1. From the application home screen, tap the blue button at the bottom of the screen.
  2. Scan the presentation request QR code.

A remote verifier for online age verification is available in the playground: proof of age verifier.

Reference verifiers are also available:

Technical references

The following references are relevant for implementers integrating France Identitรฉ into an age-verification relying-party flow:

Reference Scope
EU Age Verification profile Age-verification profile and implementation guidance.
OpenID for Verifiable Presentations 1.0 OID4VP 1.0 protocol specification.
OpenID4VC High Assurance Interoperability Profile 1.0 HAIP 1.0 profile for high-assurance OpenID4VC interoperability.
ISO/IEC 18013 working documents ISO/IEC 18013 working documents, including mobile driving licence and mdoc-related material.
ISO/IEC 18013-7 Annex B protocol PDF Local copy of the ISO/IEC 18013-7 Annex B protocol material used by the playground documentation.