Notes on exploring Credential Issuance
About this page
This page contains my (Steven) notes taken while exploring the new OIDC4CI and OIDC4P specifications.
The relevant specifications
-
OpenID for Verifiable Credential Issuance. Main specificaions. Contains flows for issueing credentials, including OpenID Connect flows for obtaining
access_tokens
. - OpenID for Verifiable Presentations, Specification used for requesting and presenting Verifiable Presentations, needed during the issuance flow.
- OpenID SIOPv2. Specification about communicating with a wallet.
- Status List 2021. Standard used for issuers to revoke or suspend credentials.
- Peer DID Method Specification. A DID format which adds transactions to the resolver results so key rotations can be supported. A simpler version of the keri format.
- DIF Presentation Exchange. Specification which describes a query format for requesting VPs and VCs from a wallet, used in the OpenIDConnecet for Verifiable Presentation flows.
Nuts node features:
What does the Nuts node need to support in order to be a decent trust layer?
- Issuance of VCs to a holder
- Wallet initalizes the flow.
- LRZA credentials
- Request an
NutsAuthCredential
(Toestemmingsverzoek)
- Issuer initializes de flow:
- Issuance of a
NutsAuthCredential
(overdracht, bgz, netwerkzorg)
- Issuance of a
- Wallet initalizes the flow.
- Issuer should be able to change state of the credential: suspending, revoking
- Requester should be able to obtain an
access_token
for (e.g. FHIR) API access- AuthServer should be able to request additional VPs from client.
- Searching for DIDs bases on relevant properties
- Relevance depends on the use-case
- Information should be published
- Trust relation between publisher and the searcher
- All claims should be able to be verified by VP request on actual interaction
- Resolving of public key material bases on (potentially multiple) DID methods
- DID web method for Nuts?
did:nuts:web:nuts.example.com/123
wherenuts.example.com
represents the domain of the vendor. Con: no history of the DID document, trust based on DNS. Potentially add alternative methods, or define a simple web-method based nuts method bassed on did:peer.
- DID web method for Nuts?
- Resolving of endpoint for services
- Most of the OpenIDConnect standards use the
.well_known/x
endpoints. Can this be used as an alternative for the Nuts services?
- Most of the OpenIDConnect standards use the
Issuer initiated flow with SIOP flow:
Example request and respond message for issuing a AuthorisationCredential
in the Nuts network.
We assume that the issuer already knows the subjects DID.
- Issuer DID:
did:nuts:123
- Subject DID:
did:nuts:456
- LRZA Issuer:
did:web:lrza
Stepts are based on the following sequence diagram: Edit
- Credential offer request:
Lookup the wallet meta data and take the
credential_offer_endpoint
This might be found in the services from the DID document, or found on a.well_known
endpoint.
Create a issuer_state
and store it with the current date and subjectDID.
Perform the following request
GET <credential_offer_endpoint>?credential_offer=offer
Where offer
is the url encoded value of the following JSON object:
{
"credential_issuer":"did:nuts:123",
"credentials":[{
"format": "ldp_vc",
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1"
],
"types": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
}],
"grants":{
"authorization_code":{
"issuer_state": "state123"
}
}
- Wallet: Store offer + datum in FetchCredentialQueue
- Wallet: Take next item in the FetchCredentialQueue en create an AuthorizationRequest
Get issuer metadata. Lookup credentials_supported
:
{
"credential_issuer":"did:nuts:123",
"credential_endpoint": "https://nuts.example.com/credentials",
"credentials_supported":[{
"format": "ldp_vc",
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1"
],
"types": [
"VerifiableCredential",
"NutsAuthorizationCredential"
],
"cryptographic_binding_methods_supported": [
"did"
],
"cryptographic_suites_supported": [
"JsonWebSignature2020"
]
},
{...}]
}
[
{
"type":"openid_credential",
"format": "ldp_vc",
"credential_definition": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1"
],
"types": [
"VerifiableCredential",
"NutsAuthorizationCredential"
]
}
},
]
- Perform a
GET https://issuer.example.com/authorize?
response_type=code
&client_id=did:nuts:456
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&authorization_details=...
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&wallet_issuer= https%3A%2F%2Fclient.example.org%2Fwallet%2Fdid%3Anuts%3A456
- Issuer performs a wallet meta data lookup
- Meta information returns
- Auth request to wallet SIOP server:
Build the
presentation_definition
:
{
"id": "request-of-a-LRZA-credential",
"input_descriptors": [{
"id": "some-unique-id",
"constraints": {
"fields": [{
"path": [
"$.issuer"
],
"filter": {
"type": "string",
"pattern": "did:web:lrza"
}
},
{
"path": [
"$.credentialSubject.id"
],
"filter": {
"type": "string",
"pattern": "did:nuts:456"
}
},
{
"path": [
"$.type"
],
"filter": {
"type": "array",
"uniqueItems": true,
"contains": {
"enum": ["VerifiableCredential", "LRZACredential"]
},
"minContains": 2
}
}
]
}
}],
"purpose": "validate the organization identity in order to issue an auth credential",
"format": {
"ldp_vp": {
"proof_type": ["JsonWebSignature2020"]
}
}
}
GET https://client.example.org/wallets/did:nuts:456?
response_type=vp_token
&client_id=did:nuts:123
&redirect_uri=https://issuer.example.org/cb
&presentation_definition=<definition>
&nonce=n-0S6_WzA2Mj
- Token antwoord
Location: https://client.example.org/cb#
presentation_submission=...
&vp_token=...
vp_token:
{
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": [
"VerifiablePresentation"
],
"verifiableCredential": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1"
],
"id": "did:web:lrza#abc",
"type": [
"VerifiableCredential",
"LRZACredentials"
],
"issuer": {
"id": "did:web:lrza"
},
"issuanceDate": "2010-01-01T19:23:24Z",
"credentialSubject": {
"id":"did:nuts:456",
"kvk":"776655",
"name":"De regenboog",
"city":"hengelo",
},
"proof": {
"type": "JsonWebSignature2020",
"created": "2021-03-19T15:30:15Z",
"jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..PT8yCqVjj5ZHD0W36zsBQ47oc3El07WGPWaLUuBTOT48IgKI5HDoiFUt9idChT_Zh5s8cF_2cSRWELuD8JQdBw",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:web:lrza#keys-1"
}
}
],
"id": "ebc6f1c2",
"holder": "did:nuts:456",
"proof": {
"type": "JsonWebSignature2020",
"created": "2021-03-19T15:30:15Z",
"challenge": "n-0S6_WzA2Mj",
"domain": "https://client.example.org/cb",
"jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..GF5Z6TamgNE8QjE3RbiDOj3n_t25_1K7NVWMUASe_OEzQV63GaKdu235MCS3hIYvepcNdQ_ZOKpGNCf0vIAoDA",
"proofPurpose": "authentication",
"verificationMethod": "did:nuts:456#key-1"
}
}
Thoughts: So, during requesting an access_token, instead of sending al information with the request, just tell the other side the scope (i.e. eOverdracht) and the AuthServer will request the required VPs. But what about the user session? Perhaps store the IRMA sessions in the wallet of the care provider and initialize the request with the irma session so the Nuts node knows which IRMA session to provide.