diff --git a/CAIPs/caip-25.md b/CAIPs/caip-25.md index 8b78501e..45b5ba38 100644 --- a/CAIPs/caip-25.md +++ b/CAIPs/caip-25.md @@ -32,25 +32,55 @@ application through a provider connecting to a wallet. ## Specification The session is defined by a wallet's response to a provider's request, and -updated, extended, closed, etc by successive calls and events. These are out of -scope of this CAIP interface and will be specified in a forthcoming one. - -Within that session model, this interface outlines the authorization of an -injected provider per namespace. These authorization call/responses should be -idempotent, assuming the provider is tracking a session property, referred to by -a `sessionIdentifier` as defined in [CAIP-171][]. If a wallet needs to initiate -a new session, whether due to user input, security policy, or session expiry -reasons, it can simply generate a new session identifier to signal this event to -the calling provider. - -The application interfaces with a provider to populate a session with a base -state describing authorized chains, methods, event, and accounts. This -negotation takes place by sending the application's REQUIRED and REQUESTED -properties of the session. If any requirements are not met, a failure response -expressive of one or more specific failure states will be sent (see below). +updated, extended, closed, etc by successive calls and notifications. The exact +parameters and assumptions of that session abstraction are defined in +[CAIP-171][], but note that a string identifier referring to it is absent from +the initial call (if authorization is granted) and present in both the initial +response and all future responses. + +Given the session model of [CAIP-171][], this interface outlines the +authorization of a provider to handle a set of interfaces grouped into +namespaces, as well as to interact with a session abstraction used by both +caller and respondent to manage the authorization over time. The +`sessionIdentifier` defined in [CAIP-171][] enables this mutual management and +alignment across calls that are idempotent if identical. If a respondent (e.g. a +wallet) needs to initiate a new session, whether due to user input, security +policy, or session expiry reasons, it can simply generate a new session +identifier to signal this notification to the calling provider; if a caller +needs to initiate a new session, it can do so by sending a new request without +`sessionIdentifier`. In such cases, a respondent (e.g. wallet) may choose to +explicitly close all sessions upon generation of a new one from the same origin, +or leave it to time-out; maintaining concurrent sessions is discouraged (see +Security Considerations). + +In the initial call, the application interfaces with a provider to populate a +session with a base state describing authorized chains, methods, notification, +and accounts. This negotation takes place by sending the application's REQUIRED +and REQUESTED authorizations of the session, grouped into objects scoping those +authorizations which in turn are grouped into two top-level objects (named +`requiredScopes` and `optionalScopes` respectively). These two objects are not +mutually exclusive (i.e., additional properties of a required scope may be +requested under the same keyed scope object key in the requested object). Note +that scopes can be keyed to an entire [CAIP-104][] "namespace", meaning +applicable to *any* current or future [CAIP-2][] chainID within that namespace, +or keyed to a specific [CAIP-2][] within that namespace. + +If any properties in the required scope(s) are not authorized by the +respondent (e.g. wallet), a failure response expressive of one or more specific +failure states will be sent (see [#### failure states](#failure-states) below), +with the exception of user denying consent. For privacy reasons, an `undefined` +response (or no response, depending on implementation) should be sent to prevent +incentivizing unwanted requests and to minimize the surface for fingerprinting +of public web traffic (See Privacy Considerations below). + Conversely, a succesful response will contain all the required properties *and the provider's choice of the optional properties* expressed as a unified set of -parameters. +parameters. In the case of identically-keyed scopes appearing in both arrays in +the request where properties from both are returned as authorized, the two +scopes MUST be merged in the response (see examples below). However, respondents +MUST NOT restructure scopes (e.g., by folding properties from a [CAIP2][]-keyed, +chain-specific scope object into a [CAIP-104][]-keyed, namespace-wide scope +object) as this may introduce ambiguities (See Security Considerations below). ### Request @@ -63,26 +93,26 @@ Example: { "id": 1, "jsonrpc": "2.0", - "method": "provider_authorization", + "method": "provider_authorize", "params": { - "requiredNamespaces": { + "requiredScopes": { "eip155": { "chains": ["eip155:1", "eip155:137"], "methods": ["eth_sendTransaction", "eth_signTransaction", "eth_sign", "get_balance", "personal_sign"], - "events": ["accountsChanged", "chainChanged"] + "notifications": ["accountsChanged", "chainChanged"] }, "eip155:10": { "methods": ["get_balance"], - "events": ["accountsChanged", "chainChanged"] + "notifications": ["accountsChanged", "chainChanged"] }, "cosmos": { ... } }, - "optionalNamespaces":{ + "optionalScopes":{ "eip155:42161": { "methods": ["eth_sendTransaction", "eth_signTransaction", "get_balance", "personal_sign"], - "events": ["accountsChanged", "chainChanged"] + "notifications": ["accountsChanged", "chainChanged"] }, "sessionProperties": { "expiry": "2022-12-24T17:07:31+00:00", @@ -92,21 +122,21 @@ Example: } ``` -The JSON-RPC method is labelled as `provider_authorization` and both the -"requiredNamespaces" and "optionalNamespaces" arrays are populated with -`namespace` objects each named after the scope of authorization: -1. EITHER an entire ChainAgnostic [namespace][] -2. OR a specific [CAIP-2][] in that namespace. +The JSON-RPC method is labelled as `provider_authorize` and its `params` object +contains "requiredScopes" and/or "optionalScopes" objects populated with "scope +objects" each named after the scope of authorization requested: +1. EITHER an entire [CAIP-104][] [namespace][] +2. OR a specific [CAIP-2][]-identified chain in a specific namespace. -Each `namespace` object contains the following parameters: +Each scope object contains the following parameters: - chains - array of [CAIP-2][]-compliant `chainId`'s. This parameter MAY be omitted if a single-chain scope is already declared in the index of the object. - methods - array of JSON-RPC methods expected to be used during the session -- events - array of JSON-RPC message/events expected to be emitted during the - session +- notifications - array of JSON-RPC message/notifications expected to be emitted + during the session -The `requiredNamespaces` array MUST contain 1 or more of these objects, if present; the `optionalNamespaces` array MUST contain 1 or more of them, if -present. +The `requiredScopes` array MUST contain 1 or more of these objects, if present; +the `optionalScopes` array MUST contain 1 or more of them, if present. A third object is the `sessionProperties` object, all of whose properties MUST be in the interpreted as optional, since requesting applications cannot mandate @@ -127,24 +157,25 @@ The wallet can respond to this method with either a success result or an error m #### Success -The succesfull reslt contains one mandatory string (keyed as `sessionId` with a value +The successful result contains one mandatory string (keyed as `sessionId` with a value conformant to [CAIP-171][]) and two session objects, both mandatory and non-empty. -The first is called `sessionNamespaces` and contains 1 or more namespace objects. -* All required namespaces and all, none, or some of the optional namespaces (at the -discretion of the provider) MUST be included if successful. -* As in the request, each namespace object MUST contain `methods` and `events` objects, -and a `chains` object if a specific chain is not specified in the object's index. -* Unlike the request, each namespace object MUST also contain an `accounts` array, -containing 0 or more [CAIP-10][] conformant accounts authorized for the session and valid -in the namespace and chain(s) authorized by the object they are in. Additional constraints -on the accounts authorized for a given session MAY be specified in the corresponding -[namespaces][] specification. - -A `sessionProperties` object MAY also be present, and its contents MAY correspond to the -properties requested in the response or not (at the discretion of the provider) but MUST -conform to the properties names and value constraints described in [CAIP-170][]; any other -MUST be dropped by the requester. +The first is called `sessionScopes` and contains 1 or more scope objects. +* All required scope objects and all, none, or some of the optional scope object +(at the discretion of the provider) MUST be included if successful. +* As in the request, each scope object object MUST contain `methods` and +`notifications` objects, and a `chains` object if a specific chain is not +specified in the object's index. +* Unlike the request, each scope object MUST also contain an `accounts` array, +containing 0 or more [CAIP-10][] conformant accounts authorized for the session +and valid in the namespace and chain(s) authorized by the scope object they are +in. Additional constraints on the accounts authorized for a given session MAY be +specified in the corresponding [CAIP-104][] namespaces specification. + +A `sessionProperties` object MAY also be present, and its contents MAY +correspond to the properties requested in the response or not (at the discretion +of the provider) but MUST conform to the property names and value constraints +described in [CAIP-170][]; any other MUST be dropped by the requester. An example of a successful response follows: @@ -154,21 +185,21 @@ An example of a successful response follows: "jsonrpc": "2.0", "result": { "sessionId": "0xdeadbeef", - "sessionNamespaces": { + "sessionScopes": { "eip155": { "chains": ["eip155:1", "eip155:137"], "methods": ["eth_sendTransaction", "eth_signTransaction", "get_balance", "eth_sign", "personal_sign"] - "events": ["accountsChanged", "chainChanged"], + "notifications": ["accountsChanged", "chainChanged"], "accounts": ["eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb", "eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"] }, "eip155:10": { "methods": ["get_balance"], - "events": ["accountsChanged", "chainChanged"], + "notifications": ["accountsChanged", "chainChanged"], "accounts:" [] }, "eip155:42161": { "methods": ["personal_sign"], - "events": ["accountsChanged", "chainChanged"], + "notifications": ["accountsChanged", "chainChanged"], "accounts":["eip155:42161:0x0910e12C68d02B561a34569E1367c9AAb42bd810"] "cosmos": { ... @@ -183,55 +214,143 @@ An example of a successful response follows: #### Failure States -The response MUST NOT be a success result when the user disapproves the accounts -matching the requested chains to be exposed or the requested methods are not -approved or the requested chains are not supported by the wallet or the -requested methods are not supported. +The response MUST NOT be a JSON-RPC success result in any of the following +failure states. + +##### Generic Failure Code -An example of an error response should match the following format: +Unless the dapp is known to the wallet and trusted, the generic/undefined error +response, ```jsonc { "id": 1, "jsonrpc": "2.0", "error": { - "code": 5000, - "message": "User disapproved requested chains" + "code": 0, + "message": "Unknown error" } } ``` +is RECOMMENDED for any of the following cases: +- the user denies consent for exposing accounts that match the requested and + approved chains, +- the user denies consent for requested methods, +- the user denies all requested or any required scope objects, +- the wallet cannot support all requested or any required scope objects, +- the requested chains are not supported by the wallet, or +- the requested methods are not supported by the wallet + +##### Trusted Failure Codes + +More informative error messages MAY be sent in trusted-counterparty +circumstances, although extending this trust too widely may contribute to +widespread fingerprinting and analytics which corrode herd privacy (see +[Privacy Considerations](#privacy-considerations) below). The core error +messages over trusted connections are as follows: + The valid error messages codes are the following: -* When user disapproves exposing accounts to requested chains +* Unknown error OR no scopes were authorized * code = 5000 - * message = "User disapproved requested chains" + * message = "Unknown error with request" * When user disapproves accepting calls with the request methods * code = 5001 * message = "User disapproved requested methods" -* When user disapproves accepting calls with the request events +* When user disapproves accepting calls with the request notifications * code = 5002 - * message = "User disapproved requested events" -* When wallet evaluates requested chains to not be supported + * message = "User disapproved requested notifications" +* When provider evaluates requested chains to not be supported * code = 5100 * message = "Requested chains are not supported" -* When wallet evaluates requested methods to not be supported +* When provider evaluates requested methods to not be supported * code = 5101 * message = "Requested methods are not supported" -* When wallet evaluates requested events to not be supported +* When provider evaluates requested notifications to not be supported * code = 5102 - * message = "Requested events are not supported" + * message = "Requested notifications are not supported" + +##### Trust-Agnostic Malformed Request Failure Codes + +Regardless of caller trust level, the following error responses can reduce +friction and user experience problems in the case of malformed requests. + +* When provider does not recognize one or more requested method(s) + * code = 5201 + * message = "Unknown method(s) requested" +* When provider does not recognize one or more requested notification(s) + * code = 5202 + * message = "Unknown notification(s) requested" * When a badly-formed request includes a `chainId` mismatched to scope - * code = 5103 + * code = 5203 * message = "Scope/chain mismatch" * When a badly-formed request defines one `chainId` two ways - * code = 5104 + * code = 5204 * message = "ChainId defined in two different scopes" * Invalid Session Properties Object - * code = 5200 + * code = 5300 * message = "Invalid Session Properties requested" - * Required Session Properties - * code = 5201 - * message = "Session Properties can only be optional" +* Session Properties requested outside of Session Properties Object + * code = 5301 + * message = "Session Properties can only be optional and global" + +Note: respondents are RECOMMENDED to implement support for core RPC Documents +per each supported namespace to avoid sending error messages 5201 and 5202 in +cases where 0, 5101 or 5102 would be more appropriate. + +## Security Considerations + +The crucial security function of a shared session negotiated and maintained by a +series of CAIP-25 calls is to reduce ambiguity in authorization. This requires +a potentially counterintuitive structuring of the building-blocks of a +Chain-Agnostic session into scopes at the "namespace-wide" ([CAIP-104][]) or at +the "chain-specific" ([CAIP-2][]) level; for this reason, requests and responses +are structures as arrays of objects keyed to these scopes, formatted either as a +[CAIP-104][] scheme OR as a full [CAIP-2][]. While internal systems are free to +translate this object into other structures, preserving it in the CAIP-25 +interface is crucial to the unambiguous communication between caller and +respondent about what exact authorization is granted. + +## Privacy Considerations + +One major risk in browser-based or HTTP-based communications is "fingerprinting +risk", i.e. the risk that public or intercepted traffic can be used to +deanonymize browsers and/or wallets deductively based on response times, error +codes, etc. To minimize this risk, and to minimize the data (including +behavioral data) leaked by responses to potentially malicious CAIP-25 calls, +respondents are recommended to ignore calls +1. which the respondent does not authorize, +2. which are rejected by policy, or +3. requests which are rejected for unknown reasons. + +"Ignoring" these calls means responding to all three in a way that is +*indistinguishable* to a malicious caller or observer which might deduce +information from differences in those responses (including the time taken to +provide them). Effectively, this means allowing requests in all three cases to +time out even if the end-user experience might be better served by +differentiating them, particularly in complex multi-party architectures where +parties on one side of this interface need to have a shared understanding of why +a request did not receive a response. At scale, however, better user experiences +in a single architecture or context can contribute to a systemic erosion of +anonymity. + +Given this "silent time out" behavior, the best strategy to ensure good user +experience is not to request too many properties in the initial establishment of +a session and to iteratively and incrementally expand session authorization over +time. This also contributes to a more consentful experience overall and +encourages progressive trust establishment across complex architectures with +many distinct actors and agents. + +Another design pattern that accomodates the "silent time out" behavior is minor +updates to the session. For example, a caller sending a request identical to a +previous request (or a previous response) except for a new session expiry +further in the future could expect one of exactly three responses: +1. An identical response to the previous request (meaning the session extension was denied); +2. A response identical expect that it includes the new, extended session expiry; or, +3. A silent time out (meaning the calling behavior was malformed in ways the +respondent cannot understand, or the respondent choses not to make explicit how +the request was malformed, or the end-user rejected them, or the request itself +was in violation of policy). ## Changelog @@ -251,6 +370,7 @@ The valid error messages codes are the following: [CAIP-10]: https://chainagnostic.org/CAIPs/caip-10 [CAIP-25]: https://chainagnostic.org/CAIPs/caip-25 [CAIP-75]: https://chainagnostic.org/CAIPs/caip-75 +[CAIP-104]: https://chainagnostic.org/CAIPs/caip-104 [CAIP-171]: https://chainagnostic.org/CAIPs/caip-171 [namespaces]: https://namespaces.chainagnostic.org [RFC3339]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 diff --git a/caip-template.md b/caip-template.md index 4f065910..130b8313 100644 --- a/caip-template.md +++ b/caip-template.md @@ -1,7 +1,7 @@ --- # Every document starts with a front matter in YAML enclosed by triple dashes. # See https://jekyllrb.com/docs/front-matter/ to learn more about this concept. -caip: +caip: CAIP-X title: author: , FirstName (@GitHubUsername) and GitHubUsername (@GitHubUsername)> discussions-to: @@ -40,14 +40,19 @@ The technical specification should describe the standard in detail. The specific The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.--> +## Test Cases + + +## Security Considerations + + +## Privacy Considerations + + ## Backwards Compatibility All CAIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The CAIP must explain how the author proposes to deal with these incompatibilities. CAIP submissions without a sufficient backwards compatibility treatise may be rejected outright. -## Test Cases - -Please add test cases here if applicable. - ## Links Links to external resources that help understanding the CAIP better. This can e.g. be links to existing implementations.