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,
eip191: use EIP-191 ↗,eip1271: use EIP-1271 ↗.
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]: Date and Time on the Internet: Timestamps
- [EIP-191]: Signed Data Standard
- [EIP-1271]: Standard Signature Validation Method for Contracts
Copyright
Copyright and related rights waived via CC0.