diff --git a/.mega-linter.yml b/.mega-linter.yml index e6c677ca01..cd37dd35f6 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -14,7 +14,13 @@ DISABLE_LINTERS: SCALA_SCALAFIX, SQL_TSQLLINT, ] -DISABLE_ERRORS_LINTERS: [KOTLIN_KTLINT, PROTOBUF_PROTOLINT, OPENAPI_SPECTRAL, MARKDOWN_MARKDOWN_LINK_CHECK] +DISABLE_ERRORS_LINTERS: + [ + KOTLIN_KTLINT, + PROTOBUF_PROTOLINT, + OPENAPI_SPECTRAL, + MARKDOWN_MARKDOWN_LINK_CHECK, + ] DISABLE: [COPYPASTE, SPELL, CREDENTIALS] SHOW_ELAPSED_TIME: false diff --git a/.yamllint.yml b/.yamllint.yml index e0ba390f35..fd8e1418e2 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -1,4 +1,3 @@ ---- ########################################### # These are the rules used for # # linting all the yaml files in the stack # @@ -15,3 +14,5 @@ rules: max: 600 comments: min-spaces-from-content: 1 # Used to follow prettier standard: https://github.com/prettier/prettier/pull/10926 + level: warning + document-start: disable diff --git a/build.sbt b/build.sbt index e7d594461e..79610e3265 100644 --- a/build.sbt +++ b/build.sbt @@ -683,12 +683,7 @@ lazy val prismAgentServer = project Compile / sourceGenerators += openApiGenerateClasses, openApiGeneratorSpec := baseDirectory.value / ".." / "api" / "http/prism-agent-openapi-spec.yaml", openApiGeneratorConfig := baseDirectory.value / "openapi/generator-config/config.yaml", - openApiGeneratorImportMapping := Seq( - "DIDDocument", - "DIDResolutionResult" - ) - .map(model => (model, s"io.iohk.atala.agent.server.http.model.OASModelPatches.$model")) - .toMap, + openApiGeneratorImportMapping := Map.empty, Docker / maintainer := "atala-coredid@iohk.io", Docker / dockerUsername := Some("input-output-hk"), Docker / dockerRepository := Some("ghcr.io"), diff --git a/infrastructure/shared/apisix/conf/apisix.yaml b/infrastructure/shared/apisix/conf/apisix.yaml index 35fbff642d..9debaa0b6c 100644 --- a/infrastructure/shared/apisix/conf/apisix.yaml +++ b/infrastructure/shared/apisix/conf/apisix.yaml @@ -12,6 +12,11 @@ routes: plugins: proxy-rewrite: regex_uri: ["^/prism-agent/(.*)", "/$1"] + - uri: /prism-agent/dids* + upstream_id: 4 + plugins: + proxy-rewrite: + regex_uri: ["^/prism-agent/dids(/.*)?", "/dids$1"] - uri: /prism-agent/connections* upstream_id: 4 plugins: @@ -21,7 +26,7 @@ routes: upstream_id: 4 plugins: proxy-rewrite: - regex_uri: ["^/prism-agent/connection-invitations", "/connection-invitations"] + uri: "/connection-invitations" - uri: /didcomm upstream_id: 3 plugins: @@ -66,15 +71,15 @@ upstreams: type: roundrobin - id: 2 nodes: - "prism-agent:8080": 1 #api + "prism-agent:8080": 1 # api type: roundrobin - id: 3 nodes: - "prism-agent:8090": 1 #didcom + "prism-agent:8090": 1 # didcom type: roundrobin - id: 4 nodes: - "prism-agent:8085": 1 #tapir + "prism-agent:8085": 1 # tapir type: roundrobin - id: 5 nodes: @@ -82,6 +87,6 @@ upstreams: type: roundrobin - id: 6 nodes: - "prism-agent:8082": 1 #_system + "prism-agent:8082": 1 # _system type: roundrobin #END diff --git a/prism-agent/service/api/http/castor/parameters.yaml b/prism-agent/service/api/http/castor/parameters.yaml index 1e2db9e619..7394b4e22a 100644 --- a/prism-agent/service/api/http/castor/parameters.yaml +++ b/prism-agent/service/api/http/castor/parameters.yaml @@ -9,21 +9,3 @@ components: schema: type: string example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff - resolutionAcceptOption: - in: header - name: Accept - required: false - description: | - Represents [accept resolution option](https://www.w3.org/TR/did-core/#did-resolution-options). - - For [DID resolution result](https://w3c-ccg.github.io/did-resolution/#did-resolution-result), `application/ld+json;profile="https://w3id.org/did-resolution"` can be used. - - For [DID document representation](https://www.w3.org/TR/did-core/#representations), `application/` can be used. - - Default to `application/ld+json;profile="https://w3id.org/did-resolution"` if not specified. - schema: - type: string - enum: - - application/did+ld+json - - application/ld+json;profile="https://w3id.org/did-resolution" - example: application/ld+json;profile="https://w3id.org/did-resolution" diff --git a/prism-agent/service/api/http/castor/schemas.yaml b/prism-agent/service/api/http/castor/schemas.yaml index d868d49f1f..952b582545 100644 --- a/prism-agent/service/api/http/castor/schemas.yaml +++ b/prism-agent/service/api/http/castor/schemas.yaml @@ -8,138 +8,6 @@ components: scheduledOperation: $ref: "#/components/schemas/DidOperationSubmission" - DIDResolutionResult: - type: object - required: - - "@context" - - didDocumentMetadata - - didResolutionMetadata - properties: - "@context": - type: string - example: "https://w3id.org/did-resolution/v1" - didDocument: - $ref: "#/components/schemas/DIDDocument" - didDocumentMetadata: - $ref: "#/components/schemas/DIDDocumentMetadata" - didResolutionMetadata: - $ref: "#/components/schemas/DIDResolutionMetadata" - - DIDDocument: - type: object - description: A W3C compliant Prism DID document representation. - required: - - "@context" - - id - properties: - "@context": - type: array - items: - type: string - example: "https://www.w3.org/ns/did/v1" - id: - type: string - description: | - [DID subject](https://www.w3.org/TR/did-core/#did-subject). - The value must match the DID that was given to the resolver. - example: "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" - controller: - type: string - description: | - [DID controller](https://www.w3.org/TR/did-core/#did-controller) - example: "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" - verificationMethod: - type: array - items: - $ref: "#/components/schemas/VerificationMethod" - authentication: - type: array - items: - $ref: "#/components/schemas/VerificationMethodRef" - assertionMethod: - type: array - items: - $ref: "#/components/schemas/VerificationMethodRef" - keyAgreement: - type: array - items: - $ref: "#/components/schemas/VerificationMethodRef" - capabilityInvocation: - type: array - items: - $ref: "#/components/schemas/VerificationMethodRef" - capabilityDelegation: - type: array - items: - $ref: "#/components/schemas/VerificationMethodRef" - service: - type: array - items: - $ref: "#/components/schemas/Service" - - DIDDocumentMetadata: - type: object - description: | - [DID document metadata](https://www.w3.org/TR/did-core/#did-document-metadata) - properties: - deactivated: - type: boolean - description: If a DID has been deactivated, DID document metadata MUST include this property with the boolean value true. If a DID has not been deactivated, this property is OPTIONAL, but if included, MUST have the boolean value false. - canonicalId: - type: string - description: | - A DID in canonical form. - - If a DID is in long form and has been published, DID document metadata MUST contain a `canonicalId`` property with the short form DID as its value. - - If a DID in short form or has not been published, DID document metadata MUST NOT contain a `canonicalId` property. - - DIDResolutionMetadata: - type: object - description: | - [DID resolution metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata) - properties: - error: - type: string - description: Resolution error constant according to [DID spec registries](https://www.w3.org/TR/did-spec-registries/#error) - example: invalidDid - errorMessage: - type: string - description: Resolution error message - example: The initialState does not match the suffix - contentType: - type: string - description: The media type of the returned DID document - example: application/did+ld+json - - VerificationMethod: - type: object - description: | - A cryptographic public key expressed in the DID document. - https://www.w3.org/TR/did-core/#verification-methods - required: - - id - - type - - controller - - publicKeyJwk - properties: - id: - type: string - example: "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1" - type: - type: string - example: "EcdsaSecp256k1VerificationKey2019" - controller: - type: string - example: "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" - publicKeyJwk: - $ref: "#/components/schemas/PublicKeyJwk" - - VerificationMethodRef: - type: string - description: A reference to a `verificationMethod` - example: did:prism:c7bd808e8e135236d7262ecf5e639b8f9d22bd886f59a4e6c909486846ca8319#key-1 - CreateManagedDidRequest: type: object required: @@ -159,7 +27,7 @@ components: services: type: array items: - $ref: "#/components/schemas/Service" + $ref: "../tapir-generated.yaml#/components/schemas/Service" CreateManagedDIDResponse: type: object @@ -209,7 +77,7 @@ components: description: ID of key to remove from DID document example: key-1 addService: - $ref: "#/components/schemas/Service" + $ref: "../tapir-generated.yaml#/components/schemas/Service" removeService: type: object required: @@ -274,58 +142,6 @@ components: - PUBLICATION_PENDING - PUBLISHED - PublicKeyJwk: - type: object - required: - - kty - properties: - crv: - type: string - example: "secp256k1" - x: - type: string - example: "38M1FDts7Oea7urmseiugGW7tWc3mLpJh6rKe7xINZ8" - y: - type: string - example: "nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4" - kty: - type: string - example: "EC" - - Service: - type: object - description: | - A service expressed in the DID document. - https://www.w3.org/TR/did-core/#services - required: - - id - - type - - serviceEndpoint - properties: - id: - type: string - example: "service-1" - type: - type: string - description: | - Service type. - Can contain multiple possible values as described in the [Create DID operation](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#create-did) under the construction section. - - *Known issue: This is currently misaligned with Prism DID method. This will be fixed in the future version of Prism Agent* - enum: - - LinkedDomains - example: LinkedDomains - serviceEndpoint: - type: array - description: | - Application service endpoints. - Can contain multiple possible values as described in the [Create DID operation](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#create-did) under the construction section. - - *Known issue: This is currently misaligned with Prism DID method. This will be fixed in the future version of Prism Agent* - items: - type: string - example: "https://bar.example.com/" - ManagedDIDKeyTemplate: type: object description: key-pair template to add to DID document. diff --git a/prism-agent/service/api/http/prism-agent-openapi-spec.yaml b/prism-agent/service/api/http/prism-agent-openapi-spec.yaml index f20a673708..a0772528e8 100644 --- a/prism-agent/service/api/http/prism-agent-openapi-spec.yaml +++ b/prism-agent/service/api/http/prism-agent-openapi-spec.yaml @@ -40,64 +40,7 @@ paths: # Castor # ---------------------------------- /dids/{didRef}: - get: - operationId: getDid - tags: ["DID"] - summary: Resolve Prism DID to a W3C representation - description: | - Resolve Prism DID to a W3C DID document representation. - The response can be the [DID resolution result](https://w3c-ccg.github.io/did-resolution/#did-resolution-result) - or [DID document representation](https://www.w3.org/TR/did-core/#representations) depending on the `Accept` request header. - The response is implemented according to [resolver HTTP binding](https://w3c-ccg.github.io/did-resolution/#bindings-https) in the DID resolution spec. - parameters: - - $ref: "./castor/parameters.yaml#/components/parameters/didRefInPath" - - $ref: "./castor/parameters.yaml#/components/parameters/resolutionAcceptOption" - responses: - "200": - description: The resolution result or W3C DID document representation - content: - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: "./castor/schemas.yaml#/components/schemas/DIDResolutionResult" - application/did+ld+json: - schema: - $ref: "./castor/schemas.yaml#/components/schemas/DIDDocument" - "400": - description: Invalid input - content: - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: "./castor/schemas.yaml#/components/schemas/DIDResolutionResult" - "404": - description: Not found - content: - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: "./castor/schemas.yaml#/components/schemas/DIDResolutionResult" - "406": - description: Representation not supported - content: - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: "./castor/schemas.yaml#/components/schemas/DIDResolutionResult" - "410": - description: DID Deactivated - content: - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: "./castor/schemas.yaml#/components/schemas/DIDResolutionResult" - "500": - description: Internal error - content: - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: "./castor/schemas.yaml#/components/schemas/DIDResolutionResult" - "501": - description: Method not supported - content: - application/ld+json;profile="https://w3id.org/did-resolution": - schema: - $ref: "./castor/schemas.yaml#/components/schemas/DIDResolutionResult" + $ref: "./tapir-generated.yaml#/paths/~1dids~1{didRef}" /did-registrar/dids: get: diff --git a/prism-agent/service/api/http/tapir-generated.yaml b/prism-agent/service/api/http/tapir-generated.yaml new file mode 100644 index 0000000000..4cdfa186b9 --- /dev/null +++ b/prism-agent/service/api/http/tapir-generated.yaml @@ -0,0 +1,1460 @@ +openapi: 3.0.3 +info: + title: Prism Agent + version: 1.0.0 +paths: + /schema-registry/schemas: + get: + tags: + - Schema Registry + summary: Lookup schemas by indexed fields + description: + "Lookup schemas by `author`, `name`, `tags` parameters and control + the pagination by `offset` and `limit` parameters " + operationId: lookupSchemasByQuery + parameters: + - name: author + in: query + required: false + schema: + type: string + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + - name: name + in: query + required: false + schema: + type: string + example: DrivingLicense + - name: version + in: query + required: false + schema: + type: string + example: 1.0.0 + - name: tags + in: query + required: false + schema: + type: string + example: driving + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + - name: order + in: query + required: false + schema: + type: string + responses: + "200": + description: Collection of CredentialSchema records. + content: + application/json: + schema: + $ref: "#/components/schemas/CredentialSchemaResponsePage" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + post: + tags: + - Schema Registry + summary: Publish new schema to the schema registry + description: + Create the new credential schema record with metadata and internal + JSON Schema on behalf of Cloud Agent. The credential schema will be signed + by the keys of Cloud Agent and issued by the DID that corresponds to it. + operationId: createSchema + requestBody: + description: JSON object required for the credential schema creation + content: + application/json: + schema: + $ref: "#/components/schemas/CredentialSchemaInput" + required: true + responses: + "201": + description: The new credential schema record is successfully created + content: + application/json: + schema: + $ref: "#/components/schemas/CredentialSchemaResponse" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + /schema-registry/{author}/{id}: + put: + tags: + - Schema Registry + summary: Publish the new version of the credential schema to the schema registry + description: + Publish the new version of the credential schema record with metadata + and internal JSON Schema on behalf of Cloud Agent. The credential schema will + be signed by the keys of Cloud Agent and issued by the DID that corresponds + to it. + operationId: updateSchema + parameters: + - name: author + in: path + description: + DID of the identity which authored the credential schema. A piece + of Metadata. + required: true + schema: + type: string + - name: id + in: path + description: + A locally unique identifier to address the schema. UUID is generated + by the backend. + required: true + schema: + type: string + format: uuid + requestBody: + description: JSON object required for the credential schema update + content: + application/json: + schema: + $ref: "#/components/schemas/CredentialSchemaInput" + required: true + responses: + "200": + description: The credential schema record is successfully updated + content: + application/json: + schema: + $ref: "#/components/schemas/CredentialSchemaResponse" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + /schema-registry/schemas/{guid}: + get: + tags: + - Schema Registry + summary: Fetch the schema from the registry by `guid` + description: Fetch the credential schema by the unique identifier + operationId: getSchemaById + parameters: + - name: guid + in: path + description: Globally unique identifier of the credential schema record + required: true + schema: + type: string + format: uuid + responses: + "200": + description: CredentialSchema found by `guid` + content: + application/json: + schema: + $ref: "#/components/schemas/CredentialSchemaResponse" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: Resource could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + /schema-registry/test: + get: + tags: + - Schema Registry + summary: Trace the request input from the point of view of the server + description: Trace the request input from the point of view of the server + operationId: test + responses: + "200": + description: "" + content: + application/json: + schema: + type: string + "400": + description: Invalid value + content: + text/plain: + schema: + type: string + /verification/policies: + get: + tags: + - Verification + summary: Lookup verification policies by query + description: + Lookup verification policies by `name`, and control the pagination + by `offset` and `limit` parameters + operationId: lookupVerificationPoliciesByQuery + parameters: + - name: name + in: query + required: false + schema: + type: string + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + - name: order + in: query + required: false + schema: + type: string + responses: + "200": + description: "" + content: + application/json: + schema: + $ref: "#/components/schemas/VerificationPolicyPage" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + post: + tags: + - Verification + summary: Create the new verification policy + description: Create the new verification policy + operationId: createVerificationPolicy + requestBody: + description: Create verification policy object + content: + application/json: + schema: + $ref: "#/components/schemas/VerificationPolicyInput" + required: true + responses: + "201": + description: Created verification policy entity + content: + application/json: + schema: + $ref: "#/components/schemas/VerificationPolicy" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + /verification/policies/{id}: + get: + tags: + - Verification + summary: Fetch the verification policy by id + description: Get the verification policy by id + operationId: getVerificationPolicyById + parameters: + - name: id + in: path + description: Get the verification policy by id + required: true + schema: + type: string + format: uuid + responses: + "200": + description: "" + content: + application/json: + schema: + $ref: "#/components/schemas/VerificationPolicy" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: Resource could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + put: + tags: + - Verification + summary: Update the verification policy object by id + description: Update the verification policy entry + operationId: updateVerificationPolicy + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + - name: nonce + in: query + description: Nonce of the previous VerificationPolicy + required: true + schema: + type: integer + format: int32 + requestBody: + description: Update verification policy object + content: + application/json: + schema: + $ref: "#/components/schemas/VerificationPolicyInput" + required: true + responses: + "200": + description: "" + content: + application/json: + schema: + $ref: "#/components/schemas/VerificationPolicy" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: Resource could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + delete: + tags: + - Verification + summary: Deleted the verification policy by id + description: Delete the verification policy by id + operationId: deleteVerificationPolicyById + parameters: + - name: id + in: path + description: Delete the verification policy by id + required: true + schema: + type: string + format: uuid + - name: nonce + in: query + description: Nonce of the previous VerificationPolicy + required: true + schema: + type: integer + format: int32 + responses: + "200": + description: Verification policy deleted successfully + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: Resource could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + /connections: + get: + tags: + - Connections Management + summary: Gets the list of connection records. + description: Get the list of connection records paginated + operationId: getConnections + parameters: + - name: offset + in: query + required: false + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + responses: + "200": + description: The list of connection records. + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectionsPage" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + post: + tags: + - Connections Management + summary: Creates a new connection record and returns an Out of Band invitation. + description: |2 + + Generates a new Peer DID and creates an [Out of Band 2.0](https://identity.foundation/didcomm-messaging/spec/v2.0/#out-of-band-messages) invitation. + It returns a new connection record in `InvitationGenerated` state. + The request body may contain a `label` that can be used as a human readable alias for the connection, for example `{'label': "Bob"}` + operationId: createConnection + requestBody: + description: JSON object required for the connection creation + content: + application/json: + schema: + $ref: "#/components/schemas/CreateConnectionRequest" + required: true + responses: + "201": + description: + The connection record was created successfully, and is returned + in the response body. + content: + application/json: + schema: + $ref: "#/components/schemas/Connection" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + /connections/{connectionId}: + get: + tags: + - Connections Management + summary: Gets an existing connection record by its unique identifier. + description: Gets an existing connection record by its unique identifier + operationId: getConnection + parameters: + - name: connectionId + in: path + description: The unique identifier of the connection record. + required: true + schema: + type: string + format: uuid + responses: + "200": + description: The connection record. + content: + application/json: + schema: + $ref: "#/components/schemas/Connection" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: Resource could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + /connection-invitations: + post: + tags: + - Connections Management + summary: Accepts an Out of Band invitation. + description: |2 + + Accepts an [Out of Band 2.0](https://identity.foundation/didcomm-messaging/spec/v2.0/#out-of-band-messages) invitation, generates a new Peer DID, + and submits a Connection Request to the inviter. + It returns a connection object in `ConnectionRequestPending` state, until the Connection Request is eventually sent to the inviter by the prism-agent's background process. The connection object state will then automatically move to `ConnectionRequestSent`. + operationId: acceptConnectionInvitation + requestBody: + description: + The request used by an invitee to accept a connection invitation + received from an inviter, using out-of-band mechanism. + content: + application/json: + schema: + $ref: "#/components/schemas/AcceptConnectionInvitationRequest" + required: true + responses: + "200": + description: The invitation was successfully accepted. + content: + application/json: + schema: + $ref: "#/components/schemas/Connection" + "400": + description: Invalid request parameters + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + /dids/{didRef}: + get: + tags: + - DID + summary: Resolve Prism DID to a W3C representation + description: | + Resolve Prism DID to a W3C DID document representation. + The response can be the [DID resolution result](https://w3c-ccg.github.io/did-resolution/#did-resolution-result) + or [DID document representation](https://www.w3.org/TR/did-core/#representations) depending on the `Accept` request header. + The response is implemented according to [resolver HTTP binding](https://w3c-ccg.github.io/did-resolution/#bindings-https) in the DID resolution spec. + operationId: getDID + parameters: + - name: didRef + in: path + description: Prism DID according to [the Prism DID method syntax](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#prism-did-method-syntax) + required: true + schema: + type: string + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + responses: + "200": + description: The resolution result or W3C DID document representation + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: "#/components/schemas/DIDResolutionResult" + application/did+ld+json: + schema: + $ref: "#/components/schemas/DIDDocument" + "400": + description: Invalid DID or DID URL + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: "#/components/schemas/DIDResolutionResult" + application/did+ld+json: + schema: + $ref: "#/components/schemas/DIDDocument" + "404": + description: The DID is not found + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: "#/components/schemas/DIDResolutionResult" + application/did+ld+json: + schema: + $ref: "#/components/schemas/DIDDocument" + "406": + description: The DID document representation is not supported + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: "#/components/schemas/DIDResolutionResult" + application/did+ld+json: + schema: + $ref: "#/components/schemas/DIDDocument" + "410": + description: The DID is deactivated + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: "#/components/schemas/DIDResolutionResult" + application/did+ld+json: + schema: + $ref: "#/components/schemas/DIDDocument" + "500": + description: Internal error + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: "#/components/schemas/DIDResolutionResult" + application/did+ld+json: + schema: + $ref: "#/components/schemas/DIDDocument" + "501": + description: The DID method is not supported + content: + application/ld+json; profile=https://w3id.org/did-resolution: + schema: + $ref: "#/components/schemas/DIDResolutionResult" + application/did+ld+json: + schema: + $ref: "#/components/schemas/DIDDocument" +components: + schemas: + AcceptConnectionInvitationRequest: + required: + - invitation + type: object + properties: + invitation: + type: string + description: The base64-encoded raw invitation. + example: eyJAaWQiOiIzZmE4NWY2NC01NzE3LTQ1NjItYjNmYy0yYzk2M2Y2NmFmYTYiLCJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvbXktZmFtaWx5LzEuMC9teS1tZXNzYWdlLXR5cGUiLCJkaWQiOiJXZ1d4cXp0ck5vb0c5MlJYdnhTVFd2IiwiaW1hZ2VVcmwiOiJodHRwOi8vMTkyLjE2OC41Ni4xMDEvaW1nL2xvZ28uanBnIiwibGFiZWwiOiJCb2IiLCJyZWNpcGllbnRLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInJvdXRpbmdLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8xOTIuMTY4LjU2LjEwMTo4MDIwIn0= + Connection: + required: + - connectionId + - role + - state + - invitation + - createdAt + - self + - kind + type: object + properties: + connectionId: + type: string + description: The unique identifier of the connection. + format: uuid + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + label: + type: string + description: A human readable alias for the connection. + example: Peter + myDid: + type: string + description: + The DID representing me as the inviter or invitee in this specific + connection. + example: did:peer:12345 + theirDid: + type: string + description: + The DID representing the other peer as the an inviter or invitee + in this specific connection. + example: did:peer:67890 + role: + type: string + description: The role played by the Prism agent in the connection flow. + example: Inviter + enum: + - Inviter + - Invitee + state: + type: string + description: The current state of the connection protocol execution. + example: InvitationGenerated + enum: + - InvitationGenerated + - InvitationReceived + - ConnectionRequestPending + - ConnectionRequestSent + - ConnectionRequestReceived + - ConnectionResponsePending + - ConnectionResponseSent + - ConnectionResponseReceived + - ProblemReportPending + - ProblemReportSent + - ProblemReportReceived + invitation: + $ref: "#/components/schemas/ConnectionInvitation" + createdAt: + type: string + description: The date and time the connection record was created. + format: date-time + example: 2022-03-10T12:00Z + updatedAt: + type: string + description: The date and time the connection record was last updated. + format: date-time + example: 2022-03-10T12:00Z + self: + type: string + description: The reference to the connection resource. + example: https://atala-prism-products.io/connections/ABCD-1234 + kind: + type: string + description: The type of object returned. In this case a `Connection`. + example: Connection + ConnectionInvitation: + required: + - id + - type + - from + - invitationUrl + type: object + properties: + id: + type: string + description: + The unique identifier of the invitation. It should be used + as parent thread ID (pthid) for the Connection Request message that follows. + format: uuid + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + type: + type: string + description: + The DIDComm Message Type URI (MTURI) the invitation message + complies with. + example: https://didcomm.org/out-of-band/2.0/invitation + from: + type: string + description: + The DID representing the sender to be used by recipients for + future interactions. + example: did:peer:1234457 + invitationUrl: + type: string + description: + The invitation message encoded as a URL. This URL follows the + Out of [Band 2.0 protocol](https://identity.foundation/didcomm-messaging/spec/v2.0/#out-of-band-messages) + and can be used to generate a QR code for example. + example: https://my.domain.com/path?_oob=eyJAaWQiOiIzZmE4NWY2NC01NzE3LTQ1NjItYjNmYy0yYzk2M2Y2NmFmYTYiLCJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvbXktZmFtaWx5LzEuMC9teS1tZXNzYWdlLXR5cGUiLCJkaWQiOiJXZ1d4cXp0ck5vb0c5MlJYdnhTVFd2IiwiaW1hZ2VVcmwiOiJodHRwOi8vMTkyLjE2OC41Ni4xMDEvaW1nL2xvZ28uanBnIiwibGFiZWwiOiJCb2IiLCJyZWNpcGllbnRLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInJvdXRpbmdLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8xOTIuMTY4LjU2LjEwMTo4MDIwIn0= + description: The invitation for this connection + example: > + ConnectionInvitation( + 0527aea1-d131-3948-a34d-03af39aba8b4, + https://didcomm.org/out-of-band/2.0/invitation,did:peer:1234457,https://my.domain.com/path? + _oob=eyJAaWQiOiIzZmE4NWY2NC01NzE3LTQ1NjItYjNmYy0yYzk2M2Y2NmFmYTYiLCJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvbXktZmFtaWx5LzEuMC9teS1tZXNzYWdlLXR5cGUiLCJkaWQiOiJXZ1d4cXp0ck5vb0c5MlJYdnhTVFd2IiwiaW1hZ2VVcmwiOiJodHRwOi8vMTkyLjE2OC41Ni4xMDEvaW1nL2xvZ28uanBnIiwibGFiZWwiOiJCb2IiLCJyZWNpcGllbnRLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInJvdXRpbmdLZXlzIjpbIkgzQzJBVnZMTXY2Z21NTmFtM3VWQWpacGZrY0pDd0R3blpuNnozd1htcVBWIl0sInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8xOTIuMTY4LjU2LjEwMTo4MDIwIn0=) + ConnectionsPage: + required: + - kind + - self + - pageOf + type: object + properties: + contents: + type: array + items: + $ref: "#/components/schemas/Connection" + description: "" + examples: [] + kind: + type: string + description: "" + example: ConnectionsPage + self: + type: string + description: "" + example: /prism-agent/connections?offset=10&limit=10 + pageOf: + type: string + description: "" + example: "" + next: + type: string + description: "" + example: /prism-agent/connections?offset=20&limit=10 + previous: + type: string + description: "" + example: /prism-agent/connections?offset=0&limit=10 + CreateConnectionRequest: + type: object + properties: + label: + type: string + description: A human readable alias for the connection. + example: Peter + CredentialSchemaInput: + required: + - name + - version + - type + - schema + - author + type: object + properties: + name: + type: string + description: + A human-readable name for the credential schema. A piece of + Metadata. + example: DrivingLicense + minLength: 1 + version: + type: string + description: + Denotes the revision of a given Credential Schema. It should + follow semantic version convention to describe the impact of the schema + evolution. + example: 1.0.0 + pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ + description: + type: string + description: A human-readable description of the credential schema + example: Simple credential schema for the driving licence verifiable credential. + minLength: 1 + type: + type: string + description: + This field resolves to a JSON schema with details about the + schema metadata that applies to the schema. A piece of Metadata. + example: https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json + schema: + description: + Valid JSON Schema where the Credential Schema data fields are + defined. A piece of Metadata + example: + $id: driving-license-1.0 + $schema: https://json-schema.org/draft/2020-12/schema + description: Driving License + type: object + properties: + credentialSubject: + type: object + properties: + emailAddress: + type: string + format: email + givenName: + type: string + familyName: + type: string + dateOfIssuance: + type: datetime + drivingLicenseID: + type: string + drivingClass: + type: integer + required: + - emailAddress + - familyName + - dateOfIssuance + - drivingLicenseID + - drivingClass + additionalProperties: true + tags: + type: array + items: + type: string + description: + Tokens that allow to lookup and filter the credential schema + records. + examples: + - driving + - licence + - id + author: + type: string + description: + DID of the identity which authored the credential schema. A + piece of Metadata. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + pattern: ^did:(?[a-z0-9]+(:[a-z0-9]+)*)\:(?[^#?]*)$ + CredentialSchemaResponse: + required: + - guid + - id + - name + - version + - description + - type + - schema + - author + - authored + - kind + - self + type: object + properties: + guid: + type: string + description: + "Globally unique id of the credential schema.It's composed + from the bytes of the string that contain the `author`, `name`, and `version` + values.The string format looks like the resource identifier: `author`/`id`?version=`version`" + format: uuid + example: 0527aea1-d131-3948-a34d-03af39aba8b4 + id: + type: string + description: + A locally unique identifier to address the schema. UUID is + generated by the backend. + example: 0527aea1-d131-3948-a34d-03af39aba8b5 + longId: + type: string + description: + Resource id of the credential schema. Contains the `author`'s + DID, `id` and `version` fields. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff/0527aea1-d131-3948-a34d-03af39aba8b4?version=1.0.0 + name: + type: string + description: + A human-readable name for the credential schema. A piece of + Metadata. + example: DrivingLicense + version: + type: string + description: + Denotes the revision of a given Credential Schema. It should + follow semantic version convention to describe the impact of the schema + evolution. + example: 1.0.0 + tags: + type: array + items: + type: string + description: + Tokens that allow to lookup and filter the credential schema + records. + examples: + - driving + - licence + - id + description: + type: string + description: A human-readable description of the credential schema + example: Simple credential schema for the driving licence verifiable credential. + type: + type: string + description: + This field resolves to a JSON schema with details about the + schema metadata that applies to the schema. A piece of Metadata. + example: https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json + schema: + description: + Valid JSON Schema where the Credential Schema data fields are + defined. A piece of Metadata + example: + $id: driving-license-1.0 + $schema: https://json-schema.org/draft/2020-12/schema + description: Driving License + type: object + properties: + credentialSubject: + type: object + properties: + emailAddress: + type: string + format: email + givenName: + type: string + familyName: + type: string + dateOfIssuance: + type: datetime + drivingLicenseID: + type: string + drivingClass: + type: integer + required: + - emailAddress + - familyName + - dateOfIssuance + - drivingLicenseID + - drivingClass + additionalProperties: true + author: + type: string + description: + DID of the identity which authored the credential schema. A + piece of Metadata. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + authored: + type: string + description: + "[RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date on + which the credential schema was created. A piece of Metadata." + format: date-time + example: 2022-03-10T12:00Z + proof: + $ref: "#/components/schemas/Proof" + kind: + type: string + description: + A string that identifies the type of resource being returned + in the response. + example: CredentialSchema + self: + type: string + description: + The URL that uniquely identifies the resource being returned + in the response. + example: /prism-agent/schema-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4 + CredentialSchemaResponsePage: + required: + - kind + - self + - pageOf + type: object + properties: + contents: + type: array + items: + $ref: "#/components/schemas/CredentialSchemaResponse" + description: + A sequence of CredentialSchemaResponse objects representing + the list of credential schemas that the API response contains + examples: [] + kind: + type: string + description: + A string field indicating the type of the API response. In + this case, it will always be set to `CredentialSchemaPage` + example: CredentialSchemaPage + self: + type: string + description: A string field containing the URL of the current API endpoint + example: /prism-agent/schema-registry/schemas?skip=10&limit=10 + pageOf: + type: string + description: + A string field indicating the type of resource that the contents + field contains + example: /prism-agent/schema-registry/schemas + next: + type: string + description: + An optional string field containing the URL of the next page + of results. If the API response does not contain any more pages, this + field should be set to None. + example: /prism-agent/schema-registry/schemas?skip=20&limit=10 + previous: + type: string + description: + An optional string field containing the URL of the previous + page of results. If the API response is the first page of results, this + field should be set to None. + example: /prism-agent/schema-registry/schemas?skip=0&limit=10 + DIDDocument: + required: + - id + type: object + properties: + "@context": + type: array + items: + type: string + description: The JSON-LD context for the DID resolution result. + id: + type: string + description: |- + [DID subject](https://www.w3.org/TR/did-core/#did-subject). + The value must match the DID that was given to the resolver. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + controller: + type: string + description: "[DID controller](https://www.w3.org/TR/did-core/#did-controller)" + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + verificationMethod: + type: array + items: + $ref: "#/components/schemas/VerificationMethod" + authentication: + type: array + items: + type: string + assertionMethod: + type: array + items: + type: string + keyAgreement: + type: array + items: + type: string + capabilityInvocation: + type: array + items: + type: string + capabilityDelegation: + type: array + items: + type: string + service: + type: array + items: + $ref: "#/components/schemas/Service" + description: A W3C compliant Prism DID document representation. + DIDDocumentMetadata: + type: object + properties: + deactivated: + type: boolean + description: + If a DID has been deactivated, DID document metadata MUST include + this property with the boolean value true. If a DID has not been deactivated, + this property is OPTIONAL, but if included, MUST have the boolean value + false. + example: false + canonicalId: + type: string + description: |2 + + A DID in canonical form. + If a DID is in long form and has been published, DID document metadata MUST contain a `canonicalId`` property with the short form DID as its value. + If a DID in short form or has not been published, DID document metadata MUST NOT contain a `canonicalId` property. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + description: "[DID document metadata](https://www.w3.org/TR/did-core/#did-document-metadata)" + DIDResolutionMetadata: + type: object + properties: + error: + type: string + description: Resolution error constant according to [DID spec registries](https://www.w3.org/TR/did-spec-registries/#error) + example: invalidDid + errorMessage: + type: string + description: Resolution error message + example: The initialState does not match the suffix + contentType: + type: string + description: The media type of the returned DID document + example: application/did+ld+json + description: "[DID resolution metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata)" + DIDResolutionResult: + required: + - "@context" + - didDocumentMetadata + - didResolutionMetadata + type: object + properties: + "@context": + type: string + description: The JSON-LD context for the DID resolution result. + example: https://w3id.org/did-resolution/v1 + didDocument: + $ref: "#/components/schemas/DIDDocument" + didDocumentMetadata: + $ref: "#/components/schemas/DIDDocumentMetadata" + didResolutionMetadata: + $ref: "#/components/schemas/DIDResolutionMetadata" + ErrorResponse: + required: + - status + - type + - title + - instance + type: object + properties: + status: + type: integer + description: The HTTP status code for this occurrence of the problem. + format: int32 + example: 200 + type: + type: string + description: A URI reference that identifies the problem type. + example: https://example.org/doc/#model-MalformedEmail/ + title: + type: string + description: + A short, human-readable summary of the problem type. It does + not change from occurrence to occurrence of the problem. + example: Malformed email + detail: + type: string + description: + A human-readable explanation specific to this occurrence of + the problem. + example: The received '{}à!è@!.b}' email does not conform to the email format + instance: + type: string + description: + A URI reference that identifies the specific occurrence of + the problem. It may or may not yield further information if dereferenced. + example: The received '{}à!è@!.b}' email does not conform to the email format + Proof: + required: + - type + - created + - verificationMethod + - proofPurpose + - proofValue + - jws + type: object + properties: + type: + type: string + description: + The type of cryptographic signature algorithm used to generate + the proof. + example: Ed25519Signature2018 + created: + type: string + description: + The date and time at which the proof was created, in UTC format. + This field is used to ensure that the proof was generated before or at + the same time as the credential schema itself. + format: date-time + example: 2022-03-10T12:00Z + verificationMethod: + type: string + description: + The verification method used to generate the proof. This is + usually a DID and key ID combination that can be used to look up the public + key needed to verify the proof. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1 + proofPurpose: + type: string + description: + "The purpose of the proof (for example: `assertionMethod`). + This indicates that the proof is being used to assert that the issuer + really issued this credential schema instance." + example: assertionMethod + proofValue: + type: string + description: + The cryptographic signature value that was generated using + the private key associated with the verification method, and which can + be used to verify the proof. + example: FiPfjknHikKmZ... + jws: + type: string + description: The JSON Web Signature (JWS) that contains the proof information. + example: eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImt0eSI6Ik... + domain: + type: string + description: + It specifies the domain context within which the credential + schema and proof are being used + example: prims.atala.com + description: + A digital signature over the Credential Schema for the sake of + asserting authorship. A piece of Metadata. + example: + type: Ed25519Signature2018 + created: "2022-03-10T12:00:00Z" + verificationMethod: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1 + proofPurpose: assertionMethod + proofValue: FiPfjknHikKmZ... + jws: eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImt0eSI6Ik... + domain: prims.atala.com + PublicKeyJwk: + required: + - kty + type: object + properties: + crv: + type: string + x: + type: string + y: + type: string + kty: + type: string + Service: + required: + - id + - type + - serviceEndpoint + type: object + properties: + id: + type: string + description: |- + The id of the service. + Requires a URI fragment when use in create / update DID. + Returns the full ID (with DID prefix) when resolving DID + example: service-1 + type: + type: string + description: + Service type. Can contain multiple possible values as described + in the [Create DID operation](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#create-did) + under the construction section. + example: LinkedDomains + serviceEndpoint: + type: array + items: + type: string + description: + The service endpoint. Can contain multiple possible values + as described in the [Create DID operation] + description: A service expressed in the DID document. https://www.w3.org/TR/did-core/#services + VerificationMethod: + required: + - id + - type + - controller + - publicKeyJwk + type: object + properties: + id: + type: string + description: The identifier for the verification method. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1 + type: + type: string + description: The type of the verification method. + example: JsonWebKey2020 + controller: + type: string + description: The DID that controls the verification method. + example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + publicKeyJwk: + $ref: "#/components/schemas/PublicKeyJwk" + description: A cryptographic public key expressed in the DID document. https://www.w3.org/TR/did-core/#verification-methods + VerificationPolicy: + required: + - self + - kind + - id + - nonce + - name + - description + - createdAt + - updatedAt + type: object + properties: + self: + type: string + kind: + type: string + id: + type: string + format: uuid + nonce: + type: integer + format: int32 + name: + type: string + description: + type: string + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + constraints: + type: array + items: + $ref: "#/components/schemas/VerificationPolicyConstraint" + VerificationPolicyConstraint: + required: + - schemaId + type: object + properties: + schemaId: + type: string + trustedIssuers: + type: array + items: + type: string + VerificationPolicyInput: + required: + - name + - description + type: object + properties: + id: + type: string + format: uuid + name: + type: string + description: + type: string + constraints: + type: array + items: + $ref: "#/components/schemas/VerificationPolicyConstraint" + VerificationPolicyPage: + required: + - self + - kind + - pageOf + type: object + properties: + self: + type: string + kind: + type: string + pageOf: + type: string + next: + type: string + previous: + type: string + contents: + type: array + items: + $ref: "#/components/schemas/VerificationPolicy" diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala index 2a235cddc3..fe77cc029c 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Main.scala @@ -28,6 +28,7 @@ import io.circe.parser.* import io.circe.syntax.* import io.iohk.atala.agent.server.health.HealthInfo import io.iohk.atala.connect.controller.ConnectionControllerImpl +import io.iohk.atala.castor.controller.DIDControllerImpl import java.security.Security @@ -156,7 +157,8 @@ object AgentApp extends ZIOAppDefault { RepoModule.credentialSchemaServiceLayer, AppModule.manageDIDServiceLayer, RepoModule.verificationPolicyServiceLayer, - ConnectionControllerImpl.layer + ConnectionControllerImpl.layer, + DIDControllerImpl.layer, ) } yield app diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Modules.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Modules.scala index f5d80c5909..84a9218d2b 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Modules.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/Modules.scala @@ -67,11 +67,13 @@ import zio.stream.ZStream import java.io.IOException import java.util.concurrent.Executors import io.iohk.atala.mercury.protocol.trustping.TrustPing +import io.iohk.atala.castor.controller.DIDServerEndpoints +import io.iohk.atala.castor.controller.DIDController object Modules { def app(port: Int): RIO[ - DidOps & DidAgent & ManagedDIDService & AppConfig & DIDRegistrarApi & IssueCredentialsProtocolApi & DIDApi & + DidOps & DidAgent & ManagedDIDService & AppConfig & DIDRegistrarApi & IssueCredentialsProtocolApi & PresentProofApi & ActorSystem[Nothing], Unit ] = { @@ -80,14 +82,17 @@ object Modules { httpServerApp.unit } - lazy val zioApp - : RIO[CredentialSchemaController & VerificationPolicyController & ConnectionController & AppConfig, Unit] = { + lazy val zioApp: RIO[ + CredentialSchemaController & VerificationPolicyController & ConnectionController & DIDController & AppConfig, + Unit + ] = { val zioHttpServerApp = for { allSchemaRegistryEndpoints <- SchemaRegistryServerEndpoints.all allVerificationPolicyEndpoints <- VerificationPolicyServerEndpoints.all allConnectionEndpoints <- ConnectionServerEndpoints.all + allDIDEndpoints <- DIDServerEndpoints.all allEndpoints = ZHttpEndpoints.withDocumentations[Task]( - allSchemaRegistryEndpoints ++ allVerificationPolicyEndpoints ++ allConnectionEndpoints + allSchemaRegistryEndpoints ++ allVerificationPolicyEndpoints ++ allConnectionEndpoints ++ allDIDEndpoints ) appConfig <- ZIO.service[AppConfig] httpServer <- ZHttp4sBlazeServer.start(allEndpoints, port = appConfig.agent.httpEndpoint.http.port) @@ -489,13 +494,6 @@ object GrpcModule { } object HttpModule { - val didApiLayer: TaskLayer[DIDApi] = { - val serviceLayer = AppModule.didServiceLayer - val apiServiceLayer = serviceLayer >>> DIDApiServiceImpl.layer - val apiMarshallerLayer = DIDApiMarshallerImpl.layer - (apiServiceLayer ++ apiMarshallerLayer) >>> ZLayer.fromFunction(new DIDApi(_, _)) - } - val didRegistrarApiLayer: TaskLayer[DIDRegistrarApi] = { val serviceLayer = AppModule.manageDIDServiceLayer val apiServiceLayer = serviceLayer >>> DIDRegistrarApiServiceImpl.layer @@ -514,15 +512,13 @@ object HttpModule { } val presentProofProtocolApiLayer: RLayer[DidOps & DidAgent, PresentProofApi] = { - val serviceLayer = AppModule.presentationServiceLayer ++ AppModule.connectionServiceLayer // ++ didCommServiceLayer + val serviceLayer = AppModule.presentationServiceLayer ++ AppModule.connectionServiceLayer val apiServiceLayer = serviceLayer >>> PresentProofApiServiceImpl.layer val apiMarshallerLayer = PresentProofApiMarshallerImpl.layer (apiServiceLayer ++ apiMarshallerLayer) >>> ZLayer.fromFunction(new PresentProofApi(_, _)) } - val layers = - didApiLayer ++ didRegistrarApiLayer ++ - issueCredentialsProtocolApiLayer ++ presentProofProtocolApiLayer + val layers = didRegistrarApiLayer ++ issueCredentialsProtocolApiLayer ++ presentProofProtocolApiLayer } object RepoModule { diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/HttpRoutes.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/HttpRoutes.scala index d72f36f0cd..d4fe9d2330 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/HttpRoutes.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/HttpRoutes.scala @@ -3,22 +3,20 @@ package io.iohk.atala.agent.server.http import akka.http.scaladsl.model.ContentType import akka.http.scaladsl.server.Directives.* import akka.http.scaladsl.server.Route -import io.iohk.atala.agent.openapi.api.{DIDApi, DIDRegistrarApi, IssueCredentialsProtocolApi, PresentProofApi} +import io.iohk.atala.agent.openapi.api.{DIDRegistrarApi, IssueCredentialsProtocolApi, PresentProofApi} import zio.* object HttpRoutes { def routes: URIO[ - DIDApi & DIDRegistrarApi & IssueCredentialsProtocolApi & PresentProofApi, + DIDRegistrarApi & IssueCredentialsProtocolApi & PresentProofApi, Route ] = for { - didApi <- ZIO.service[DIDApi] disRegistrarApi <- ZIO.service[DIDRegistrarApi] issueCredentialsProtocolApi <- ZIO.service[IssueCredentialsProtocolApi] presentProofApi <- ZIO.service[PresentProofApi] - } yield didApi.route ~ - disRegistrarApi.route ~ + } yield disRegistrarApi.route ~ issueCredentialsProtocolApi.route ~ presentProofApi.route ~ additionalRoute diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/marshaller/DIDApiMarshallerImpl.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/marshaller/DIDApiMarshallerImpl.scala deleted file mode 100644 index 632b91df43..0000000000 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/marshaller/DIDApiMarshallerImpl.scala +++ /dev/null @@ -1,20 +0,0 @@ -package io.iohk.atala.agent.server.http.marshaller - -import akka.http.scaladsl.marshalling.ToEntityMarshaller -import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller -import io.iohk.atala.agent.openapi.api.DIDApiMarshaller -import io.iohk.atala.agent.openapi.model.{DIDOperationResponse, ErrorResponse} -import spray.json.RootJsonFormat -import zio.* -import io.iohk.atala.agent.server.http.model.OASModelPatches - -object DIDApiMarshallerImpl extends JsonSupport { - - val layer: ULayer[DIDApiMarshaller] = ZLayer.succeed { - new DIDApiMarshaller { - implicit def toEntityMarshallerDIDResolutionResult: ToEntityMarshaller[OASModelPatches.DIDResolutionResult] = - summon[RootJsonFormat[OASModelPatches.DIDResolutionResult]] - } - } - -} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/marshaller/JsonSupport.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/marshaller/JsonSupport.scala index 8a1b311ff0..9370aa584d 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/marshaller/JsonSupport.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/marshaller/JsonSupport.scala @@ -2,7 +2,6 @@ package io.iohk.atala.agent.server.http.marshaller import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport import io.iohk.atala.agent.openapi.model.* -import io.iohk.atala.agent.server.http.model.OASModelPatches import spray.json.{ DefaultJsonProtocol, DeserializationException, @@ -27,15 +26,12 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol { CreateManagedDidRequestDocumentTemplate.apply ) given RootJsonFormat[CreateManagedDIDResponse] = jsonFormat1(CreateManagedDIDResponse.apply) - given RootJsonFormat[DIDDocumentMetadata] = jsonFormat2(DIDDocumentMetadata.apply) given RootJsonFormat[DIDOperationResponse] = jsonFormat1(DIDOperationResponse.apply) given RootJsonFormat[DidOperationSubmission] = jsonFormat2(DidOperationSubmission.apply) - given RootJsonFormat[DIDResolutionMetadata] = jsonFormat3(DIDResolutionMetadata.apply) given RootJsonFormat[ErrorResponse] = jsonFormat5(ErrorResponse.apply) given RootJsonFormat[ManagedDID] = jsonFormat3(ManagedDID.apply) given RootJsonFormat[ManagedDIDPage] = jsonFormat6(ManagedDIDPage.apply) given RootJsonFormat[ManagedDIDKeyTemplate] = jsonFormat2(ManagedDIDKeyTemplate.apply) - given RootJsonFormat[PublicKeyJwk] = jsonFormat4(PublicKeyJwk.apply) given RootJsonFormat[Service] = jsonFormat3(Service.apply) given RootJsonFormat[UpdateManagedDIDRequest] = jsonFormat1(UpdateManagedDIDRequest.apply) given RootJsonFormat[UpdateManagedDIDRequestActionsInner] = jsonFormat6(UpdateManagedDIDRequestActionsInner.apply) @@ -48,7 +44,6 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol { given RootJsonFormat[UpdateManagedDIDRequestActionsInnerUpdateService] = jsonFormat3( UpdateManagedDIDRequestActionsInnerUpdateService.apply ) - given RootJsonFormat[VerificationMethod] = jsonFormat4(VerificationMethod.apply) // Issue Credential Protocol implicit object UUIDFormat extends JsonFormat[UUID] { @@ -83,9 +78,4 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol { given RootJsonFormat[PresentationStatus] = jsonFormat5(PresentationStatus.apply) given RootJsonFormat[RequestPresentationAction] = jsonFormat2(RequestPresentationAction.apply) given RootJsonFormat[PresentationStatusPage] = jsonFormat6(PresentationStatusPage.apply) - - // Manual model patches - given RootJsonFormat[OASModelPatches.DIDDocument] = jsonFormat10(OASModelPatches.DIDDocument.apply) - given RootJsonFormat[OASModelPatches.DIDResolutionResult] = jsonFormat4(OASModelPatches.DIDResolutionResult.apply) - } diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/CustomMediaTypes.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/CustomMediaTypes.scala deleted file mode 100644 index 722f2a17eb..0000000000 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/CustomMediaTypes.scala +++ /dev/null @@ -1,18 +0,0 @@ -package io.iohk.atala.agent.server.http.model - -import akka.http.scaladsl.model.MediaType -import akka.http.scaladsl.model.HttpCharsets - -object CustomMediaTypes { - - val `application/did+ld+json`: MediaType.WithFixedCharset = - MediaType.customWithFixedCharset("application", "did+ld+json", HttpCharsets.`UTF-8`) - - val `application/ld+json;did-resolution`: MediaType.WithFixedCharset = MediaType.customWithFixedCharset( - "application", - "ld+json", - HttpCharsets.`UTF-8`, - params = Map("profile" -> "https://w3id.org/did-resolution") - ) - -} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/OASDomainModelHelper.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/OASDomainModelHelper.scala index aa41b32ea9..948dfb194e 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/OASDomainModelHelper.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/OASDomainModelHelper.scala @@ -28,9 +28,8 @@ trait OASDomainModelHelper { extension (service: Service) { def toDomain: Either[String, castorDomain.Service] = { for { - serviceEndpoint <- service.serviceEndpoint.traverse(s => - Uri.parseTry(s).toEither.left.map(_ => s"unable to parse serviceEndpoint $s as URI") - ) + serviceEndpoint <- service.serviceEndpoint + .traverse(s => Uri.parseTry(s).toEither.left.map(_ => s"unable to parse serviceEndpoint $s as URI")) serviceType <- castorDomain.ServiceType .parseString(service.`type`) .toRight(s"unsupported serviceType ${service.`type`}") @@ -190,88 +189,6 @@ trait OASDomainModelHelper { .mapError(e => HttpServiceError.InvalidPayload(s"Error parsing string as DidCommID: ${e.getMessage()}")) } - extension (resolution: (castorDomain.w3c.DIDDocumentMetadataRepr, castorDomain.w3c.DIDDocumentRepr)) { - def toOAS: OASModelPatches.DIDResolutionResult = { - val (metadata, didDoc) = resolution - val isDeactivated = metadata.deactivated - OASModelPatches.DIDResolutionResult( - `@context` = "https://w3id.org/did-resolution/v1", - didDocument = - if (isDeactivated) None - else - Some( - OASModelPatches.DIDDocument( - `@context` = Seq("https://www.w3.org/ns/did/v1"), - id = didDoc.id, - controller = Some(didDoc.controller), - verificationMethod = Some(didDoc.verificationMethod.map(_.toOAS)), - authentication = Some(didDoc.authentication.map(_.toOAS)), - assertionMethod = Some(didDoc.assertionMethod.map(_.toOAS)), - keyAgreement = Some(didDoc.keyAgreement.map(_.toOAS)), - capabilityInvocation = Some(didDoc.capabilityInvocation.map(_.toOAS)), - service = Some(didDoc.service.map(_.toOAS)) - ) - ), - didDocumentMetadata = DIDDocumentMetadata( - deactivated = Some(metadata.deactivated), - canonicalId = Some(metadata.canonicalId) - ), - didResolutionMetadata = DIDResolutionMetadata() - ) - } - } - - extension (resolutionError: castorDomain.w3c.DIDResolutionErrorRepr) { - def toOAS: OASModelPatches.DIDResolutionResult = { - OASModelPatches.DIDResolutionResult( - `@context` = "https://w3id.org/did-resolution/v1", - didDocument = None, - didDocumentMetadata = DIDDocumentMetadata(), - didResolutionMetadata = - DIDResolutionMetadata(error = Some(resolutionError.value), errorMessage = resolutionError.errorMessage) - ) - } - } - - extension (publicKeyRepr: castorDomain.w3c.PublicKeyRepr) { - def toOAS: VerificationMethod = { - VerificationMethod( - id = publicKeyRepr.id, - `type` = publicKeyRepr.`type`, - controller = publicKeyRepr.controller, - publicKeyJwk = publicKeyRepr.publicKeyJwk.toOAS - ) - } - } - - extension (publicKeyReprOrRef: castorDomain.w3c.PublicKeyReprOrRef) { - def toOAS: String = { - publicKeyReprOrRef match { - case s: String => s - case pk: PublicKeyRepr => throw Exception("Embedded public key is not yet supported in W3C representation") - } - } - } - - extension (publicKeyJwk: castorDomain.w3c.PublicKeyJwk) { - def toOAS: PublicKeyJwk = { - PublicKeyJwk( - crv = Some(publicKeyJwk.crv), - x = Some(publicKeyJwk.x), - y = Some(publicKeyJwk.y), - kty = publicKeyJwk.kty, - ) - } - } - - extension (service: castorDomain.w3c.ServiceRepr) { - def toOAS: Service = Service( - id = service.id, - `type` = service.`type`, - serviceEndpoint = service.serviceEndpoint // FIXME @pat - ) - } - extension (didDetail: walletDomain.ManagedDIDDetail) { def toOAS: ManagedDID = { val (longFormDID, status) = didDetail.state match { diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/OASModelPatches.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/OASModelPatches.scala deleted file mode 100644 index 9a498f777f..0000000000 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/model/OASModelPatches.scala +++ /dev/null @@ -1,31 +0,0 @@ -package io.iohk.atala.agent.server.http.model - -import io.iohk.atala.agent.openapi.model.{Service, VerificationMethod, DIDDocumentMetadata, DIDResolutionMetadata} - -// Use for overriding models when OpenAPI generator cannot correctly generate models -// The config to patch the generated models is in build.sbt -object OASModelPatches { - - // Need this because the OAS generator cannot generate a case class with a field named '@context' - final case class DIDDocument( - `@context`: Seq[String], - id: String, - controller: Option[String] = None, - verificationMethod: Option[Seq[VerificationMethod]] = None, - authentication: Option[Seq[String]] = None, - assertionMethod: Option[Seq[String]] = None, - keyAgreement: Option[Seq[String]] = None, - capabilityInvocation: Option[Seq[String]] = None, - capabilityDelegation: Option[Seq[String]] = None, - service: Option[Seq[Service]] = None - ) - - // Need this because the OAS generator cannot generate a case class with a field named '@context' - final case class DIDResolutionResult( - `@context`: String, - didDocument: Option[DIDDocument] = None, - didDocumentMetadata: DIDDocumentMetadata, - didResolutionMetadata: DIDResolutionMetadata - ) - -} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/service/DIDApiServiceImpl.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/service/DIDApiServiceImpl.scala deleted file mode 100644 index c2e45827a6..0000000000 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/service/DIDApiServiceImpl.scala +++ /dev/null @@ -1,90 +0,0 @@ -package io.iohk.atala.agent.server.http.service - -import akka.http.scaladsl.server.Directives.* -import akka.http.scaladsl.marshalling.ToEntityMarshaller -import akka.http.scaladsl.server.Route -import io.iohk.atala.castor.core.service.DIDService -import io.iohk.atala.castor.core.model.did.PrismDID -import io.iohk.atala.castor.core.model.did.w3c.W3CModelHelper.* -import io.iohk.atala.castor.core.model.did.w3c.makeW3CResolver -import io.iohk.atala.agent.openapi.api.DIDApiService -import io.iohk.atala.agent.openapi.model.* -import io.iohk.atala.agent.server.http.model.{HttpServiceError, OASDomainModelHelper, OASErrorModelHelper} -import io.iohk.atala.castor.core.model.error.DIDOperationError -import zio.* -import io.iohk.atala.agent.server.http.model.{OASModelPatches, CustomMediaTypes} -import io.iohk.atala.castor.core.model.did.w3c.DIDResolutionErrorRepr -import spray.json.{CompactPrinter, JsonWriter, RootJsonFormat} -import akka.http.scaladsl.marshalling.Marshaller - -class DIDApiServiceImpl(service: DIDService)(using runtime: Runtime[Any]) - extends DIDApiService, - AkkaZioSupport, - OASDomainModelHelper, - OASErrorModelHelper { - - override def getDid(didRef: String, accept: Option[String])(implicit - toEntityMarshallerDIDResolutionResult: ToEntityMarshaller[OASModelPatches.DIDResolutionResult] - ): Route = { - val result = for { - result <- makeW3CResolver(service)(didRef).either - resolutionResult = result.fold(_.toOAS, _.toOAS) - resolutionError = result.swap.toOption - } yield buildHttpBindingResponse(resolutionResult, resolutionError) - - onZioSuccess(result)(identity) - } - - // Return response dynamically based on "Content-Type" negotiation - // according to https://w3c-ccg.github.io/did-resolution/#bindings-https - private def buildHttpBindingResponse( - resolutionResult: OASModelPatches.DIDResolutionResult, - resolutionError: Option[DIDResolutionErrorRepr] - ): Route = { - import io.iohk.atala.agent.server.http.marshaller.JsonSupport.{optionFormat, given} - import DIDResolutionErrorRepr.* - - val jsonLdMarshaller: ToEntityMarshaller[OASModelPatches.DIDResolutionResult] = { - val writer = optionFormat[OASModelPatches.DIDDocument] - Marshaller.StringMarshaller - .wrap(CustomMediaTypes.`application/did+ld+json`)(CompactPrinter) - .compose(writer.write) - .compose(_.didDocument) - } - val resolutionResultMarshaller: ToEntityMarshaller[OASModelPatches.DIDResolutionResult] = { - val writer = summon[RootJsonFormat[OASModelPatches.DIDResolutionResult]] - Marshaller.StringMarshaller - .wrap(CustomMediaTypes.`application/ld+json;did-resolution`)(CompactPrinter) - .compose(writer.write) - .compose { result => - result.copy(didResolutionMetadata = - result.didResolutionMetadata.copy(contentType = Some(CustomMediaTypes.`application/did+ld+json`.value)) - ) - } - } - - given ToEntityMarshaller[OASModelPatches.DIDResolutionResult] = - Marshaller.oneOf(resolutionResultMarshaller, jsonLdMarshaller) - - val isDeactivated = resolutionResult.didDocumentMetadata.deactivated.getOrElse(false) - resolutionError match { - case None if !isDeactivated => complete(200 -> resolutionResult) - case None => complete(410 -> resolutionResult) - case Some(InvalidDID(_)) => complete(400 -> resolutionResult) - case Some(InvalidDIDUrl(_)) => complete(400 -> resolutionResult) - case Some(NotFound) => complete(404 -> resolutionResult) - case Some(RepresentationNotSupported) => complete(406 -> resolutionResult) - case Some(InternalError(_)) => complete(500 -> resolutionResult) - case Some(_) => complete(500 -> resolutionResult) - } - } -} - -object DIDApiServiceImpl { - val layer: URLayer[DIDService, DIDApiService] = ZLayer.fromZIO { - for { - rt <- ZIO.runtime[Any] - svc <- ZIO.service[DIDService] - } yield DIDApiServiceImpl(svc)(using rt) - } -} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/service/PresentProofApiServiceImpl.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/service/PresentProofApiServiceImpl.scala index 7b1ac8a369..16b185b05f 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/service/PresentProofApiServiceImpl.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/agent/server/http/service/PresentProofApiServiceImpl.scala @@ -26,7 +26,6 @@ import io.iohk.atala.pollux.core.model._ import io.iohk.atala.mercury.model.Base64 import cats.instances.option import io.iohk.atala.pollux.core.model.presentation.Options -import io.iohk.atala.agent.openapi.model.PublicKeyJwk class PresentProofApiServiceImpl(presentationService: PresentationService, connectionService: ConnectionService)(using runtime: Runtime[Any] diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/EndpointOutputs.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/EndpointOutputs.scala index b3c3587375..3368925683 100644 --- a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/EndpointOutputs.scala +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/EndpointOutputs.scala @@ -1,6 +1,5 @@ package io.iohk.atala.api.http import sttp.model.StatusCode -import sttp.tapir.EndpointOutput.OneOfVariant import sttp.tapir.json.zio.jsonBody import sttp.tapir.{oneOfVariantValueMatcher, *} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/codec/CustomMediaTypes.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/codec/CustomMediaTypes.scala new file mode 100644 index 0000000000..f6dc4d45ed --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/codec/CustomMediaTypes.scala @@ -0,0 +1,18 @@ +package io.iohk.atala.api.http.codec + +import sttp.model.MediaType + +object CustomMediaTypes { + + val `application/did+ld+json`: MediaType = MediaType( + mainType = "application", + subType = "did+ld+json", + ) + + val `application/ld+json;did-resolution`: MediaType = MediaType( + mainType = "application", + subType = "ld+json", + otherParameters = Map("profile" -> "https://w3id.org/did-resolution") + ) + +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/codec/DIDCodec.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/codec/DIDCodec.scala new file mode 100644 index 0000000000..c139065470 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/api/http/codec/DIDCodec.scala @@ -0,0 +1,47 @@ +package io.iohk.atala.api.http.codec + +import io.iohk.atala.castor.controller.http.{DIDDocument, DIDResolutionResult} +import sttp.model.MediaType +import sttp.tapir.* +import sttp.tapir.DecodeResult.Error.{JsonDecodeException, JsonError} +import sttp.tapir.DecodeResult.{Error, Value} +import zio.json.{EncoderOps, JsonCodec, JsonDecoder, JsonEncoder} + +object DIDCodec { + + final case class DIDJsonLD() extends CodecFormat { + override val mediaType: MediaType = CustomMediaTypes.`application/did+ld+json` + } + + final case class DIDResolutionJsonLD() extends CodecFormat { + override val mediaType: MediaType = CustomMediaTypes.`application/ld+json;did-resolution` + } + + def didJsonLD: Codec[String, DIDResolutionResult, DIDJsonLD] = { + val errorMsg = "Decoding application/did+ld+json resource is not supported" + val didDocumentCodec = sttp.tapir.json.zio.zioCodec[Option[DIDDocument]] + Codec + .json[DIDResolutionResult](_ => DecodeResult.Error(errorMsg, Exception(errorMsg)))(resolutionResult => + didDocumentCodec.encode(resolutionResult.didDocument) + ) + .schema(didDocumentCodec.schema.map[DIDResolutionResult](_ => None)(_.didDocument)) + .format(DIDJsonLD()) + } + + def didResolutionJsonLD: Codec[String, DIDResolutionResult, DIDResolutionJsonLD] = { + val codecFormat = DIDResolutionJsonLD() + sttp.tapir.json.zio + .zioCodec[DIDResolutionResult] + .format(codecFormat) + .map[DIDResolutionResult](i => i)(resolutionResult => + // inject the contentType to the response body based on the content negotiation result + // https://www.w3.org/TR/did-core/#did-resolution-metadata + resolutionResult + .copy(didResolutionMetadata = + resolutionResult.didResolutionMetadata + .copy(contentType = Some(codecFormat.mediaType.toString)) + ) + ) + } + +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDController.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDController.scala new file mode 100644 index 0000000000..c04d41750c --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDController.scala @@ -0,0 +1,44 @@ +package io.iohk.atala.castor.controller + +import io.iohk.atala.api.http.RequestContext +import io.iohk.atala.castor.controller.http.DIDResolutionResult +import sttp.model.StatusCode +import zio.* +import io.iohk.atala.castor.controller.http.{DIDDocument, DIDDocumentMetadata, DIDResolutionMetadata} +import io.iohk.atala.castor.core.model.did.w3c.{ + PublicKeyReprOrRef, + DIDDocumentMetadataRepr, + DIDDocumentRepr, + DIDResolutionErrorRepr +} + +trait DIDController { + def getDID(did: String): UIO[(StatusCode, DIDResolutionResult)] +} + +object DIDController { + def toResolutionResult( + didDocumentMetadata: DIDDocumentMetadataRepr, + didDocument: DIDDocumentRepr + ): DIDResolutionResult = { + val isDeactivated = didDocumentMetadata.deactivated + DIDResolutionResult( + `@context` = "https://w3id.org/did-resolution/v1", + didDocument = if (isDeactivated) None else Some(didDocument), + didDocumentMetadata = didDocumentMetadata, + didResolutionMetadata = DIDResolutionMetadata() + ) + } + + def toResolutionResult(resolutionError: DIDResolutionErrorRepr): DIDResolutionResult = { + DIDResolutionResult( + `@context` = "https://w3id.org/did-resolution/v1", + didDocument = None, + didDocumentMetadata = DIDDocumentMetadata(), + didResolutionMetadata = DIDResolutionMetadata( + error = Some(resolutionError.value), + errorMessage = resolutionError.errorMessage + ) + ) + } +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDControllerImpl.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDControllerImpl.scala new file mode 100644 index 0000000000..519825e651 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDControllerImpl.scala @@ -0,0 +1,51 @@ +package io.iohk.atala.castor.controller + +import io.iohk.atala.api.http.RequestContext +import io.iohk.atala.castor.controller.http.DIDResolutionResult +import sttp.model.StatusCode +import zio.* +import io.iohk.atala.castor.core.service.DIDService +import io.iohk.atala.castor.core.service.DIDServiceImpl +import io.iohk.atala.castor.core.model.did.w3c.makeW3CResolver +import io.iohk.atala.castor.core.model.did.w3c.DIDResolutionErrorRepr +import io.iohk.atala.castor.controller.DIDControllerImpl.resolutionStatusCodeMapping + +class DIDControllerImpl(service: DIDService) extends DIDController { + + override def getDID(did: String): UIO[(StatusCode, DIDResolutionResult)] = { + for { + result <- makeW3CResolver(service)(did).either + resolutionResult = result.fold( + DIDController.toResolutionResult, + { case (metadata, document) => + DIDController.toResolutionResult(metadata, document) + } + ) + statsCode = resolutionStatusCodeMapping(resolutionResult, result.swap.toOption) + } yield statsCode -> resolutionResult + } + +} + +object DIDControllerImpl { + val layer: URLayer[DIDService, DIDController] = ZLayer.fromFunction(DIDControllerImpl(_)) + + // MUST conform to https://w3c-ccg.github.io/did-resolution/#bindings-https + def resolutionStatusCodeMapping( + resolutionResult: DIDResolutionResult, + resolutionError: Option[DIDResolutionErrorRepr] + ): StatusCode = { + import DIDResolutionErrorRepr.* + val isDeactivated = resolutionResult.didDocumentMetadata.deactivated.getOrElse(false) + resolutionError match { + case None if !isDeactivated => StatusCode.Ok + case None => StatusCode.Gone + case Some(InvalidDID(_)) => StatusCode.BadRequest + case Some(InvalidDIDUrl(_)) => StatusCode.BadRequest + case Some(NotFound) => StatusCode.NotFound + case Some(RepresentationNotSupported) => StatusCode.NotAcceptable + case Some(InternalError(_)) => StatusCode.InternalServerError + case Some(_) => StatusCode.InternalServerError + } + } +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDEndpoints.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDEndpoints.scala new file mode 100644 index 0000000000..472daf95c2 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDEndpoints.scala @@ -0,0 +1,46 @@ +package io.iohk.atala.castor.controller + +import io.iohk.atala.api.http.codec.CustomMediaTypes +import io.iohk.atala.api.http.codec.DIDCodec.{didJsonLD, didResolutionJsonLD} +import io.iohk.atala.api.http.{ErrorResponse, RequestContext} +import sttp.model.{Header, StatusCode} +import sttp.tapir.* +import io.iohk.atala.castor.controller.http.{DIDResolutionResult, DIDInput} +import sttp.tapir.json.zio.jsonBody + +object DIDEndpoints { + + // MUST conform to https://w3c-ccg.github.io/did-resolution/#bindings-https + val getDID: PublicEndpoint[ + String, + Nothing, + (StatusCode, DIDResolutionResult), + Any + ] = infallibleEndpoint.get + .in("dids" / DIDInput.didRefPathSegment) + .out( + statusCode + .description(StatusCode.Ok, "The resolution result or W3C DID document representation") + .description(StatusCode.BadRequest, "Invalid DID or DID URL") + .description(StatusCode.NotFound, "The DID is not found") + .description(StatusCode.NotAcceptable, "The DID document representation is not supported") + .description(StatusCode.Gone, "The DID is deactivated") + .description(StatusCode.NotImplemented, "The DID method is not supported") + .description(StatusCode.InternalServerError, "Internal error") + .and( + oneOf[DIDResolutionResult]( + oneOfVariant(stringBodyUtf8AnyFormat(didResolutionJsonLD)), + oneOfVariant(stringBodyUtf8AnyFormat(didJsonLD)), + ) + ) + ) + .name("getDID") + .summary("Resolve Prism DID to a W3C representation") + .description("""Resolve Prism DID to a W3C DID document representation. + |The response can be the [DID resolution result](https://w3c-ccg.github.io/did-resolution/#did-resolution-result) + |or [DID document representation](https://www.w3.org/TR/did-core/#representations) depending on the `Accept` request header. + |The response is implemented according to [resolver HTTP binding](https://w3c-ccg.github.io/did-resolution/#bindings-https) in the DID resolution spec. + |""".stripMargin) + .tag("DID") + +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDServerEndpoints.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDServerEndpoints.scala new file mode 100644 index 0000000000..a392a82487 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/DIDServerEndpoints.scala @@ -0,0 +1,27 @@ +package io.iohk.atala.castor.controller + +import io.iohk.atala.api.http.RequestContext +import sttp.tapir.ztapir.* +import zio.* + +class DIDServerEndpoints(didController: DIDController) { + + private val getDIDServerEndpoint: ZServerEndpoint[Any, Any] = + DIDEndpoints.getDID.zServerLogic { didRef => + didController.getDID(didRef) + } + + val all: List[ZServerEndpoint[Any, Any]] = List( + getDIDServerEndpoint + ) + +} + +object DIDServerEndpoints { + def all: URIO[DIDController, List[ZServerEndpoint[Any, Any]]] = { + for { + didController <- ZIO.service[DIDController] + didEndpoints = new DIDServerEndpoints(didController) + } yield didEndpoints.all + } +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocument.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocument.scala new file mode 100644 index 0000000000..e9a51827c6 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocument.scala @@ -0,0 +1,78 @@ +package io.iohk.atala.castor.controller.http + +import io.iohk.atala.api.http.Annotation +import io.iohk.atala.castor.core.model.did.w3c +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonEncoder, JsonDecoder} +import io.iohk.atala.castor.controller.http.DIDDocument.annotations + +@description("A W3C compliant Prism DID document representation.") +final case class DIDDocument( + @description(annotations.`@context`.description) + `@context`: Seq[String], + @description(annotations.id.description) + @encodedExample(annotations.id.example) + id: String, + @description(annotations.controller.description) + @encodedExample(annotations.controller.example) + controller: Option[String] = None, + verificationMethod: Option[Seq[VerificationMethod]] = None, + authentication: Option[Seq[String]] = None, + assertionMethod: Option[Seq[String]] = None, + keyAgreement: Option[Seq[String]] = None, + capabilityInvocation: Option[Seq[String]] = None, + capabilityDelegation: Option[Seq[String]] = None, + service: Option[Seq[Service]] = None +) + +object DIDDocument { + + object annotations { + object `@context` + extends Annotation[Seq[String]]( + description = "The JSON-LD context for the DID resolution result.", + example = Seq("https://www.w3.org/ns/did/v1") + ) + + object id + extends Annotation[String]( + description = """[DID subject](https://www.w3.org/TR/did-core/#did-subject). + |The value must match the DID that was given to the resolver.""".stripMargin, + example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" + ) + + object controller + extends Annotation[String]( + description = "[DID controller](https://www.w3.org/TR/did-core/#did-controller)", + example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" + ) + } + + given encoder: JsonEncoder[DIDDocument] = DeriveJsonEncoder.gen[DIDDocument] + given decoder: JsonDecoder[DIDDocument] = DeriveJsonDecoder.gen[DIDDocument] + given schema: Schema[DIDDocument] = Schema.derived + + given Conversion[w3c.DIDDocumentRepr, DIDDocument] = (didDocument: w3c.DIDDocumentRepr) => { + DIDDocument( + `@context` = didDocument.context, + id = didDocument.id, + controller = Some(didDocument.controller), + verificationMethod = Some(didDocument.verificationMethod.map(i => i)), + authentication = Some(didDocument.authentication.map(toPublicKeyRef)), + assertionMethod = Some(didDocument.assertionMethod.map(toPublicKeyRef)), + keyAgreement = Some(didDocument.keyAgreement.map(toPublicKeyRef)), + capabilityInvocation = Some(didDocument.capabilityInvocation.map(toPublicKeyRef)), + capabilityDelegation = Some(didDocument.capabilityDelegation.map(toPublicKeyRef)), + service = Some(didDocument.service.map(i => i)) + ) + } + + // TODO: support embedded public key + private def toPublicKeyRef(publicKeyReprOrRef: w3c.PublicKeyReprOrRef): String = { + publicKeyReprOrRef match { + case s: String => s + case pk => throw Exception("Embedded public key is not yet supported by PRISM DID.") + } + } +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocumentMetadata.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocumentMetadata.scala new file mode 100644 index 0000000000..95219da3d0 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDDocumentMetadata.scala @@ -0,0 +1,51 @@ +package io.iohk.atala.castor.controller.http + +import io.iohk.atala.api.http.Annotation +import io.iohk.atala.castor.core.model.did.w3c +import sttp.tapir.Schema +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonEncoder, JsonDecoder} +import sttp.tapir.Schema.annotations.{description, encodedExample} +import io.iohk.atala.castor.controller.http.DIDDocumentMetadata.annotations + +@description("[DID document metadata](https://www.w3.org/TR/did-core/#did-document-metadata)") +final case class DIDDocumentMetadata( + @description(annotations.deactivated.description) + @encodedExample(annotations.deactivated.example) + deactivated: Option[Boolean] = None, + @description(annotations.canonicalId.description) + @encodedExample(annotations.canonicalId.example) + canonicalId: Option[String] = None +) + +object DIDDocumentMetadata { + + object annotations { + object deactivated + extends Annotation[Boolean]( + description = + "If a DID has been deactivated, DID document metadata MUST include this property with the boolean value true. If a DID has not been deactivated, this property is OPTIONAL, but if included, MUST have the boolean value false.", + example = false + ) + + object canonicalId + extends Annotation[String]( + description = """ + |A DID in canonical form. + |If a DID is in long form and has been published, DID document metadata MUST contain a `canonicalId`` property with the short form DID as its value. + |If a DID in short form or has not been published, DID document metadata MUST NOT contain a `canonicalId` property. + |""".stripMargin, + example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" + ) + } + + given encoder: JsonEncoder[DIDDocumentMetadata] = DeriveJsonEncoder.gen[DIDDocumentMetadata] + given decoder: JsonDecoder[DIDDocumentMetadata] = DeriveJsonDecoder.gen[DIDDocumentMetadata] + given schema: Schema[DIDDocumentMetadata] = Schema.derived + + given Conversion[w3c.DIDDocumentMetadataRepr, DIDDocumentMetadata] = + (didDocumentMetadata: w3c.DIDDocumentMetadataRepr) => + DIDDocumentMetadata( + deactivated = Some(didDocumentMetadata.deactivated), + canonicalId = Some(didDocumentMetadata.canonicalId) + ) +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDInput.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDInput.scala new file mode 100644 index 0000000000..4da2fa8561 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDInput.scala @@ -0,0 +1,13 @@ +package io.iohk.atala.castor.controller.http + +import sttp.tapir.* + +object DIDInput { + + val didRefPathSegment = path[String]("didRef") + .description( + "Prism DID according to [the Prism DID method syntax](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#prism-did-method-syntax)" + ) + .example("did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff") + +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDResolutionMetadata.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDResolutionMetadata.scala new file mode 100644 index 0000000000..b680ff2ef6 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDResolutionMetadata.scala @@ -0,0 +1,48 @@ +package io.iohk.atala.castor.controller.http + +import io.iohk.atala.api.http.Annotation +import io.iohk.atala.castor.controller.http.DIDResolutionMetadata.annotations +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonEncoder, JsonDecoder} + +@description("[DID resolution metadata](https://www.w3.org/TR/did-core/#did-resolution-metadata)") +final case class DIDResolutionMetadata( + @description(annotations.error.description) + @encodedExample(annotations.error.example) + error: Option[String] = None, + @description(annotations.errorMessage.description) + @encodedExample(annotations.errorMessage.example) + errorMessage: Option[String] = None, + @description(annotations.contentType.description) + @encodedExample(annotations.contentType.example) + contentType: Option[String] = None +) + +object DIDResolutionMetadata { + + object annotations { + object error + extends Annotation[String]( + description = + "Resolution error constant according to [DID spec registries](https://www.w3.org/TR/did-spec-registries/#error)", + example = "invalidDid" + ) + + object errorMessage + extends Annotation[String]( + description = "Resolution error message", + example = "The initialState does not match the suffix" + ) + + object contentType + extends Annotation[String]( + description = "The media type of the returned DID document", + example = "application/did+ld+json" + ) + } + + given encoder: JsonEncoder[DIDResolutionMetadata] = DeriveJsonEncoder.gen[DIDResolutionMetadata] + given decoder: JsonDecoder[DIDResolutionMetadata] = DeriveJsonDecoder.gen[DIDResolutionMetadata] + given schema: Schema[DIDResolutionMetadata] = Schema.derived +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDResolutionResult.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDResolutionResult.scala new file mode 100644 index 0000000000..cdc454f5db --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/DIDResolutionResult.scala @@ -0,0 +1,30 @@ +package io.iohk.atala.castor.controller.http + +import io.iohk.atala.api.http.Annotation +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonEncoder, JsonDecoder} +import io.iohk.atala.castor.controller.http.DIDResolutionResult.annotations + +final case class DIDResolutionResult( + @description(annotations.`@context`.description) + @encodedExample(annotations.`@context`.example) + `@context`: String, + didDocument: Option[DIDDocument] = None, + didDocumentMetadata: DIDDocumentMetadata, + didResolutionMetadata: DIDResolutionMetadata +) + +object DIDResolutionResult { + object annotations { + object `@context` + extends Annotation[String]( + description = "The JSON-LD context for the DID resolution result.", + example = "https://w3id.org/did-resolution/v1" + ) + } + + given encoder: JsonEncoder[DIDResolutionResult] = DeriveJsonEncoder.gen[DIDResolutionResult] + given decoder: JsonDecoder[DIDResolutionResult] = DeriveJsonDecoder.gen[DIDResolutionResult] + given schema: Schema[DIDResolutionResult] = Schema.derived +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/PublicKeyJwk.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/PublicKeyJwk.scala new file mode 100644 index 0000000000..3502731a7a --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/PublicKeyJwk.scala @@ -0,0 +1,26 @@ +package io.iohk.atala.castor.controller.http + +import io.iohk.atala.castor.core.model.did.w3c +import sttp.tapir.Schema +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonEncoder, JsonDecoder} + +final case class PublicKeyJwk( + crv: Option[String] = None, + x: Option[String] = None, + y: Option[String] = None, + kty: String +) + +object PublicKeyJwk { + given encoder: JsonEncoder[PublicKeyJwk] = DeriveJsonEncoder.gen[PublicKeyJwk] + given decoder: JsonDecoder[PublicKeyJwk] = DeriveJsonDecoder.gen[PublicKeyJwk] + given schema: Schema[PublicKeyJwk] = Schema.derived + + given Conversion[w3c.PublicKeyJwk, PublicKeyJwk] = (publicKeyJwk: w3c.PublicKeyJwk) => + PublicKeyJwk( + crv = Some(publicKeyJwk.crv), + x = Some(publicKeyJwk.x), + y = Some(publicKeyJwk.y), + kty = publicKeyJwk.kty + ) +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/Service.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/Service.scala new file mode 100644 index 0000000000..95b1ab68a8 --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/Service.scala @@ -0,0 +1,60 @@ +package io.iohk.atala.castor.controller.http + +import io.iohk.atala.castor.core.model.did.w3c +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonEncoder, JsonDecoder} +import io.iohk.atala.api.http.Annotation +import io.iohk.atala.castor.controller.http.Service.annotations + +@description("A service expressed in the DID document. https://www.w3.org/TR/did-core/#services") +final case class Service( + @description(annotations.id.description) + @encodedExample(annotations.id.example) + id: String, + @description(annotations.`type`.description) + @encodedExample(annotations.`type`.example) + `type`: String, + @description(annotations.serviceEndpoint.description) + serviceEndpoint: Seq[String] +) + +object Service { + + object annotations { + object id + extends Annotation[String]( + description = """The id of the service. + |Requires a URI fragment when use in create / update DID. + |Returns the full ID (with DID prefix) when resolving DID""".stripMargin, + example = "service-1" + ) + + object `type` + extends Annotation[String]( + description = + "Service type. Can contain multiple possible values as described in the [Create DID operation](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#create-did) under the construction section.", + example = "LinkedDomains" + ) + + object serviceEndpoint + extends Annotation[Seq[String]]( + description = + "The service endpoint. Can contain multiple possible values as described in the [Create DID operation]", + example = Seq("https://example.com") + ) + } + + given encoder: JsonEncoder[Service] = DeriveJsonEncoder.gen[Service] + given decoder: JsonDecoder[Service] = DeriveJsonDecoder.gen[Service] + given schema: Schema[Service] = Schema + .derived[Service] + .modify(_.serviceEndpoint)(_.copy(isOptional = false)) + + given Conversion[w3c.ServiceRepr, Service] = (service: w3c.ServiceRepr) => + Service( + id = service.id, + `type` = service.`type`, + serviceEndpoint = service.serviceEndpoint + ) +} diff --git a/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/VerificationMethod.scala b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/VerificationMethod.scala new file mode 100644 index 0000000000..38d978a5ad --- /dev/null +++ b/prism-agent/service/server/src/main/scala/io/iohk/atala/castor/controller/http/VerificationMethod.scala @@ -0,0 +1,58 @@ +package io.iohk.atala.castor.controller.http + +import io.iohk.atala.api.http.Annotation +import io.iohk.atala.castor.core.model.did.w3c +import sttp.tapir.Schema +import sttp.tapir.Schema.annotations.{description, encodedExample} +import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonEncoder, JsonDecoder} +import io.iohk.atala.castor.controller.http.VerificationMethod.annotations + +@description( + "A cryptographic public key expressed in the DID document. https://www.w3.org/TR/did-core/#verification-methods" +) +final case class VerificationMethod( + @description(annotations.id.description) + @encodedExample(annotations.id.example) + id: String, + @description(annotations.`type`.description) + @encodedExample(annotations.`type`.example) + `type`: String, + @description(annotations.controller.description) + @encodedExample(annotations.controller.example) + controller: String, + publicKeyJwk: PublicKeyJwk +) + +object VerificationMethod { + object annotations { + object id + extends Annotation[String]( + description = "The identifier for the verification method.", + example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff#key-1" + ) + + object `type` + extends Annotation[String]( + description = "The type of the verification method.", + example = "JsonWebKey2020" + ) + + object controller + extends Annotation[String]( + description = "The DID that controls the verification method.", + example = "did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff" + ) + } + + given encoder: JsonEncoder[VerificationMethod] = DeriveJsonEncoder.gen[VerificationMethod] + given decoder: JsonDecoder[VerificationMethod] = DeriveJsonDecoder.gen[VerificationMethod] + given schema: Schema[VerificationMethod] = Schema.derived + + given Conversion[w3c.PublicKeyRepr, VerificationMethod] = (publicKey: w3c.PublicKeyRepr) => + VerificationMethod( + id = publicKey.id, + `type` = publicKey.`type`, + controller = publicKey.controller, + publicKeyJwk = publicKey.publicKeyJwk + ) +} diff --git a/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt b/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt index f7dd2182d2..becd45dfc5 100644 --- a/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt +++ b/tests/e2e-tests/src/main/kotlin/api_models/DidResolutionResult.kt @@ -11,7 +11,8 @@ data class DidDocument( var `@context`: List? = null, var assertionMethod: List? = null, var authentication: List? = null, - var capabilityInvocation: List? = null, + var capabilityInvocation: List? = null, + var capabilityDelegation: List? = null, var controller: String? = null, var id: String? = null, var keyAgreement: List? = null,