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 |
Table of Contents
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:
Links
- CAIP-122 “Sign-in with X”
- EIP-4361 “Sign-in with Ethereum”
- did:pkh Method Specification
- RFC 3339
- EIP-191: Signed Data Standard
- EIP-1271: Standard Signature Validation Method for Contracts
Copyright
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