generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Documentation for Verifying Presentations (#636)
* pres docs - bug in sdk * fix tests * update pres example * Apply suggestions from code review Co-authored-by: Andres Uribe <[email protected]> --------- Co-authored-by: Andres Uribe <[email protected]>
- Loading branch information
1 parent
55274ba
commit 9c64560
Showing
10 changed files
with
203 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# How To: Verify a Presentation | ||
|
||
## Background | ||
|
||
By now you should be familiar with [DIDs](did.md) and [Verifiable Credentials](credential.md). A next logical question, once you've started using the technologies, is "how do I make use of it?" To use Verifiable Credentials anywhere you need to present them — that is to share them with another party. There are a few pieces of sharing: creating a package that you share and transmitting the data to another party. Transmission, simliar to credential issuance, can be accomplished with a number of different mechanisms like [Web5](https://github.com/TBD54566975/dwn-sdk-js#readme) and [OpenID Connect](https://openid.net/sg/openid4vc/). Packaging a credential, or set of credentials, into a presentation is something that has been standardized at the W3C in the same specification that defines Verifiable Credentials, called [Verifiable Presentations](https://www.w3.org/TR/vc-data-model/#presentations-0). | ||
|
||
Verifiable Presentations are a standard data container for sharing credentials that provide a number of benefits, namely: | ||
|
||
1. A common data format designed to work with Verifiable Credentials, reusable in any number of use cases. | ||
2. Guidance on constructing presentations that ensures integrity protection and guards against replay attacks. | ||
|
||
Presentations impose no constraints on who can construct a presentation, or what may be presented. This means if you have multiple credentials issued to different DIDs, you can still construct a Verifiable Presentation to present those credentials at the same time. This is why you will see that the [`proof` property](https://www.w3.org/TR/vc-data-model/#example-basic-structure-of-a-presentation) can be an array. Verifiers must take care to make sure that the credentials in a given presentation _can be_ presented by the presenter, and that the credentials themselves are valid. | ||
|
||
Verifiable Presentations are currently accepted in two main SSI Service flows: when verifying [Presentation Submissoins](https://identity.foundation/presentation-exchange/spec/v2.0.0/#presentation-submission) while using [Presentation Exchange](https://identity.foundation/presentation-exchange/spec/v2.0.0), and verifying [Credential Applications](https://identity.foundation/credential-manifest/#credential-application) while using [Credential Manifest](https://identity.foundation/credential-manifest). As a utility, we've also exposed an endpoint to statelessly verify a presentation at `/v1/presentations/verification`. | ||
|
||
## Constructing a Verifiable Presentation | ||
|
||
Constructing a Verifiable Presentation is out of scope for the SSI Service, since the service acts as a utility for organizations managing their own credentials. However, the [SSI SDK](https://github.com/TBD54566975/ssi-sdk) provides a standards-based implementation of Verifiable Presentations using the [JWT representation](https://www.w3.org/TR/vc-data-model/#jwt-and-jws-considerations). | ||
|
||
Library code for Verifiable Presentations can be found [here](https://github.com/TBD54566975/ssi-sdk/blob/d5c302a1d9b9d04c1636a0c8dfda015f61bb0f6b/credential/model.go#L110) with associated signing and verificatoin logic [here](https://github.com/TBD54566975/ssi-sdk/blob/d5c302a1d9b9d04c1636a0c8dfda015f61bb0f6b/credential/integrity/jwt.go#L208). An example of constructing a Verifiable Presentation using the SDK is provided below as a [runnable unit test here](presentation_test.go). | ||
|
||
Upon running, we should see the test create a credential and a presentation for that credential as a JWT. | ||
|
||
```plaintext | ||
eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrI3o2TWttTjEyOTZ1YXBIbU02QTI4bkdaR2RBRW5pRDFhYTVSZEZDbjhKRXVucVY5ayIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTExNzU2MjQsImlzcyI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrIiwianRpIjoiYjQ0OTI0ZWEtMDIwMi00ZTllLWJiMmEtOTg5YmQwNWQ1N2FlIiwibmJmIjoxNjkxMTc1NjI0LCJub25jZSI6IjVjNmFhZDc2LWUyZWYtNGNiNy1iMWE4LTI3MjZiMjRhM2Y0ZSIsInZwIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0ltdHBaQ0k2SW1ScFpEcHJaWGs2ZWpaTmEyMU9NVEk1Tm5WaGNFaHRUVFpCTWpodVIxcEhaRUZGYm1sRU1XRmhOVkprUmtOdU9FcEZkVzV4VmpsckkzbzJUV3R0VGpFeU9UWjFZWEJJYlUwMlFUSTRia2RhUjJSQlJXNXBSREZoWVRWU1pFWkRiamhLUlhWdWNWWTVheUlzSW5SNWNDSTZJa3BYVkNKOS5leUpwWVhRaU9qRTJPVEV4TnpVMk1qUXNJbWx6Y3lJNkltUnBaRHByWlhrNmVqWk5hMjFPTVRJNU5uVmhjRWh0VFRaQk1qaHVSMXBIWkVGRmJtbEVNV0ZoTlZKa1JrTnVPRXBGZFc1eFZqbHJJaXdpYW5ScElqb2lPRFUyTnpRellURXRZamxsWWkwME56Z3lMV0kzTkRjdE5UbGtOekkzWVRGaVlXWTFJaXdpYm1KbUlqb3hOamt4TVRjMU5qSTBMQ0p1YjI1alpTSTZJamsyTkRkalltTmtMV0k0WkRndE5HVXhNeTFpTURKa0xURXpZelUxTm1RNVlqRTFPQ0lzSW5OMVlpSTZJbVJwWkRwclpYazZlalpOYTIxT01USTVOblZoY0VodFRUWkJNamh1UjFwSFpFRkZibWxFTVdGaE5WSmtSa051T0VwRmRXNXhWamxySWl3aWRtTWlPbnNpUUdOdmJuUmxlSFFpT2xzaWFIUjBjSE02THk5M2QzY3Vkek11YjNKbkx6SXdNVGd2WTNKbFpHVnVkR2xoYkhNdmRqRWlYU3dpZEhsd1pTSTZXeUpXWlhKcFptbGhZbXhsUTNKbFpHVnVkR2xoYkNKZExDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUpsYlhCc2IzbGxjaUk2SWxSQ1JDSXNJbXB2WWxScGRHeGxJam9pVkhWMGIzSnBZV3dnUVhWMGFHOXlJbjE5ZlEuWEJPbjBTd2RZZUMwN2dHM1VkT1ZLeHV2YXpfWVRpRkNmZ2tpZXJhZUZnVkEtT2tkWDM1SWl6T0NhdUtqdWlsQXJzZklvMkNYN1pYaDl3djRhUXZFRGciXX19.pJXQXSJcu4U752IE0IH21Yw26OsGMLrHE_-LpGLDHkfetQoJk56j9Fflg-P68xVgfNwZ4EBgGEJ88bXLRv1aDQ | ||
``` | ||
|
||
Upon decoding, we can view the JWT as: | ||
|
||
```json | ||
{ | ||
"alg": "EdDSA", | ||
"kid": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k#z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k", | ||
"typ": "JWT" | ||
} | ||
``` | ||
|
||
```json | ||
{ | ||
"iat": 1691175624, | ||
"iss": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k", | ||
"jti": "b44924ea-0202-4e9e-bb2a-989bd05d57ae", | ||
"nbf": 1691175624, | ||
"nonce": "5c6aad76-e2ef-4cb7-b1a8-2726b24a3f4e", | ||
"vp": { | ||
"@context": ["https://www.w3.org/2018/credentials/v1"], | ||
"type": ["VerifiablePresentation"], | ||
"verifiableCredential": [ | ||
"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrI3o2TWttTjEyOTZ1YXBIbU02QTI4bkdaR2RBRW5pRDFhYTVSZEZDbjhKRXVucVY5ayIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTExNzU2MjQsImlzcyI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrIiwianRpIjoiODU2NzQzYTEtYjllYi00NzgyLWI3NDctNTlkNzI3YTFiYWY1IiwibmJmIjoxNjkxMTc1NjI0LCJub25jZSI6Ijk2NDdjYmNkLWI4ZDgtNGUxMy1iMDJkLTEzYzU1NmQ5YjE1OCIsInN1YiI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJlbXBsb3llciI6IlRCRCIsImpvYlRpdGxlIjoiVHV0b3JpYWwgQXV0aG9yIn19fQ.XBOn0SwdYeC07gG3UdOVKxuvaz_YTiFCfgkieraeFgVA-OkdX35IizOCauKjuilArsfIo2CX7ZXh9wv4aQvEDg" | ||
] | ||
} | ||
} | ||
``` | ||
|
||
We can also decode the JWT VC value within the `verifiableCredential` property (the one that ends with `v4aQvEDg`) as: | ||
|
||
```json | ||
{ | ||
"alg": "EdDSA", | ||
"kid": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k#z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k", | ||
"typ": "JWT" | ||
} | ||
``` | ||
|
||
```json | ||
{ | ||
"iat": 1691175624, | ||
"iss": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k", | ||
"jti": "856743a1-b9eb-4782-b747-59d727a1baf5", | ||
"nbf": 1691175624, | ||
"nonce": "9647cbcd-b8d8-4e13-b02d-13c556d9b158", | ||
"sub": "did:key:z6MkmN1296uapHmM6A28nGZGdAEniD1aa5RdFCn8JEunqV9k", | ||
"vc": { | ||
"@context": [ | ||
"https://www.w3.org/2018/credentials/v1" | ||
], | ||
"type": [ | ||
"VerifiableCredential" | ||
], | ||
"credentialSubject": { | ||
"employer": "TBD", | ||
"jobTitle": "Tutorial Author" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Verifying a Presentation | ||
|
||
Once we have a Verifiable Presentation we can use the service to verify it by making a `PUT` request to `/v1/presentations/verification`. A sample request is as follows: | ||
|
||
```bash | ||
curl -X PUT localhost:3000/v1/presentations/verification -d '{ | ||
"presentationJwt": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrI3o2TWttTjEyOTZ1YXBIbU02QTI4bkdaR2RBRW5pRDFhYTVSZEZDbjhKRXVucVY5ayIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTExNzU2MjQsImlzcyI6ImRpZDprZXk6ejZNa21OMTI5NnVhcEhtTTZBMjhuR1pHZEFFbmlEMWFhNVJkRkNuOEpFdW5xVjlrIiwianRpIjoiYjQ0OTI0ZWEtMDIwMi00ZTllLWJiMmEtOTg5YmQwNWQ1N2FlIiwibmJmIjoxNjkxMTc1NjI0LCJub25jZSI6IjVjNmFhZDc2LWUyZWYtNGNiNy1iMWE4LTI3MjZiMjRhM2Y0ZSIsInZwIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJ2ZXJpZmlhYmxlQ3JlZGVudGlhbCI6WyJleUpoYkdjaU9pSkZaRVJUUVNJc0ltdHBaQ0k2SW1ScFpEcHJaWGs2ZWpaTmEyMU9NVEk1Tm5WaGNFaHRUVFpCTWpodVIxcEhaRUZGYm1sRU1XRmhOVkprUmtOdU9FcEZkVzV4VmpsckkzbzJUV3R0VGpFeU9UWjFZWEJJYlUwMlFUSTRia2RhUjJSQlJXNXBSREZoWVRWU1pFWkRiamhLUlhWdWNWWTVheUlzSW5SNWNDSTZJa3BYVkNKOS5leUpwWVhRaU9qRTJPVEV4TnpVMk1qUXNJbWx6Y3lJNkltUnBaRHByWlhrNmVqWk5hMjFPTVRJNU5uVmhjRWh0VFRaQk1qaHVSMXBIWkVGRmJtbEVNV0ZoTlZKa1JrTnVPRXBGZFc1eFZqbHJJaXdpYW5ScElqb2lPRFUyTnpRellURXRZamxsWWkwME56Z3lMV0kzTkRjdE5UbGtOekkzWVRGaVlXWTFJaXdpYm1KbUlqb3hOamt4TVRjMU5qSTBMQ0p1YjI1alpTSTZJamsyTkRkalltTmtMV0k0WkRndE5HVXhNeTFpTURKa0xURXpZelUxTm1RNVlqRTFPQ0lzSW5OMVlpSTZJbVJwWkRwclpYazZlalpOYTIxT01USTVOblZoY0VodFRUWkJNamh1UjFwSFpFRkZibWxFTVdGaE5WSmtSa051T0VwRmRXNXhWamxySWl3aWRtTWlPbnNpUUdOdmJuUmxlSFFpT2xzaWFIUjBjSE02THk5M2QzY3Vkek11YjNKbkx6SXdNVGd2WTNKbFpHVnVkR2xoYkhNdmRqRWlYU3dpZEhsd1pTSTZXeUpXWlhKcFptbGhZbXhsUTNKbFpHVnVkR2xoYkNKZExDSmpjbVZrWlc1MGFXRnNVM1ZpYW1WamRDSTZleUpsYlhCc2IzbGxjaUk2SWxSQ1JDSXNJbXB2WWxScGRHeGxJam9pVkhWMGIzSnBZV3dnUVhWMGFHOXlJbjE5ZlEuWEJPbjBTd2RZZUMwN2dHM1VkT1ZLeHV2YXpfWVRpRkNmZ2tpZXJhZUZnVkEtT2tkWDM1SWl6T0NhdUtqdWlsQXJzZklvMkNYN1pYaDl3djRhUXZFRGciXX19.pJXQXSJcu4U752IE0IH21Yw26OsGMLrHE_-LpGLDHkfetQoJk56j9Fflg-P68xVgfNwZ4EBgGEJ88bXLRv1aDQ" | ||
}' | ||
``` | ||
|
||
Upon success we see a response such as: | ||
|
||
```json | ||
{ | ||
"verified": true | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package howto | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/TBD54566975/ssi-sdk/credential" | ||
"github.com/TBD54566975/ssi-sdk/credential/integrity" | ||
"github.com/TBD54566975/ssi-sdk/crypto" | ||
"github.com/TBD54566975/ssi-sdk/crypto/jwx" | ||
"github.com/TBD54566975/ssi-sdk/did/key" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestVerifiablePresentation(t *testing.T) { | ||
// create a new DID to use to self-issue a credential and present it to a verifier | ||
privateKey, didKey, err := key.GenerateDIDKey(crypto.Ed25519) | ||
require.NoError(t, err) | ||
|
||
// expand the DID Document to access the key ID | ||
didKeyDocument, err := didKey.Expand() | ||
require.NoError(t, err) | ||
didKeyID := didKeyDocument.VerificationMethod[0].ID | ||
|
||
// first create a credential we wish to present | ||
credBuilder := credential.NewVerifiableCredentialBuilder() | ||
|
||
// set our DID as the issuer of the credential, since it will be self-signed | ||
err = credBuilder.SetIssuer(didKey.String()) | ||
require.NoError(t, err) | ||
|
||
// set the issuance date as the current time | ||
err = credBuilder.SetIssuanceDate(time.Now().Format(time.RFC3339)) | ||
require.NoError(t, err) | ||
|
||
// add the data to the body of the credential | ||
err = credBuilder.SetCredentialSubject( | ||
map[string]any{ | ||
// set the ID property of the credential subject to our DID, since the credential is about us | ||
credential.VerifiableCredentialIDProperty: didKey.String(), | ||
"jobTitle": "Tutorial Author", | ||
"employer": "TBD", | ||
}, | ||
) | ||
require.NoError(t, err) | ||
|
||
// build the credential | ||
unsignedCredential, err := credBuilder.Build() | ||
require.NoError(t, err) | ||
|
||
// sign the credential using our DID's private key | ||
signer, err := jwx.NewJWXSigner(didKey.String(), didKeyID, privateKey) | ||
require.NoError(t, err) | ||
|
||
// sign as a JWT | ||
signedCredentialBytes, err := integrity.SignVerifiableCredentialJWT(*signer, *unsignedCredential) | ||
require.NoError(t, err) | ||
signedCredentialJWTString := string(signedCredentialBytes) | ||
|
||
// construct a Verifiable Presentation for the credential | ||
presBuilder := credential.NewVerifiablePresentationBuilder() | ||
err = presBuilder.SetHolder(didKey.String()) | ||
require.NoError(t, err) | ||
err = presBuilder.AddVerifiableCredentials(signedCredentialJWTString) | ||
require.NoError(t, err) | ||
|
||
// build the presentation | ||
unsignedPresentation, err := presBuilder.Build() | ||
require.NoError(t, err) | ||
|
||
// sign the presentation using our DID's private key, passing no additional parameters | ||
signedPresentationBytes, err := integrity.SignVerifiablePresentationJWT(*signer, nil, *unsignedPresentation) | ||
require.NoError(t, err) | ||
signedPresentationJWTString := string(signedPresentationBytes) | ||
|
||
// print the signed presentation JWT | ||
println(signedPresentationJWTString) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.