diff --git a/doc/howto/credential.md b/doc/howto/credential.md index e69de29bb..53564c6f2 100644 --- a/doc/howto/credential.md +++ b/doc/howto/credential.md @@ -0,0 +1,123 @@ +# How To: Create a Credential + +## Background + +A [Verifiable Credential (VC)](https://www.w3.org/TR/vc-data-model/) is a standard format to package a set of claims that an _issuer_ makes about a _subject_. The Verifiable Credentials Data Model, a W3C standard, introduces a number of concepts, most notable among them, the [three party model](https://www.w3.org/TR/vc-data-model/#ecosystem-overview) of **issuers**, **holders**, and **verifiers**. The model is a novel way of empowering entities to have tamper-evident representations of their data which acts as a mechanism to present the data to any third party (a verifier) without necessitating contact between the verifier and issuer. With the three party model entities are given more [control, transparency, privacy, and utility](https://www.lifewithalacrity.com/2016/04/the-path-to-self-soverereign-identity.html) for data that is rightfully theirs. + +VCs are defined by a data model, which does not provide guidance on transmitting or sharing credentials between parties (protocols). The data model also does not provide guidance on _securing_ the credential (which puts the verifiable in verifiable credential). There are two prominent options here: [Data Integrity](https://www.w3.org/TR/vc-data-integrity/) and [JOSE/COSE](https://www.w3.org/TR/vc-jose-cose/) both of which we have demonstrated support for. The data model has a number of required (core) properties, and multiple means of extension to meet many use cases and functional needs. + +## VCs in the SSI Service + +VCs are a core component of SSI (Self Sovereign Identity) systems and work hand-in-hand with [DIDs](did.md). In our system, DIDs are used to represent the _issuer_ as well as the _subject_ of a credential. To define which content is in a VC we make use of [JSON schemas](schema.md). + +The SSI Service is transport-agnostic and does not mandate the usage of a single mechanism to deliver credentials to an intended holder. We have begun integration with both [Web5](https://github.com/TBD54566975/dwn-sdk-js#readme) and [OpenID Connect](https://openid.net/sg/openid4vc/) transportation mechanisms but leave the door open to any number of possibile options. + +At present, the service supports issuing credentials using the [v1.1 data model as a JWT](https://www.w3.org/TR/vc-data-model/#json-web-token). There is support for verifying credentials that make use of select [Data Integrity cryptographic suites](https://w3c.github.io/vc-data-integrity/) though use is discouraged due to complexity and potential security risks. Support for [v2.0](https://w3c.github.io/vc-data-model/) of the data model is planned and coming soon! + +Out of the box we have support for exposing two [credential statuses](status.md) using the [Verifiable Credentials Status List](https://w3c.github.io/vc-status-list-2021/) specification: suspension and revocation. + +## Creating a Verifiable Credential + +Creating a credential using the SSI Service currently requires four pieces of data: an `issuer` DID, a `verificationMethodId` which must be contained within the issuer's DID Document, a `subject` DID (the DID who the claims are about), and `data` which is an arbitrary JSON object for the claims that you wish to be in the credential (in the `credentialSubject` property). + +There are additional optional properties that let you specify `evidence`, an `expiry`, and whether you want to make the credential `revocable` or `suspendable`. Let's keep things simple and issue our first credential which will attest to a person's first and last name. + +### 1. Create an issuer DID + +We need two pieces of issuer information to create the credential: their DID and a verification method identifier. To get both, let's create a `did:key` to act as the issuer, with the following `PUT` command to `/v1/dids/key`. + +```bash +curl -X PUT localhost:3000/v1/dids/key -d '{"keyType": "Ed25519"}' +``` + +We get back a response with the `id` as `did:key:z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD`. Since we're using DID key we know that there is only a single key and its' `verificationMethodId` is the DID suffix: `did:key:z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD#z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD`. This value could be retrieved by resolving the DID or by making a `GET` request to `/v1/dids/key/did:key:z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD` as well. + +### 2. Create a person schema + +Because we want to include information about the subject in the credential, let's first create a schema to define the shape of the credential's data with required `firstName` and `lastName` values. While this step is optional, it's a good practice to have a schema that describes the shape of the data. + +Once we have our schema, we'll submit it to the service with a `PUT` request to `v1/schemas` as follows: + +```bash +curl -X PUT localhost:3000/v1/schemas -d '{ + "name": "Person Credential", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "credentialSubject": { + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + } + }, + "required": ["firstName", "lastName"] + } + } + } +}' +``` + +After submission we get back an identifier to refer to the schema as `aed6f4f0-5ed7-4d7a-a3df-56430e1b2a88`. + +### 3. Create a credential + +Separately, we've figured out that the subject we're creating the credential for has the DID `did:key:z6MkmNnvnfzW3nLiePweN3niGLnvp2BjKx3NM186vJ2yRg2z`. Now we have all the set up done we're ready to create our credential. + +Construct a `PUT` request to `/v1/credentials` as follows: + +```bash +curl -X PUT localhost:3000/v1/credentials -d '{ + "issuer": "did:key:z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD", + "verificationMethodId": "did:key:z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD#z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD", + "subject": "did:key:z6MkmNnvnfzW3nLiePweN3niGLnvp2BjKx3NM186vJ2yRg2z", + "schemaId": "aed6f4f0-5ed7-4d7a-a3df-56430e1b2a88", + "data": { + "firstName": "Satoshi", + "lastName": "Nakamoto" + } +}' +``` + +Upon success we'll see a response such as: + +```json +{ + "id": "46bc3d25-6aaf-4f50-99ed-61c4b35f6411", + "fullyQualifiedVerificationMethodId": "did:key:z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD#z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD", + "credential": { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "http://localhost:3000/v1/credentials/46bc3d25-6aaf-4f50-99ed-61c4b35f6411", + "type": ["VerifiableCredential"], + "issuer": "did:key:z6Mkm1TmRWRPK6n21QncUZnk1tdYkje896mYCzhMfQ67assD", + "issuanceDate": "2023-07-28T12:45:15-07:00", + "credentialSubject": { + "id": "did:key:z6MkmNnvnfzW3nLiePweN3niGLnvp2BjKx3NM186vJ2yRg2z", + "firstName": "Satoshi", + "lastName": "Nakamoto" + }, + "credentialSchema": { + "id": "aed6f4f0-5ed7-4d7a-a3df-56430e1b2a88", + "type": "JsonSchema2023" + } + }, + "credentialJwt": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa20xVG1SV1JQSzZuMjFRbmNVWm5rMXRkWWtqZTg5Nm1ZQ3poTWZRNjdhc3NEI3o2TWttMVRtUldSUEs2bjIxUW5jVVpuazF0ZFlramU4OTZtWUN6aE1mUTY3YXNzRCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTA1NzM1MTUsImlzcyI6ImRpZDprZXk6ejZNa20xVG1SV1JQSzZuMjFRbmNVWm5rMXRkWWtqZTg5Nm1ZQ3poTWZRNjdhc3NEIiwianRpIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwL3YxL2NyZWRlbnRpYWxzLzQ2YmMzZDI1LTZhYWYtNGY1MC05OWVkLTYxYzRiMzVmNjQxMSIsIm5iZiI6MTY5MDU3MzUxNSwibm9uY2UiOiIzMGMwNDYxZi1jMWUxLTQwNDctYWUwYS01NjgzMjdkMzY4YTYiLCJzdWIiOiJkaWQ6a2V5Ono2TWttTm52bmZ6VzNuTGllUHdlTjNuaUdMbnZwMkJqS3gzTk0xODZ2SjJ5UmcyeiIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZmlyc3ROYW1lIjoiU2F0b3NoaSIsImxhc3ROYW1lIjoiTmFrYW1vdG8ifSwiY3JlZGVudGlhbFNjaGVtYSI6eyJpZCI6ImFlZDZmNGYwLTVlZDctNGQ3YS1hM2RmLTU2NDMwZTFiMmE4OCIsInR5cGUiOiJKc29uU2NoZW1hMjAyMyJ9fX0.xwqpDuO6PDeEqYr6DflbeR6mhuwvVg0uR43i-7Zhy2DdaH1e3Jt4DuiMy09tZQ2jAXki0rjMNgLt7dPpzOl8BA" +} +``` + +In the `credential` property we see an unsecured, but readable, version of the VC. The VC is signed and packaged as a JWT in the `credentialJwt` property. If you're interested, you can decode the JWT using a tool such as [jwt.io](https://jwt.io/). If you were to 'issue' or transmit the credential to a _holder_ you would just send this JWT value. + +## Getting Credentials + +Once you've created multiple credentials, you can view all credentials by making a `GET` request to `/v1/credentials`. This endpoint also supports three query parameters: `issuer`, `schema`, and `subject` which can be used mutually exclusively. + +You can get a single credential by making a `GET` request to `/v1/credentials/{id}`. + +## Other Credential Operations + +To learn about verifying credentials [read more here](verification.md). You can also learn more about [credential status here](status.md). + diff --git a/doc/howto/did.md b/doc/howto/did.md index 414134c25..d3b6fbf30 100644 --- a/doc/howto/did.md +++ b/doc/howto/did.md @@ -33,7 +33,7 @@ Upon a successful request you should see a response such as: } ``` -## Creating A DID +## Creating a DID You can create a DID by sending a `PUT` request to the `/v1/dids/{method}` endpoint. The request body needs two pieces of information: a method and a key type. The method must be supported by the service, and the key type must be supported by the method. You can find out more specifics about what each method supports [by looking at the SDK](https://github.com/TBD54566975/ssi-sdk/tree/main/did). Certain methods may support additional properties in an optional `options` fields. @@ -41,12 +41,10 @@ For now let's keep things simple and create a new `did:key` with the key type [` **Create DID Key Request** -`PUT` to `/v1/dids/key` +Make a `PUT` request to `/v1/dids/key`. A sample CURL command is as follows: -```json -{ - "keyType": "Ed25519" -} +```bash +curl -X PUT localhost:3000/v1/dids/key -d '{"keyType": "Ed25519"}' ``` If successful, you should see a response such as... @@ -117,6 +115,3 @@ You can get a specific DID's document by making a `GET` request to the method's ## DIDs Outside the Service The [universal resolver](https://github.com/decentralized-identity/universal-resolver) is a project at the [Decentralized Identity Foundation](https://identity.foundation/) aiming to enable the resolution of _any_ DID Document. The service, when run with [Docker Compose, runs a select number of these drivers (and more can be configured). It's possible to leverage the resolution of DIDs not supported by the service by making `GET` requests to `/v1/dids/resolver/{did}`. - - - diff --git a/doc/howto/schema.md b/doc/howto/schema.md index e69de29bb..45999ce78 100644 --- a/doc/howto/schema.md +++ b/doc/howto/schema.md @@ -0,0 +1,123 @@ +# How To: Create a Schema + +## Background + +When creating [Verifiable Credentials](https://www.w3.org/TR/vc-data-model) it's useful to have a mechanism to define the shape the data in the credential takes, in a consistent manner. The VC Data Model uses an open world data model, and with it, provides a mechanism to "extend" the core terminology to add any term with a technology known as [JSON-LD](https://json-ld.org/). JSON-LD is responsible for the `@context` property visible in VCs, DIDs, and other documents in the SSI space. However, JSON-LD is focused on _semantics_, answering the question "do we have a shared understanding of what this thing is?" more specifically, for a name credential, does your concept of "name" match mine. Though the core data model is a JSON-LD data model, processing VCs as JSON-LD is not a requirement. The SSI Service chooses to take a simpler approach and [process VCs as pure JSON](https://www.w3.org/TR/vc-data-model/#json). + +When constructing and processing VCs as pure JSON it is useful to have a mechanism to define the data and add some light validation onto the shape that data takes. [JSON Schema](https://json-schema.org/) is a widely used, and widely supported toolset that enables such functionalty: the ability to define a schema, which provides a set of properties (both required and optional), and some light validation on top of those properties. The VC Data Model has [a section on data schemas](https://www.w3.org/TR/vc-data-model/#data-schemas) that enables this functionality. + +## Intro to JSON Schema with Verifiable Credentials + +Making use of the `credentialSchema` property [defined in the VC Data Model](https://www.w3.org/TR/vc-data-model/#data-schemas) TBD and other collaborators in the W3C are working on [a new specification](https://w3c.github.io/vc-json-schema/) which enables a standards-compliant path to using JSON Schema with Verifiable Credentials. The VC JSON Schema specification defines two options for using JSON Schemas: the first, a plan JSON Schema that can apply to _any set of properties_ in a VC, and the second, a Verifiable Credential that wraps a JSON Schema. + +In some cases it is useful to package a JSON Schema as a Verifiable Credential to retain information about authorship (who created the schema), when it was created, and enable other features the VC Data Model offers, such as the ability to suspend the usage of a schema with [a status](https://www.w3.org/TR/vc-data-model/#status). + +An example email JSON Schema using [JSON Schema Draft 2020-12](https://json-schema.org/draft/2020-12/json-schema-core.html) is provided below: + +```json +{ + "$id": "https://example.com/schemas/email.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "name": "Email Address", + "type": "object", + "properties": { + "emailAddress": { + "type": "string", + "format": "email" + } + }, + "required": ["emailAddress"] +} +``` + +We can see that the schema defines a property `emailAddress` of JSON type `string`, and it is required. This means that any piece of JSON we apply this schema to will pass if a valid `emailAddress` property is present and fail otherwise. + +Now that we have a valid JSON Schema, we'll need to transform it so it's useful in being applied to a Verifiable Credential, not just any arbitrary JSON. We know that we want the presence of an `emailAddress` in the `credentialSubject` property of a VC and adjust the schema accordingly: + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "name": "Email Credential", + "type": "object", + "properties": { + "credentialSubject": { + "type": "object", + "properties": { + "emailAddress": { + "type": "string", + "format": "email" + } + }, + "required": ["emailAddress"] + } + } +} +``` + +Now our schema, applied to a Verifiable Credential, will guarantee that the `credentialSubject` property contains a valid `emailAddress` property. + +## Creating a Schema + +The service exposes a set of APIs for managing schemas. To create a schema you have two options: signed or not. As mentioned earlier, the signed version of a schema is packaged as a Verifiable Credential. To create a signed schema you'll need to pass in two additional properties – the issuer DID and the ID of the verification method to use to sign the schema. We'll keep things simple for now and create an unsigned schema. + +After forming a valid JSON Schema, generate a `PUT` request to `/v1/schemas` as follows: + +```bash +curl -X PUT localhost:3000/v1/schemas -d '{ + "name": "Email Credential", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "credentialSubject": { + "type": "object", + "properties": { + "emailAddress": { + "type": "string", + "format": "email" + } + }, + "required": ["emailAddress"] + } + } + } +}' +``` + +Upon success you'll see a response which includes the schema you passed in, with a service-generated identifier for the schema. You'll also notice a type `JsonSchema2023`, which is defined by the [VC JSON Schema specification](https://w3c.github.io/vc-json-schema/#jsonschema2023): + +```json +{ + "id": "ebeebf7b-d452-4832-b8d3-0042ec80e108", + "type": "JsonSchema2023", + "schema": { + "$id": "http://localhost:3000/v1/schemas/ebeebf7b-d452-4832-b8d3-0042ec80e108", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "name": "Email Credential", + "properties": { + "credentialSubject": { + "properties": { + "emailAddress": { + "format": "email", + "type": "string" + } + }, + "required": [ + "emailAddress" + ], + "type": "object" + } + }, + "type": "object" + } +} +``` + +Now you're ready to use the schema in [creating a credential](credential.md). + +## Getting Schemas + +Once you've created multiple schemas, you can view them all by make a `GET` request to the `v1/schemas` endpoint. Future enhancements may enable filtering based on name, author, or other properties. + +You can get a specific schema by make a `GET` request to the `v1/schemas/{schemaId}` endpoint. + diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 9b2d81608..c4f4282b9 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -2788,7 +2788,7 @@ paths: type: string summary: Batch Create DIDs tags: - - CredentialAPI + - DecentralizedIdentityAPI /v1/dids/resolver/{id}: get: consumes: diff --git a/pkg/server/router/did.go b/pkg/server/router/did.go index d07386ce9..352bc9955 100644 --- a/pkg/server/router/did.go +++ b/pkg/server/router/did.go @@ -11,6 +11,7 @@ import ( "github.com/gin-gonic/gin" "github.com/goccy/go-json" "github.com/pkg/errors" + "github.com/tbd54566975/ssi-service/internal/util" "github.com/tbd54566975/ssi-service/pkg/server/framework" "github.com/tbd54566975/ssi-service/pkg/server/pagination" @@ -408,7 +409,7 @@ func NewBatchDIDRouter(svc *did.BatchService) *BatchDIDRouter { // @Summary Batch Create DIDs // @Description Create a batch of verifiable credentials. The operation is atomic, meaning that all requests will // @Description succeed or fail. This is currently only supported for the DID method named `did:key`. -// @Tags CredentialAPI +// @Tags DecentralizedIdentityAPI // @Accept json // @Produce json // @Param method path string true "Method. Only `key` is supported." diff --git a/pkg/service/schema/service.go b/pkg/service/schema/service.go index 4ab277158..57b562991 100644 --- a/pkg/service/schema/service.go +++ b/pkg/service/schema/service.go @@ -164,8 +164,6 @@ func (s Service) createCredentialSchema(ctx context.Context, jsonSchema schema.J // set subject value as the schema subject := credential.CredentialSubject(jsonSchema) - // TODO(gabe) remove this after https://github.com/TBD54566975/ssi-sdk/pull/404 is merged - subject[credential.VerifiableCredentialIDProperty] = schemaURI if err := builder.SetCredentialSubject(subject); err != nil { return nil, sdkutil.LoggingErrorMsgf(err, "could not set subject: %+v", subject) }