# aca-py & OpenID for Verifiable Presentations **Example for verifier-initiated cross device flow (SIOP)** ```plantuml @startuml 'https://plantuml.com/sequence-diagram skinparam BoxPadding 10 autonumber actor User participant Controller participant "aca-py" as HolderAgent participant "OIDC\nRelying Party" as RP == Holder receives auth request == RP -> Controller : auth request w/ request_uri [see note A] activate Controller Controller -> Controller : derive invitation from auth request [see note B] Controller -> HolderAgent : receive-invitation activate HolderAgent HolderAgent -> HolderAgent : create conn record Controller <-- HolderAgent : conn record == Agent fetches request object == opt without auto-accept Controller -> User : accept invitation? activate User User --> Controller : OK deactivate User Controller -> HolderAgent : accept-invitation deactivate Controller end HolderAgent -> RP : GET request_uri [see note C] activate RP RP -> RP : create request object HolderAgent <-- RP : 200 OK : request object deactivate RP HolderAgent -> HolderAgent : update conn record deactivate Controller HolderAgent -> HolderAgent : create pres ex record & oid4vp record HolderAgent ->> Controller : Event(pres ex record) deactivate HolderAgent activate Controller == Holder creates Verifiable Presentation with W3C credentials == User <- Controller : send presentation? deactivate Controller activate User User --> Controller : OK deactivate User activate Controller Controller -> HolderAgent : get matching credentials activate HolderAgent Controller <-- HolderAgent : credentials deactivate HolderAgent opt User <- Controller : choose credentials deactivate Controller activate User User --> Controller : credentials deactivate User activate Controller end Controller -> Controller : create presentationSpec Controller -> HolderAgent : send-presentation(presentationSpec) activate HolderAgent HolderAgent -> HolderAgent : create vp_token, presentation_submission HolderAgent -> RP : POST redirect_uri (vp_token, presentation_submission) [see Note D] note right : session information is conveyed via ""nonce"". activate RP HolderAgent -> HolderAgent : update pres ex record Controller <-- HolderAgent : pres ex record deactivate HolderAgent deactivate Controller == RP verifies presentation == RP -> RP : verify token deactivate RP deactivate Controller @enduml ``` ## Note A **Auth Request** ``` https://wallet.example.org? client_id=https%3A%2F%2Fclient.example.org%2Fcb &request_uri=https%3A%2F%2Fclient.example.org%2F567545564 ``` See https://openid.net/specs/openid-4-verifiable-presentations-1_0.html for a format specification of a deferred authentication request with request-uri :::info :bulb: **Note** the non-normative authentication request example given in https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-7.2 lacks compliance with OIDC core 1.0, because the required parameter "scope" is missing. ::: To allow for on-device and cross-device scenarios, the login page of the web service presents both a QR code (cross-device) and an app link / universal link (on-device). ## Note B **OOB Invitation Message Derived From Auth Request** ([InvitationMessage](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/protocols/out_of_band/v1_0/messages/invitation.py)) [Aries RFC 0434 Out-of-Band Protocol 1.1](https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband) ```json { "@id": "ba80c9a4-a087-42f3-97df-2612b21ba446", // UUID generated by controller "@type": "https://didcomm.org/out-of-band/1.0/invitation", "label": "Auth Request by https://client.example.org", "handshake_protocols": [ "https://example.org/oid4vp-handshake/0.1" // non-didcomm protocol ], "services": [ { "id": "https://client.example.org", "serviceEndpoint": "https://client.example.org/567545564", // request_uri "type": "oid_request_uri" // custom type } ] } ``` :::info :bulb: **Note** This approach assumes that there are no parameters in the auth request which are not also included in the request object, apart from `request_uri`. If the auth request contains parameters which must be considered, the invitation message could possibly be derived by including the auth request as an attachment. (see [Aries RFC 0434](https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband#messages)) ::: ## Note C **Request Object** ```json { "client_id": "https://client.example.org/post", "redirect_uri": "https://client.example.org/post", "response_type": "vp_token", "response_mode": "post", "presentation_definition": {...}, "nonce": "9391b6c3-e77d-4fec-a4d0-8bb1af85139b" // aca-py: challenge must be UUID4 } ``` <!-- The following presentation definition inside an authentication request (such as in **Note A** requests selected claims from the citizenship credential according to https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-verifier-initiated-cross-de The holder is defined by the `id` of `credentialSubject`. The holder must prove control of the private key belonging to the holder did when presenting the proof to the verifier. **Presentation Definition** ```json { "input_descriptors": [ { "schema": [ { "uri": "https://www.w3.org/2018/credentials#VerifiableCredential" }, { "uri": "https://w3id.org/citizenship#PermanentResidentCard", "required": true } ], "name": "Permanent Resident Card", "id": "citizenship", "constraints": { "limit_disclosure": "required", "fields": [ { "path": [ "$.credentialSubject.id" ], "id": "ea9da655-3c0c-4015-99b0-3108d24675ba" }, { "path": [ "$.credentialSubject.givenName" ] }, { "path": [ "$.credentialSubject.familyName" ] }, { "path": [ "$.credentialSubject.birthDate" ] } ], "is_holder": [ { "field_id": [ "ea9da655-3c0c-4015-99b0-3108d24675ba" ], "directive": "required" } ] } } ], "id": "6728ee4f-ba17-4a02-8989-ed48eb51d73f" } ``` --> **Presentation Exchange Record** ([V20PresExRecord](https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py)) [Aries RFC 0454 Present Proof Protocol 2.0](https://github.com/hyperledger/aries-rfcs/tree/main/features/0454-present-proof-v2) ```json { "updated_at": "2022-10-18T12:19:30.040123Z", "pres_ex_id": "12ae5042-38b5-41c7-8b5c-9fcaef25bb65", "connection_id": "991c4caa-45e2-4caf-8c7e-4fd6786522f4", "thread_id": "f2b497bc-b2b0-463a-86a9-d832df3adca9", "trace": false, "state": "request-received", "initiator": "external", "pres_request": { "@type": "https://didcomm.org/present-proof/2.0/request-presentation", "@id": "f2b497bc-b2b0-463a-86a9-d832df3adca9", "formats": [ { "attach_id": "auth_request", "format": "dif/presentation-exchange/definitions@v1.0" // use DIF format } ], "comment": "auto-created pres request from auth request", "will_confirm": false, "request_presentations~attach": [ { "@id": "auth_request", "mime-type": "application/json", "data": { "json": { "options": { "challenge": "9391b6c3-e77d-4fec-a4d0-8bb1af85139b", // nonce goes here "domain": "https://client.example.org/post" // client_id }, "presentation_definition": {...} } } } ] }, "by_format": {...}, "role": "prover", "created_at": "2022-10-18T12:19:30.040123Z", "auto_verify": false, "oid4vp_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6" // additional parameter } ``` **OID4VP Record** ```json { "oid4vp_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "connection_id": "991c4caa-45e2-4caf-8c7e-4fd6786522f4", "client_id": "https://client.example.org/post", "redirect_uri": "https://client.example.org/post", "request_uri": "https://client.example.org/567545564", "response_type": "vp_token", "response_mode": "post", "nonce": "9391b6c3-e77d-4fec-a4d0-8bb1af85139b", "presentation_definition_uri": null, "pres_ex_id": "12ae5042-38b5-41c7-8b5c-9fcaef25bb65", "state": "request-received" // [initial, request-received, done] } ``` :::info :bulb: **Note** `presentation_definition` objects are not stored in the oid4vp record as they are included in the presentation exchange record anyway. ::: **Presentation Submission** ```json { "id": "cf54ec54-cf04-404d-b1dc-bbe8b6afcd92", "definition_id": "6728ee4f-ba17-4a02-8989-ed48eb51d73f", "descriptor_map": [ { "id": "citizenship", "format": "ldp_vp", "path": "$", "path_nested": { "id": "citizenship", "format": "ldp_vc", "path": "$.verifiableCredential[0]" } } ] } ``` ## Note D **POST presentation** ``` POST /post HTTP/1.1 Host: client.example.org Content-Type: application/x-www-form-urlencoded presentation_submission=...& vp_token=... ``` ## References - https://developer.android.com/training/app-links/ - https://developer.apple.com/ios/universal-links - https://github.com/hyperledger/aries-rfcs/tree/main/features/0434-outofband - https://openid.net/specs/openid-connect-core-1_0.html#RequestUriParameter - https://openid.net/specs/openid-connect-core-1_0.html#RequestObject - https://openid.net/specs/openid-4-verifiable-presentations-1_0.html - https://openid.net/specs/openid-connect-self-issued-v2-1_0.html - https://www.w3.org/TR/did-core/#services - https://www.w3.org/TR/did-spec-registries/#service-types