Logan Girvin
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    Identity Hub Request and Response Formats === Goals --- This document hopes to revise and refine identity hub requests and response formats with a set of features that are needed for pretty much any identity hub use case. The formats described in this document are taken from and/or inspired by the OData standard for building RESTful APIs. We also took ideas from REST API design best practices around the web, as well as popular web APIs that are considered best-in-industry. OData saves us from "having to worry about the various approaches to define request and response headers, status codes, HTTP methods, URL conventions, media types, value formats, query options, etc. OData also provides guidance for tracking changes, defining functions/actions for reusable procedures, and sending asynchronous/batch requests." With that said, identity hubs are not REST APIs. For instance: - REST APIs assumes there is a finite, fixed set of resources (data types/objects) that are exposed by a service and can be described in something like a CSDL schema. Instead, we want hubs to support any resource, where each resource is described using JSON-LD, Turtle, or another RDF serialization. - REST APIs assume requests and responses always take place over HTTP. Instead, we want the interface to identity hubs to be valid over other transport protocols as well. - REST API request and response metadata is communicated in HTTP headers, URL query parameters, and URL fragments. We are concerned that even over TLS secured HTTP connections, an attacker may be able to compromise the privacy of a message. Instead, we want to ensure all privacy-sensitive metadata is contained in the body of a request, which can be encrypted with the DID keys of the message recipient. These differences force us to significantly depart from REST API design and the OData standard. As a result, we'll lose some advantages of the OData standard, such as automatic integration with OData clients and the use of client library generation tools. We can still borrow and/or adapt many of OData's patterns and conventions to serve the needs of identity hubs. There's a lot to cover here, so we'll begin with the most pressing issues and develop this document over time to meet new requirements. This document currently includes: 1. Base Schemas 2. Basic Request & Response Formats 3. Authentication 4. Authorization 5. Error Codes 6. Data Formats & Encodings 7. Filtering 8. Versioning 9. Paging 10. Extensibility 11. Serving Hubs over HTTP 12. Future Work Each of these sections is a basic proposal for a v1 implementation of identity hubs. Each section will require more detail and richness to be added over time. Base Schemas --- A hub request is represented by a JSON object: ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "BaseRequest", "iss": "did:example:123456", # the sender of the request "aud": "did:example:some-hub", # the hub deployment that recieves the request "sub": "did:example:abc123", # the DID that owns the target hub } ``` A hub response is a JSON object as well: ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "BaseResponse", "developer_message": "hello" # optional informational message } ``` ## Basic Requests & Responses TODO: A blurb about how all objects in hubs are represented as a series of commits. ### Write Requests To create an object, the client constructs and signs a commit, then sends it to a hub for storage: #### Object creation request ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "WriteRequest", "iss": "did:example:123456", "aud": "did:example:some-hub", "sub": "did:example:abc123", # each commit is a JWT using JSON serialization # the JWT contents are displayed below in decoded form "commit": { # commit metadata, included in signature computation "protected": { "interface": "Collections", "context": "http://schema.org", "type": "MusicPlaylist", "operation": "create", # must be one of {create, update, delete} "committed_at": "2018-10-24T18:39:10.10:00Z", "commit_strategy": "basic", # indicates the structure of commits "sub": "did:example:abc123", # the DID that the object is owned by "kid": "did:example:123456#key-abc", "meta": { "name": "Sample playlist", # "tags": [] } } # commit metadata, not included in signature computation "header": { # the rev is the unique ID for this commit, it is # the hash of the protected & payload portions "rev": "3a9de008f526d239...", # the DID that created this commit. This must validate against the kid "iss": "did:example:123456" }, # commit contents, format specified by commit_strategy "payload": { "@context": "http://identity.foundation", "@type": "MusicPlaylist", "@id": "foo", "name": "A playlist", "tracks": [] }, # can be optionally used for non-repudiation of commits "signature": "j3irpj90af992l..." } } ``` > The commit revision algorithm comes from the [storage and replication](https://hackmd.io/EesqN9cXQEKEeXamCR90BA?view#Commits) proposal. #### Object creation response (success) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "WriteResponse", "developer_message": "completely optional", "revisions": ["3a9de008f526d239..."] } ``` #### Object creation response (error) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "ErrorResponse", "error_code": "ABC123", # A standard value that can be handled in code "error_url": "https://mydocumentation.com/errors/ABC123" # more info "developer_message": "A helpful description for a developer", "user_message": "Completely optional", # can be used for error messages "target": "email", # the property in the request that caused error "inner_error": { # a custom object specific to the hub provider "request_id": "", "timestamp": "", "trace": "", "custom-field": "", } } ``` To update an object, you send another commit that modifies the object: #### Object update request ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "WriteRequest", "iss": "did:example:123456", "aud": "did:example:some-hub", "sub": "did:example:abc123", # each commit is a JWT using JSON serialization # the JWT contents are displayed below in decoded form "commit": { "protected": { "interface": "Collections", "context": "http://schema.org", "type": "MusicPlaylist", "operation": "update", # must be update for an update request "committed_at": "2018-10-24T18:39:10.10:00Z", # the object this commit modifies # each object's ID is the rev of its create commit "object_id": "3a9de008f526d239...", "commit_strategy": "basic", "sub": "did:example:abc123", "kid": "did:example:123456#key-abc", "meta": { "name": "Sample playlist", # "tags": [] } }, "header": { "rev": "fe4fd3240ff1c68a...", "iss": "did:example:123456" }, "payload": { "@context": "http://identity.foundation", "@type": "MusicPlaylist", "@id": "foo", "name": "A modified playlist", "tracks": [] }, "signature": "j3irpj90af992l..." } } ``` #### Object update response (success) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "WriteResponse", "developer_message": "completely optional", "revisions": [ # all known commits for this object "fe4fd3240ff1c68a...", "3a9de008f526d239..." ] } ``` #### Object update response (error) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "ErrorResponse", "error_code": "ABC123", "error_url": "https://mydocumentation.com/errors/ABC123", "developer_message": "A helpful description for a developer", "user_message": "Completely optional", "target": "email", "inner_error": { "request_id": "", "timestamp": "", "trace": "", "custom-field": "", } } ``` To delete an object from a hub entirely, you send another commit that deletes the object: #### Object delete request ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "WriteRequest", "iss": "did:example:123456", "aud": "did:example:some-hub", "sub": "did:example:abc123", # each commit is a JWT using JSON serialization # the JWT contents are displayed below in decoded form "commit": { "protected": { "interface": "Collections", "context": "http://schema.org", "type": "MusicPlaylist", "operation": "delete", # must be delete for a delete request "committed_at": "2018-10-24T18:39:10.10:00Z", "object_id": "3a9de008f526d239...", # the object this commit deletes "commit_strategy": "basic", "sub": "did:example:abc123", "kid": "did:example:123456#key-abc" } "header": { "rev": "cc2bd8f09bb88b5d...", "iss": "did:example:123456" }, # different commit strategies might use different values here "payload": { }, "signature": "j3irpj90af992l..." } } ``` #### Object delete response (success) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "WriteResponse", "developer_message": "completely optional", "revisons": [ "cc2bd8f09bb88b5d...", "fe4fd3240ff1c68a...", "3a9de008f526d239..." ] } ``` #### Object delete response (error) ```jsonld= # Error Response { "@context": "https://schema.identity.foundation/0.1", "@type": "ErrorResponse", "error_code": "ABC123", "error_url": "https://mydocumentation.com/errors/ABC123", "developer_message": "A helpful description for a developer", "user_message": "Completely optional", "target": "email", "inner_error": { "request_id": "", "timestamp": "", "trace": "", "custom-field": "", } } ``` ### Querying objects To read data from a hub, a client will likely begin by querying the available objects. The `Object/Query` request accepts parameters for searching for available objects in a user's hub. #### Object query request ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "ObjectQueryRequest", "iss": "did:example:123456", "aud": "did:example:some-hub", "sub": "did:example:abc123", "query": { "interface": "Collections", "context": "http://schema.org", "type": "MusicPlaylist", # Optional "object_id": ["3a9de008f526d239..", "a8f3e7..."] } } ``` #### Object query response (success) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "ObjectQueryResponse", "developer_message": "completely optional", "objects": [ { # object metadata "interface": "Collections", "context": "http://schema.org", "type": "MusicPlaylist", "id": "3a9de008f526d239...", "created_by": "did:example:abc123", "created_at": "2018-10-24T18:39:10.10:00Z", "[other metadata fields]": "from storage specification", "sub": "did:example:abc123", "commit_strategy": "basic", }, # ...more objects ] } ``` #### Object query response (error) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "ErrorResponse", "developer_message": "A helpful description for a developer", "error_code": "ABC123", "error_url": "https://mydocumentation.com/errors/ABC123" "user_message": "Completely optional", "target": "email", "inner_error": { "request_id": "", "timestamp": "", "trace": "", "[custom-field]": "", } } ``` ### Reading a single object Once a specific object of interest has been identified, you can submit a request to read all of the commits for that object. The client can then apply its commit strategy logic to produce the current state of the object based on the commits. Currently the only supported fields in the `query` object are `object_id` (to retrieve all commits for an object) and `revisions` (to retrieve a specific list of revisions). Further options will be added in the future - see the Filtering section below. The `fields` object allows the query to return only a subset of information. This is useful for discovering new commits to an object which has been synced previously. #### Single object read request ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "CommitQueryRequest", "iss": "did:example:123456", "aud": "did:example:some-hub", "sub": "did:example:abc123", "query": { "object_id": ["3a9de008f526d239..."], "revision": ["abc", "def", ...] }, "fields": ["rev"] } ``` #### Single object read response (success) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "CommitQueryResponse", "developer_message": "completely optional", "commits": [ { "protected": { "interface": "Collections", "context": "http://schema.org", "type": "MusicPlaylist", "operation": "create", "committed_at": "2018-10-24T18:39:10.10:00Z", "commit_strategy": "basic", "sub": "did:example:abc123", "kid": "did:example:123456#key-abc", "meta": { "name": "Sample playlist", # "tags": [] } }, "header": { "rev": "3a9de008f526d239...", "iss": "did:example:123456" }, "payload": { "@context": "http://identity.foundation", "@type": "MusicPlaylist", "@id": "foo", "name": "A playlist", "tracks": [] }, "signature": "j3irpj90af992l..." }, { "protected": { "context": "http://schema.org", "type": "MusicPlaylist", "operation": "update", "committed_at": "2018-10-24T18:39:10.10:00Z", "object_id": "3a9de008f526d239...", "commit_strategy": "basic", "sub": "did:example:abc123", "kid": "did:example:123456#key-abc", "meta": { "name": "Sample playlist", # "tags": [] } } "header": { "rev": "fe4fd3240ff1c68a...", "iss": "did:example:123456" }, "payload": { "@context": "http://identity.foundation", "@type": "MusicPlaylist", "@id": "foo", "name": "A modified playlist", "tracks": [] }, "signature": "j3irpj90af992l..." }, # ... }, # pagination fields here } ``` #### Single object read response (error) ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "ErrorResponse", "error_code": "ABC123", "error_url": "https://mydocumentation.com/errors/ABC123" "developer_message": "A helpful description for a developer", "user_message": "Completely optional", "target": "email", "inner_error": { "request_id": "", "timestamp": "", "trace": "", "custom-field": "", } } ``` > [name=Danny Strockis] In the future we may consider introducing a "partition" concept, where we ask the client to send Commits/Read requests to each partition individually, to make it easier for hub implementations to return all commits. Authentication --- All requests and responses above are encoded into a JWE format that is both signed and encrypted by the sender using the DID keys of the hub and the client. This allows the hub to authenticate the client and secure any messages exchanged. Documentation on hub authentication [is available here](https://github.com/decentralized-identity/did-auth-jose/blob/master/docs/Authentication.md). Authorization --- Client authorization to restrict access to various API calls and data in a hub is described in full [in this authorization document](https://hackmd.io/j_hVcMyNRAmDrTN2E9gUIw). Error Codes --- When apps read and write data from identity hubs, they need to be able to support any hub provider the user chooses. Hub implementations should therefore return a finite and consistent set of error cases that can be handled by developers. The table below enumerates a set of errors that hub implementations can return. Hub providers are able to add their own custom, non-standard error codes and debug information in the `inner_error` object. Our design principles for introducing new error codes are as follows: - We strive to have as few unique error codes as possible. - Apps are expected to react differently to each error code. So new error codes should only be introduced when a developer needs to handle an error case differently from other error cases. - No app should ever need to parse the `developer_message`, `user_message`, or `inner_error` fields to handle an error case. - The `developer_message`, `user_message`, and `inner_error` fields are provided to assist in debugging. | Error Code | Description | | -------- | -------- | | `bad_request` | The request could not be completed because it was malformed. Cases include:<br/>- the request JWE could not be decrypted<br/>- the response JWE could not encrypted<br/>- a required property was missing<br/>- an invalid value was provided in request<br/>| | `authentication_failed` | The client could not be authenticated, becuase the request JWS could not be verified using the DID keys of the DID provided in the `iss` property. | | `permissions_required` | The request is denied because the DID in the `iss` field has not be granted sufficient permission to perform the operation. | | `not_found` | The resource requested could not be found in the hub. | | `too_many_requests` | The client is being throttled, and should wait before retrying any requests. | | `server_error` | An unexpected or unhandled error occurred on the hub. | | `not_implemented` | The operation requested includes a standard hub feature that is not supported by this implementation of the hub. | | `service_unavailable` | The hub service is temporarily not available and reqeusts should not be retried. | | `temporarily_unavailable` | The hub service experienced a transient error and the request should be retried at a later time. | We'll add more error cases over time as we discover cases that need to be handled by client applications. Data Formats & Encodings --- At this time, the only data format supported by hubs is JSON objects. JSON objects are encapsulated into JWSs and JWEs according to the [Authentication](#Authentication) section. Over HTTP, requests use the media types described in the [Serving requests over HTTP](#Serving-hub-requests-over-HTTP). Images, videos, streams, and other MIME types are not currently supported. Filtering --- Read requests for multiple resources can include additional criteria that scope down the set of resources that should be returned: ```jsonld= Request { "@context": "https://schema.identity.foundation/0.1", "@type": "ObjectQueryRequest", "iss": "did:example:123456", "aud": "did:example:some-hub", "sub": "did:example:abc123", "interface": "Collections", "query": { "context": "http://schema.org", "type": "MusicPlaylist", "filters": [ { "type": "eq", "field": "{metadata-property-name}", "value": "{value}" } ] } } ``` Currently filtering is only supported via "shortcut" properties directly on the `query` field. These properties allow filtering based on specific values of known static object properties. In the future, we plan to support more advanced filtering via an array of `filters` objects in the `query` field. Currently the only filter supported by identity hubs is the `eq` filter which works against a single property of an object or its metadata. Logical combinations like `and` and `or` are not supported at this time. The format and meaning of the filter string value is taken directly from the [OData v4.0 specifications](http://docs.oasis-open.org/odata/odata/v4.01/cs01/part2-url-conventions/odata-v4.01-cs01-part2-url-conventions.html#_Toc505773218). > TODO: rewrite the paragraph on filters Versioning --- We expect that the format for hub requests and responses will evolve over time to support new developer scenarios and user requirements. The hub request/response standard will always strive to introduce changes in a way that does not break existing clients, and remains compatible with deployments of identity hubs on previous versions. With that said, the hub standard needs a mechanism that allows breaking changes to occur if absolutely necessary. Hub API versioning will be implemented using semantic versioning in the URI of `https://schema.identity.foundation` schema, as displayed below. This implies that all objects described by this schema will need to be revised together - the `ReadRequest`, `WriteRequest`, `BaseRequest`, `BaseResponse`, `MultiObjectResponse`, `ErrorResponse`, and so forth. It also includes the format of the JWT, JWS, and JWEs that are used to encapsulate hub requests and responses. Any change to the JWE version should be considered a breaking change to the hub API. WriteRequest ```jsonld= { "@context": "https://schema.identity.foundation/0.1", "@type": "BaseRequest", "iss": "did:example:123456", "aud": "did:example:some-hub", "sub": "did:example:abc123", } ``` Minor version increments should contain only non-breaking changes such as property additions and other object extensions. Any breaking changes, such as property renames, object restructuring, or data type modifications must only occur in major version increments. Introducing a new major version is a radical change. Because of the decentralized nature of the standard, a new version will take significant amount of time to be implemented and adopted by all hub deployments and client applications. Therefore major versions should be supported by hub deployments for a long period of time, on the order of years. Introducing a new major version is not a decision to be taken lightly. Hub API versioning does not pertain to the the versions of the objects stored by hubs that are described outside of the `https://schema.identity.foundation` schema. These objects can implement their own versioning strategies in accordance with JSON-LD or whichever data format they choose to adhere to. As new versions get introduced, different hub deployments may adopt/deprecate versions at different rates. We therefore need a way for a hub to declare to clients which versions of the hub request/response APIs it supports. As a refresher, here is the (unmodified) format of the DID document for an identity hub provider (**not** the DID document for a user): ```jsonld // Partial identity hub DID Document: ... "service": [{ "type": "IdentityHub", "publicKey": "did:btcs:456#key-1", "serviceEndpoint": { "@context": "https://schema.identity.foundation/1.0/hub", "@type": "HostServiceEndpoint", "locations": ["https://hub.azure.com/"] } }] ... ``` To declare supported versions, hubs can use a `location` value with the suffix `/.well-known/hub-configuration`: ```jsonld // Partial identity hub DID Document: ... "service": [{ "type": "IdentityHub", "publicKey": "did:btcs:456#key-1", "serviceEndpoint": { "@context": "https://schema.identity.foundation/1.0/hub", "@type": "HostServiceEndpoint", "locations": ["https://hub.azure.com/.well-known/hub-configuration"] } }] ... ``` When clients see this suffix, they should fetch a simple JSON "configuraiton" document at the URL. We've chosen to host this hub configuration information in a dedicated document (rather than directly in the DID document) in an effort to keep the size of the DID document at a minimum. The configuration document will take the following format: ```jsonld= { "@context": "https://schema.identity.foundation/1.0/hub", "@type": "IdentityHubConfiguration", "endpoints": [ { "@context": "http://schema.identity.foundation/0.1/hub", "@type": "InterfaceMap", "interfaces": { ... } }, { "@context": "http://schema.identity.foundation/1.4/hub", "@type": "InterfaceMap", "interfaces": { ... } }], "signature": { "kid": "did:btcs:456#key-1", "value": "jv9323lavjsdav0d9ada2...", "timestamp": "2018-10-24T18:39:10.10:00Z" } } ``` Each entry in the `values` JSON array indicates a protocol version that is supported. The version is indicated in the `@context` value, which references a specific hub request/response API format. A discovery document should contain a maximum of one entry per major version. By parsing a hub configuration document, a client who does not support a hub's version or interfaces will fail faster, during parsing, not during a request. The configuration document can be optionally signed using one of the hub's DID keys so that its integrity can be verified. Clients may, but do not need to, throw an error if a signature is absent. The rest of the content of this discovery document is described in the sections below. Paging --- An identity hub may use pagenation in `MultiObjectResponse`s. Identity Hubs may define a maximum page size; if the returned results exceeds this size, an `skip_token` key value is included. The `skip_token` value should be treated as opaque by the client. A paged result from a read reques:t ```jsonld= Success Response { "@context": "https://schema.identity.foundation/0.1", "@type": "ObjectQueryResponse", # multiple results return pagination "developer_message": "completely optional", "objects": [ { # object metadata "context": "http://schema.org", "type": "MusicPlaylist", "id": "3a9de008f526d239...", "created_at": "2018-10-24T18:39:10.10:00Z", "[other metadata fields]": "from storage specification", "sub": "did:example:abc123", "commit_strategy": "basic", }, ... ], "skip_token": "ajfl43241nnn1p;u9390", } ``` To use a `skip_token` key value to get additional results, include the `skip_token` key and key value in the `query` property. If a response does not contain a `skip_token`, then there are no more results for the client to fetch from the hub. A read request to a hub's collection interface that uses the `skip_token` property: ```jsonld= Request { "@context": "https://schema.identity.foundation/0.1", "@type": "ObjectQueryRequest", "iss": "did:example:123456", "aud": "did:example:some-hub", "sub": "did:example:abc123", "interface": "Collections", # one of {collections, actions, ...} "query": { "context": "http://schema.org", "type": "MusicPlaylist", "skip_token": "ajfl43241nnn1p;u9390" } } ``` > [name=Logan Girvin] Context and Type seem excessive and could lead to improper store implementations (for instance changing the context and type while including the skip token could result in missed or improper results if the skip token is not checked against the query.) Extensibility --- Hub implementations can choose to add additional request and response features that are not part of the standard as needed. In accordance with JSON-LD, clients should ignore any additional properties present in objects that are not specified as part of the schema of `schema.idenity.foundation` objects. For instance, if a hub implementation returns the following response: ```jsonld= Success Response { "@context": "https://schema.identity.foundation/0.1", "@type": "ObjectQueryResponse", "developer_message": "completely optional", "objects": [ { "id": "5f6a8a3c-d22c-43f3-9984-a27ce0557185", "created_at": "2018-10-24T18:39:10.10:00Z", "[other metadata fields]": "from storage specification", "commit_strategy": "basic", "custom-field": { "foo": "bar" } } ] } ``` A client programmed against the standard hub API should not fail. Similarly, if a client includes a non-standard property in the request, a hub implementation should not fail the request. Serving hub requests over HTTP --- While hubs can run in different environments, our first implementation will be a web service exposed over HTTP (and associated client SDKs). This section details how requests and responses can be submitted to such a hub service. To make life easier for large scale hub deployments, we'll first add some richness to the discovery document outlined in the Versioning section above. To communicate with a hub, a client must get the endpoint(s) for a hub from the hub's discovery document as follows: ```jsonld= { "@context": "https://schema.identity.foundation/1.0/hub", "@type": "IdentityHubConfiguration", "endpoints": [{ "@context": "http://schema.identity.foundation/1.0/hub", "@type": "InterfaceMap", "interfaces": { "Collections": "https://hub.azure.com/Collections", "Profile": "https://www.foo.com/.identity/Profile", "Actions": "https://id.bar.org/.identity/Actions", ... } }, { "@context": "http://schema.identity.foundation/1.4/hub", "@type": "InterfaceMap", "interfaces": { "Collections": "https://hub.azure.com/Collections", "Profile": "https://www.foo.com/whatever-path/Profile", "Actions": "https://id.bar.org/.identity/Actions", ... } }], "signature": { "kid": "did:btcs:456#key-1", "value": "jv9323lavjsdav0d9ada2...", "timestamp": "2018-10-24T18:39:10.10:00Z" } } ``` Hub implementations can choose to use a single URL for all interfaces they support, or may have different URLs for each. The format of each interface URL is not specified in this document. This format allows for a hub provider to support multiple versions at different endpoints. Additionally hubs may dispatch interfaces to different paths for microservice routing, or explicitly to the same path for privacy. If the `interface` property or the version of the schema in a request does not match correct URL for a hub deployment, the hub can fail the request with one of the error codes from above. Once a client has discovered the URL(s) for a hub, it can send requests to the hub over HTTP. All requests to a hub are submitted using the HTTP POST verb: ```HTTP POST /identity/0.1/ HTTP/1.1 Host: hub.com Content-Type: application/jose Content-Length: 534 eyJraWQiOiJkaWQ6dGVzdDpodWIuaWQjS... ``` Responses are returned as JWEs as well, and hubs usually use a status code of `200`, but may use other `2**` error code values to indicate success. If a `2**` status code is returned, the response body must contain a properly formed JWE. Note that a `200` does not necessarily indicate a successful hub request; the JWE returned may still contain an error response inside. ```HTTP HTTP/1.1 200 OK Date: Mon, 27 Jul 2018 12:28:53 GMT Content-Length: 501 Content-Type: application/jose Connection: Closed eyJraWQiOiJkaWQ6dGVzdDpodWIuaWJrR... ``` > [name=Danny Strockis] OPEN QUESTION: Brandon thinks we should return RESTful HTTP status codes here, rather than always returning a `200` and then relying on the interior error codes for communicate request status. He also thinks we should adopt POST/PUT/PATCH/GET/DELETE semantics for CRUD operations. Personally I agree, but others are concerned about privacy-related information leakage. A JWE cannot be returned, however, in the following cases: - if the client's DID public keys cannot be resolved. - if the client's request was improperly encrypted, so the client's DID is unknown. - if the client's request was improperly signed, so the client could not be authenticated. - if a server error occurred while retreiving keys or encrypting the JWE response. - if the client and hub do not support the same signing or encryption algorithms. - if the client is being throttled by a reverse proxy/load balancer/gateway. In these cases, the HTTP response is returned with an error status and a JSON formatted response containing additional information. If there was a problem with the client's request, or any problem fetching or using the client's DID keys: ```HTTP HTTP/1.1 400 Bad Request Date: Mon, 27 Jul 2018 12:28:53 GMT Content-Length: 124 Content-Type: application/json Connection: Closed { "error_code": "ABC123", "error_url": "https://mydocumentation.com/errors/ABC123", "developer_message": "Unable to verify signature of JWE in request", "user_message": "Completely optional", "target": null, "inner_error": { "request_id": "", "timestamp": "", "trace": "", "custom-field": "", } } ``` If the server encountered an error during JWE parsing or construction: ```HTTP HTTP/1.1 500 Server Error Date: Mon, 27 Jul 2018 12:28:53 GMT Content-Length: 124 Content-Type: application/json Connection: Closed { "error_code": "ABC123", "error_url": "https://mydocumentation.com/errors/ABC123", "developer_message": "Temporary server error", "user_message": "Completely optional", "target": null, "inner_error": { "request_id": "", "timestamp": "", "trace": "", "custom-field": "", } } ``` If the server is throttling the client due to too many reqeusts: ```HTTP HTTP/1.1 429 Too Many Requests Date: Mon, 27 Jul 2018 12:28:53 GMT Content-Length: 124 Content-Type: application/json Retry-After: 120 Connection: Closed { "error_code": "ABC123", "error_url": "https://mydocumentation.com/errors/ABC123", "developer_message": "Too many requests", "user_message": "Completely optional", "target": null, "inner_error": { "request_id": "", "timestamp": "", "trace": "", "custom-field": "", } } ``` Hubs may also use other `4**` and `5**` status codes to indicate client and server error responses, respectively. When returning error responses outside of JWEs, the hub should be careful that error reponses do not contain information that might violate a user's privacy. Future work --- - Subscribing to changes (delta queries, link to replication docs) - Modeling relationships between entities (navigation links) - Multi-Resource Queries ($expand, GraphQL) - Response Customization ($top, $orderBy, $select, $skip, $take) - Batching - Function/Actions - Async Operations - Computations ($sum, $count)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully