CAIP-74: CACAO - Chain Agnostic CApability Object

Author Sergey Ukustov, Haardik
Discussions-To https://github.com/ChainAgnostic/CAIPs/pull/74
Status Review
Type Standard
Created 2021-11-01
Updated 2022-07-12

Simple Summary

Represent a chain-agnostic Object Capability (OCAP), created using CAIP-122, as an IPLD object.

Abstract

In this document we define a way to record the result of CAIP-122 signing operation as an IPLD-based object capability (OCAP). This creates not just an event receipt of an authentication, but also a composable and replay-able authorization receipt for verifiable authorizations, when the message signed contains the appropriate fields. The first CACAO profile was tailored to the ethereum dapps supporting [EIP-4361][] but roughly equivalent profiles for other wallet/dapp ecosystems are being added over time.

Motivation

“Sign-in with X” is a way for a user to authenticate into a service, and provide authorization. In essense, it is a signature of a well-formed payload.

We could see this as a stepping point for a rich capability-based authorization system.

In order to do this, we would like to have a standardized IPLD-based representation of the payload and the signature, that together comprise a capability.

Specification

Container format

We start construction with declaring a container format, that represents a signed payload. It should contain meta-information, payload and signatures. For reference let’s call such container CACAO (for Chain Agnostic CApability Object). We use IPLD schema language to describe the format. Reminder, unless a field is marked optional, it is mandatory.

type CACAO struct {
  h Header // container meta-information
  p Payload // payload
  s Signature // signature, single
}

Header uniquely identifies the payload format:

type Header struct {
  t String // specifies format of the payload
}

The header type will be caip122 in reference to the CAIP-122 specification for the SIWx data model. In an older version of the specification, the header type was restricted to eip4361 as it was designed to work only with Sign-in with Ethereum. As such, newer implementations MUST be able to deal with both header types appropriately.

The payload structure must be presented as follows:

type Payload struct {
  domain String // =domain
  iss String // = DID pkh
  aud String // =uri
  version String
  nonce String
  iat String // RFC3339 date-time =issued-at
  nbf optional String // RFC3339 date-time =not-before
  exp optional String // RFC3339 date-time = expiration-time
  statement optional String // =statement
  requestId optional String // =request-id
  resources optional [ String ] // =resources as URIs
}

It is important to note, that issuer here is did:pkh, which includes both blockchain address and blockchain network information. Also, as per CAIP-122 specificaction,iat, nbf, and exp are encoded as RFC 3339 date-time, which could include milliseconds precision.

The signature in essence is just bytes, but we have to give a hint on how the signature verification should work. The signature verification type is referenced from methods that are listed as possible within the CAIP-122 namespace.

type Signature struct {
  t String
  m optional SignatureMeta
  s Bytes
}

type SignatureMeta struct {
}

This construction allows a dApp to uniformly request a SIWx signature regardless of the user’s account nature.

Signature Verification

Signature signing and verification should follow the workflow as specified in the CAIP-122 namespaces. For example, for eip155 chains, we reconstruct the SIWx payload as follows, resulting in a message conformant with EIP-4361:

{.p.domain} wants you to sign in with your Ethereum account:
{.p.iss[address]}

{.p.statement}

URI: {.p.aud}
Version: {.p.version}
Chain ID: {.p.iss[chainId.reference]}
Nonce: {.p.nonce}
Issued At: {.p.iat}
Resources:
- {.p.resources[0]}
- {.p.resources[1]}
...
- {.p.resources[n]}

Signature verification goes according to t in SignatureMeta: For example,

Serialization

As a proper IPLD object, it can be deterministically serialized using CBOR into bytes. Performance is almost as fast as vanilla JSON serialization. For transport purposes we propose that a CACAO is passed inside a base64url-serialized CAR file, with root of the CAR file set to a tip of capability chain. Here and now we use CARv1 format, as CARv2 is still being worked on.

We propose, that all the necessary parent CACAOs are passed there as well. This way, even if a referenced CACAO is not yet available over IPFS, both consumer and presenter of CACAO still can access it.

