# 2. Integration Steps

### Prerequisites

- Legal entity has completed onboarding in the ecosystem.
- LEAR can access the Issuer service and issue a LEARCredentialMachine to its machine(s).
- DID method supported: did:key.
- Access to developer documentation and test environment URLs.

### Step 1 - Machine credential issuance

- The legal entity accesses the Issuer service and issues a LEARCredentialMachine to its machine.
- The service generates a key pair that is bound to the LEARCredentialMachine. Keep the Private Key secure.
- The machine obtains a DID identifier. This DID will act as the client identifier. The DID is the result of using the public key of the key pair in a did:key format.
- The LEARCredentialMachine is a JWT VC.
- The process to obtain the LEARCredentialMachine in the LEAR wallet is similar to the other VC issuances.

**Outcome**: machine holds a valid LEARCredentiaMachine bound to its DID.

### Step 2 - Client configuration

- Client type: confidential.
- Store LEARCredentialMachine and Private Key securely (Vault, HSM).

**Outcome**: client is ready to authenticate.

### Step 3 - Acquire token

To acquire a token, the client must build a Verifiable Presentation (VP) that contains the LEARCredentialMachine (as jwt\_vc\_json), sign this VP as a JWT with the machine’s private key, embed it inside a client assertion JWT with the required claims, and finally POST a client\_credentials request to the Verifier Token Endpoint.

#### Build the VP JWT (vp\_token)

LEARCredentialMachine as a JWT VC string. If it is stored Base64‑encoded, decode it first.

VP object claims:

- @context
- type
- verifiableCredential

Example VP object:

<div align="left" dir="ltr" id="bkmrk-%7B-%C2%A0-%C2%A0-%22%40context%22%3A-%5B%22"><table><colgroup><col></col></colgroup><tbody><tr><td>{

 "@context": \["https://www.w3.org/2018/credentials/v1"\],

 "type": \["VerifiablePresentation"\],

 "verifiableCredential": \["eyJhb...ssw5c"\]

}

</td></tr></tbody></table>

</div>VP JWT claims (signed with the machine private key):

- iss = DID of the machine (same as ‘sub’ in LEARCredentialMachine).
- sub = DID of the machine.
- aud = Verifier Token Endpoint URL.
- iat = current time (seconds).
- nbf = same as iat.
- exp = short expiry (e.g. iat + 10s).
- jti = UUID (unique).
- vp = VP object above.

Example VP JWT payload:

<div align="left" dir="ltr" id="bkmrk-%7B-%C2%A0-%C2%A0-%22iss%22%3A-%22did%3Ake"><table><colgroup><col></col></colgroup><tbody><tr><td>{

 "iss": "did:key:zDna...",

 "sub": "did:key:zDna...",

 "jti": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5",

 "aud": "https://verifier.dome-marketplace.eu/token",

 "nbf": 1541493724,

 "iat": 1541493724,

 "exp": 1573029723,

 "vp": {

 "@context": \["https://www.w3.org/2018/credentials/v1"\],

 "type": \["VerifiablePresentation"\],

 "verifiableCredential": \["eyJhb...ssw5c"\]

 }

}

</td></tr></tbody></table>

</div>#### Build the client assertion JWT

Claims (profile):

- iss = DID of the machine.
- sub = DID of the machine.
- aud = Verifier Token Endpoint URL (issuer identifier per RFC 8414).
- jti = UUID v4 (single use).
- iat = current time in seconds.
- exp = iat + 10 seconds (short lifetime to prevent replay).
- vp\_token= base64url (unpadded) encoding of the VP JWT string.

Correctness rules:

- Use NumericDate in seconds for iat/exp.
- Use base64url per RFC 7515 for vp\_token, not standard Base64.
- Do not include presentation\_submission.
- Sign with machine private key (Header: alg = ES256 or RS256, kid = DID key).

#### Make the request

- Endpoint: POST /token
- Content-Type: application/x-www-form-urlencoded

- Parameters:
- grant\_type=client\_credentials (REQUIRED)
- client\_id=&lt;DID or configured client id&gt; (REQUIRED)
- client\_assertion\_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer (REQUIRED)
- client\_assertion=&lt;JWT&gt; (REQUIRED)


<div align="left" dir="ltr" id="bkmrk-post-%2Ftoken-http%2F1.1"><table><colgroup><col></col></colgroup><tbody><tr><td>POST /token HTTP/1.1

Host: verifier.dome-marketplace.eu

