-
Notifications
You must be signed in to change notification settings - Fork 217
[Caip-25] Finalize core/extensions model and core terminology (formerly, "move to Last Call") #206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
0921bd6
6632d05
524124c
7d5a86d
32c4a67
17169c2
cd43574
7f57a96
c80ea56
57cd5d5
1ae5132
4adb5be
00fe3a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 events. 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 event 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, event, 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 arrays (named | ||
| `requiredScopes` and `optionalScopes` respectively). These two arrays 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 array). 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 | ||
|
|
||
|
|
@@ -65,7 +95,7 @@ Example: | |
| "jsonrpc": "2.0", | ||
| "method": "provider_authorization", | ||
bumblefudge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "params": { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't params be an array of two arrays containing various scope objects? (per our description) type Scope = {
methods: string[];
events: string[];
}
type ChainScope = Scope;
type NamespaceScope = Scope & { chains: Array<CAIP2Id>; }
type RequiredScope = NamespaceScope | ChainScope;
type OptionalScope = RequiredScope;
type CAIP25Params = [NonEmptyArray<RequiredScope>, NonEmptyArray<OptionalScope> | undefined]
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ooh, so the params is an object in the examples and an array in the text? I hesitate to fix that either way by myself, let me check out if JSON-RPC syntax will make my job easier by disqualifying one of the two options 😅
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://www.jsonrpc.org/specification#parameter_structures
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so i'm personally leaning towards keep object and make the text make the example :D
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. haha yes, I would also prefer object-based -- well that solves that :) |
||
| "requiredNamespaces": { | ||
| "requiredScopes": { | ||
| "eip155": { | ||
| "chains": ["eip155:1", "eip155:137"], | ||
| "methods": ["eth_sendTransaction", "eth_signTransaction", "eth_sign", "get_balance", "personal_sign"], | ||
|
|
@@ -79,7 +109,7 @@ Example: | |
| ... | ||
| } | ||
| }, | ||
| "optionalNamespaces":{ | ||
| "optionalScopes":{ | ||
| "eip155:42161": { | ||
| "methods": ["eth_sendTransaction", "eth_signTransaction", "get_balance", "personal_sign"], | ||
| "events": ["accountsChanged", "chainChanged"] | ||
|
|
@@ -93,20 +123,20 @@ Example: | |
| ``` | ||
|
|
||
| The JSON-RPC method is labelled as `provider_authorization` and both the | ||
bumblefudge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "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. | ||
| "requiredScopes" and "optionalScopes" arrays are 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed in today's editorial call: maybe using method names is not completely covering it - there is different behavior in methods between different clients (@TimDaub surfaced this)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that discrepancies in APIs is an issue worth attacking-- my counterargument, though, is that CASA shouldn't host or run the registry that attacks it! I think the way 211 is written, it presumes that, for example, if geth or erigon use slightly different versions of a method, they can each publish
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I disagree with this as clients should not be changing behavior for methods without renaming them If a client behaves differently for example with adding a client suffix is only going to incentivize more fragmentation and less collaboration if clients created this problem then they should fix it because they are mis-implementing methods can you imagine if every wallet implemented
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree it is not ideal - but it is reality and something we need to deal with unfortunately. What is your counter-proposal to deal with the situation?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At least with this solution we can detect with a handshake that something would fail and maybe can fallback or handle it gracefully instead of having weird unpredictable errors.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the way I see it, there does not need to be a registry of multiple RPC documents-- there is the one RPC document defined by Can you make the RPC WG next week to discuss further, Ligi? We can always invite Shane back to discuss how he/the openRPC team sees it
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Merging this PR as-is, but this entire thread is still very pertinent to #211 which lays out the RPC document stuff (in a way that can be profiled for each namespace!), so let's continue the thread there! |
||
| - events - array of JSON-RPC message/events 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 | ||
|
|
@@ -130,21 +160,22 @@ The wallet can respond to this method with either a success result or an error m | |
| The succesfull reslt contains one mandatory string (keyed as `sessionId` with a value | ||
bumblefudge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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 | ||
| `events` objects, and a `chains` object if a specific chain is not specified in | ||
bumblefudge marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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,7 +185,7 @@ 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"] | ||
|
|
@@ -196,15 +227,15 @@ An example of an error response should match the following format: | |
| "jsonrpc": "2.0", | ||
| "error": { | ||
| "code": 5000, | ||
| "message": "User disapproved requested chains" | ||
| "message": "Unknown error" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| The valid error messages codes are the following: | ||
| * When user disapproves exposing accounts to requested chains | ||
| * Unknown error OR no requested scopes were authorized | ||
| * code = 5000 | ||
| * message = "User disapproved requested chains" | ||
| * message = "Unknown error" | ||
| * When user disapproves accepting calls with the request methods | ||
| * code = 5001 | ||
| * message = "User disapproved requested methods" | ||
|
|
@@ -229,9 +260,61 @@ The valid error messages codes are the following: | |
| * Invalid Session Properties Object | ||
| * code = 5200 | ||
| * message = "Invalid Session Properties requested" | ||
| * Required Session Properties | ||
| * Session Properties requested outside of Session Properties Object | ||
| * code = 5201 | ||
| * message = "Session Properties can only be optional" | ||
| * message = "Session Properties can only be optional and global" | ||
|
|
||
| ## 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 +334,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 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be updated in line with @pedrouid's comment from last week, not sure if this is a point of contention or not, but I agree with his point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure-- will finesse the language in a new commit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Voilà
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect! Just wondering, how come the error code # change?