This diagram shows how we use OpenID4VCI in the pre-authorized code flow to issue the PID.
In this protocol, the wallet starts a normal OpenID Connect session at the AuthServer (DigiD/nl-rdo-max
), obtaining an authorization code. Next, the wallet uses this code to start the OpenID4VCI issuance protocol in the pre-authorized code flow with the Wallet Server. The Wallet Server finishes the OpenID Connect session with the AuthServer to discover the identity of the wallet user, allowing it to finish issuance.
In the diagram below we introduce an actor called the PidAttributeService, whose responsibility it is to produce the attributes to be issued given the pre-authorized code. In the case of PID issuance it can do this by finishing the OpenID session at the AuthServer that the wallet started. (This actor is a library part of the WalletServer, as opposed to a separate HTTP server; we include it as separate actor here to separate responsibilities.)
In more detail, the protocol works as follows.
- The wallet starts an OpenID Connect session at the AuthServer by sending it an Authorization Request, receiving an authorization code from the AuthServer in response.
- Using the received authorization code, the wallet starts OpenID4VCI issuance in the pre-authorized code flow by POSTing the code as a pre-authorized code in a Token Request to the WalletServer.
- The WalletServer feeds the Token Request with the pre-authorized code to its PidAttributeService component. The PidAttributeService POSTs the Token Request to the AuthServer, transforming only the pre-authorized code in it to a normal authorization code but keeping the other parameters (such as the
state
and the PKCEcode_verifier
) in the Token Request as is, thereby continuing the OpenID Connect session that the wallet previously started. - Using the resulting
access_token
, the PidAttributeService invokes the/userinfo
endpoint of the AuthServer to retrieve the BSN, with which it next does a BRP query, resulting in the attributes to be issued that are returned to the WalletServer. The WalletServer then generates thec_nonce
and anaccess_token
of its own, and returns these to the wallet. - Along with the
access_token
andc_nonce
we also return a preview of the attestations, as a custom addition to the OpenID4VCI protocol. - When the wallet accesses the
batch_credential
endpoint with theaccess_token
and a valid set of proofs of possession (signatures over thec_nonce
validating against the public keys that the wallet wants to have in its PID), the WalletServer generates the attestations and returns them.
Notice that from the perspective of the AuthServer, the OS acts as the User Agent normally does in OpenID Connect, and the WalletServer/PidAttributeService combination acts as a normal OpenID Client: the former starts the session by navigating to the AuthServer with an Authorization Request, and the latter resumes the session with Token and User Info Requests.
sequenceDiagram
autonumber
actor User
participant OS
participant Wallet
participant WalletServer
participant PidAttributeService
participant AuthServer
User->>+Wallet: click "issue PID"
Wallet->>-OS: navigate to AuthServer/authorize?redirect_uri=...
OS->>+AuthServer: GET /authorize?redirect_uri=...
note over User, AuthServer: authenticate user with DigiD app
AuthServer->>AuthServer: generate & store code
AuthServer->>-OS: GET universal_link?code=...
OS->>Wallet: openWallet(code)
activate Wallet
Wallet->>+WalletServer: POST /token(pre-authorized_code)
WalletServer->>+PidAttributeService: getAttributes(pre-authorized_code)
PidAttributeService->>+AuthServer: POST /token(code)
AuthServer->>AuthServer: lookup(code)
AuthServer->>-PidAttributeService: access_token
PidAttributeService->>+AuthServer: GET /userinfo(access_token)
AuthServer->>-PidAttributeService: claims(BSN)
PidAttributeService->>PidAttributeService: obtain attributes from BRP
PidAttributeService->>-WalletServer: attributes
WalletServer->>WalletServer: generate c_nonce, access_token
WalletServer->>-Wallet: access_token, c_nonce, attestation_previews
Wallet->>+User: Show attributes, ask consent
deactivate Wallet
User->>-Wallet: approve with PIN
activate Wallet
Wallet->>Wallet: create PoPs by signing nonce using Wallet Provider
Wallet->>+WalletServer: POST /batch_credential(access_token, PoPs)
WalletServer->>-Wallet: attestations
deactivate Wallet
For generic issuance, we can implement the AttributeService as follows:
- The issuer feeds it a bunch of to-be-issued attestations (e.g.
Vec<UnsignedMdoc>
) and receives a fresh pre-authorized token in return, which it sends to the wallet using a UL or QR; - When the WalletServer calls
getAttributes(pre-authorized_code)
on the AttributeService, it looks up the attributes to be issued using the pre-authorized code and returns them.
This would look like the following diagram.
sequenceDiagram
autonumber
actor User
participant OS
participant Wallet
participant WalletServer
participant GenericAttributeService
participant Issuer
User->>+Issuer: start issuance flow
Issuer->>Issuer: determine attributes to be issued
Issuer->>+WalletServer: createSession(attributes)
WalletServer->>+GenericAttributeService: createSession(attributes)
GenericAttributeService->>GenericAttributeService: generate pre-authorized_code<br/>store attributes
GenericAttributeService->>-WalletServer: pre-authorized_code
WalletServer->>-Issuer: pre-authorized_code
Issuer->>-OS: GET universal_link?pre-authorized_code=...
OS->>Wallet: openWallet(pre-authorized_code)
activate Wallet
Wallet->>+WalletServer: POST /token(pre-authorized_code)
WalletServer->>+GenericAttributeService: getAttributes(pre-authorized_code)
GenericAttributeService->>GenericAttributeService: lookup attributes using pre-authorized code
GenericAttributeService->>-WalletServer: attributes
WalletServer->>WalletServer: generate c_nonce, access_token
WalletServer->>-Wallet: access_token, c_nonce, attestation_previews
Wallet->>+User: Show attributes, ask consent
deactivate Wallet
User->>-Wallet: approve with PIN
activate Wallet
Wallet->>Wallet: create PoPs by signing nonce using Wallet Provider
Wallet->>+WalletServer: POST /batch_credential(access_token, PoPs)
WalletServer->>-Wallet: attestations
deactivate Wallet