Content-Type: application/x-www-form-urlencoded

  
grant\_type=client\_credentials&amp;

client\_assertion\_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&amp;

client\_assertion=eyJhbGciOiJSUzI1NiIsImtpZCI6IjIyIn0...&lt;snip&gt;

</td></tr></tbody></table>

</div>#### Obtain the response

If the request is valid, the Verifier issues an access token:

<div align="left" dir="ltr" id="bkmrk-%7B-%C2%A0-%C2%A0-%22access_token%22"><table><colgroup><col></col></colgroup><tbody><tr><td>{

 "access\_token": "eyJraWQiOiJkaWQ6a2V5OnpEb...k9aYcDBWcGww",

 "token\_type": "Bearer",

 "expires\_in": 3600

}

</td></tr></tbody></table>

</div>- Cache-Control: no-store must be included in the response.
- Refresh tokens are not supported.

Format of the access\_token:

<div align="left" dir="ltr" id="bkmrk-%7B-%C2%A0-%C2%A0-%22kid%22%3A-%22did%3Ake"><table><colgroup><col width="670"></col></colgroup><tbody><tr><td>{

 "kid": "did:key:zDnaekiwkWcXnHaW6au3BpmfWfrtVTJZrA3EHgLvcbm6EZnup",

 "typ": "JWT",

 "alg": "ES256"

}

</td></tr><tr><td>{

 "iss": "https://verifier.dome-marketplace.eu",

 "aud": "https://verifier.dome-marketplace.eu,

 "sub": "did:key:zDnaerQi587EqQLqEaj7qbxc46hzdjX2goNsmLTq1X6HqzzjP",

 "exp": 1745408685,

 "iat": 1745405085,

 "jti": "1700c742-5457-4c02-8e6f-020a94054519",

 "client\_id": "https://verifier.dome-marketplace.eu"

 "scope": "machine learcredential",

 "vc": {

 "@context": \[

 "[https://www.w3.org/ns/credentials/v2](https://www.w3.org/ns/credentials/v2)",

 "[https://dome-marketplace.eu/.well-known/credentials/lear\_credential\_machine/w3c/v2](https://credentials.eudistack.eu/.well-known/credentials/lear_credential_machine/w3c/v2)"

 \],

 "type": \[

 "VerifiableCredential",

 "LEARCredentialMachine"

 \],

 "issuer": {

 "id": "did:elsi:VATES-A12345678",

 "organization": "TRUST SERVICES, S.L." ,

 "country" : "ES",

 "commonName" : "TRUST SERVICE ELECTRONIC SEAL FOR VERIFIABLE CREDENTIALS",

 "serialNumber": "610dde5a0000000003"

 },

 "credentialSubject": {

 "mandate": {

 "mandator": {

 "id": "did:elsi:VATFR-B12345678",

 "organizationIdentifier": "VATFR-B12345678",

 "organization": "GOOD AIR, S.L.",

 "country": "FR",

 "commonName": "JEAN MARTIN - CNI 880692310285",

 "serialNumber": "880692310285",

 "email": "jean.martin@goodair.fr"

 },

 "mandatee": {

 "id": "did:key:zDnaey7ZcQ1gfXxaZSYffjvhrrFtd7PQdQtJpofzRJNCwydHL",

 "domain": "dpas.goodair.fr",

 "ipAddress": "195.70.63.244"

 },

 "power": \[{

 "type": "domain",

 "domain": "DOME",

 "function": "Onboarding",

 "action": \["Execute"\]

 }\]

 }

 },

 "validFrom": "2025-09-15T06:11:19.802230162Z",

 "validUntil": "2026-09-15T06:11:19.802230162Z",

 "credentialStatus": {

 "id": "https://issuer.dome-marketplace.eu/credentials/status/1#urn:uuid:68422e47-5d69-4e0b-8a49-34990f2f76a,

 "type": "PlainListEntity",

 "statusPurpose": "revocation",

 "statusListIndex": "urn:uuid:68422e47-5d69-4e0b-8a49-34990f2f76a2",

 "statusListCredential": "https://issuer.dome-marketplace.eu/credentials/status/1"

 }

 }

}

</td></tr></tbody></table>

</div>#### Pitfalls to avoid

- Milliseconds vs seconds in time claims.
- Using Base64 instead of Base64URL.
- Wrong iss/sub values (must be DID).
- VP with more than one LEARCredentialMachine.
- Replay attacks: enforce unique ‘jti’ within the expiration window.