Rationale

  • As a chain-agnostic standard, a capability should identify chain-specific signature methods.
  • While “Sign-in with X” standardizes payload format, the payload could be extended in future.
  • The standard should be usable for DID-based signing methods as well as blockchain based ones.
  • The format we are creating here should be uniquely serialized as an IPLD object; we expect it to be identified by CID.
  • A capability format described here should allow chaining capabilities together.
  • We should standardize on a url-safe serialization format of a capability chain suitable for well-established non-binary transport protocols.

Backwards Compatibility

In the previous version of this specification, the header type was restricted to eip4361 as it was designed to work only with Sign-in with Ethereum. Newer implementations should support both header types - eip4361 and caip122.

Example

Below you could find a CACAO, along with its serialized presentation in CAR file.

CACAO:

{
  "h": {
    "t": "eip4361"
  },
  "p": {
    "aud": "http://localhost:3000/login",
    "exp": "2022-03-10T18:09:21.481+03:00",
    "iat": "2022-03-10T17:09:21.481+03:00",
    "iss": "did:pkh:eip155:1:0xBAc675C310721717Cd4A37F6cbeA1F081b1C2a07",
    "nbf": "2022-03-10T17:09:21.481+03:00",
    "nonce": "328917",
    "domain": "localhost:3000",
    "version": "1",
    "requestId": "request-id-random",
    "resources": [
      "ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq",
      "https://example.com/my-web2-claim.json"
    ],
    "statement": "I accept the ServiceOrg Terms of Service: https://service.org/tos"
  },
  "s": {
    "s": "5ccb134ad3d874cbb40a32b399549cd32c953dc5dc87dc64624a3e3dc0684d7d4833043dd7e9f4a6894853f8dc555f97bc7e3c7dd3fcc66409eb982bff3a44671b",
    "t": "eip191"
  }
}

CACAO Serialized: base64url-encoded CARv1 file with the IPFS block of the CACAO above:

uOqJlcm9vdHOB2CpYJQABcRIgEbxa4r0lKwE4Oj8ZUbYCpULmPfgw2g_r12IcKX1CxNlndmVyc2lvbgHdBAFxEiARvFrivSUrATg6PxlRtgKlQuY9-DDaD-vXYhwpfULE2aNhaKFhdGdlaXA0MzYxYXCrY2F1ZHgbaHR0cDovL2xvY2FsaG9zdDozMDAwL2xvZ2luY2V4cHgdMjAyMi0wMy0xMFQxODowOToyMS40ODErMDM6MDBjaWF0eB0yMDIyLTAzLTEwVDE3OjA5OjIxLjQ4MSswMzowMGNpc3N4O2RpZDpwa2g6ZWlwMTU1OjE6MHhCQWM2NzVDMzEwNzIxNzE3Q2Q0QTM3RjZjYmVBMUYwODFiMUMyYTA3Y25iZngdMjAyMi0wMy0xMFQxNzowOToyMS40ODErMDM6MDBlbm9uY2VmMzI4OTE3ZmRvbWFpbm5sb2NhbGhvc3Q6MzAwMGd2ZXJzaW9uAWlyZXF1ZXN0SWRxcmVxdWVzdC1pZC1yYW5kb21pcmVzb3VyY2VzgnhCaXBmczovL2JhZnliZWllbXhmNWFiandqYmlrb3o0bWMzYTNkbGE2dWFsM2pzZ3BkcjRjanIzb3ozZXZmeWF2aHdxeCZodHRwczovL2V4YW1wbGUuY29tL215LXdlYjItY2xhaW0uanNvbmlzdGF0ZW1lbnR4QUkgYWNjZXB0IHRoZSBTZXJ2aWNlT3JnIFRlcm1zIG9mIFNlcnZpY2U6IGh0dHBzOi8vc2VydmljZS5vcmcvdG9zYXOiYXNYQVzLE0rT2HTLtAoys5lUnNMslT3F3IfcZGJKPj3AaE19SDMEPdfp9KaJSFP43FVfl7x-PH3T_MZkCeuYK_86RGcbYXRmZWlwMTkx

Versioning

Present version of CAIP-74 updates and clarifies the previous versions:

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Sergey Ukustov, Haardik, "CAIP-74: CACAO - Chain Agnostic CApability Object," Chain Agnostic Improvement Proposals, no. 74, November 2021. [Online serial]. Available: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-74.md