Skip to content
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

Authz/Pre-Authz 2.0.0 Service Specification Proposal #1787

Closed
jribbink opened this issue Oct 6, 2023 · 4 comments
Closed

Authz/Pre-Authz 2.0.0 Service Specification Proposal #1787

jribbink opened this issue Oct 6, 2023 · 4 comments
Assignees

Comments

@jribbink
Copy link
Contributor

jribbink commented Oct 6, 2023

Context

It has been 3 years since the original FCL specification outlining authorization services (known as authz & pre-authz) was defined. In combination, these two services aimed to resolve one or more signatures for a transaction using keys whose total signatory weight was ≥1000. Combined, these signatures would confirm that accounts involved in each transaction role had approved the transaction’s execution. While FCL’s authorization services have since proven themselves to be extremely robust, it seems that the time has come where these services have begun to outlive their foreseen use-cases.

The problem

Recently, the community has voiced its concerns regarding shortcomings of the current authorization model. Particularly, discussion raised in #1679 has elicited the need for change to how authorization services operate within FCL. However, this PR has not yet been merged as it failed to address the underlying issue and only offered a short-term solution. We have listened to these community concerns and are proposing robust upgrade to the FCL specification to help support these emerging requirements.

Proposal

This proposal outlines a refined, key-agnostic approach by which account authorizations would be resolved. Namely, a new configuration for authz services in FCL would mean that signatures are no longer resolved for specific account keys, but rather via one or more signatures from any keys on the account.

In addition to adding the key-agnostic specification to FCL, Flow client libraries must continue to support the key-specific authz service structure for two main reasons:

  1. Proposer authorizations are not key-agnostic by definition. All transaction payload signatures are generated based on a predetermined proposer key. This cannot change during the authorization step and must be established prior (i.e. pre-authz). For this reason, there must remain a authorization method valid for the transaction proposer.
  2. FCL must guarantee compatibility with existing wallets & authz service implementations. In future iterations of the FCL protocol, it is possible that this could become a breaking change, but for now support must remain for key-specific authz services.

In this document, we will establish the following terminology:

  • key-specific authz services - the current authz service model
  • key-agnostic authz services - the proposed addition to the authz service model (remove/optional keyId field)

Authz Service Structure

Because of changes to the schema of the authz service, it is necessary that the authz schema is given a new f_vsn to identify whether wallets/clients can support this functionality.

For the revised authz service, the key-specific configuration would remain identical to its predecessor v1.0.0 implementation, however with the version bumped to v2.0.0.

{
    "f_vsn": 2.0.0      
    "f_type": "Service",
    "id": "foobar#authz-http-post",
    "type": "authz",
    "identity": {
        "address": "f086a545ce3c552d",
        "keyId": 615
    },
    "method": "HTTP/POST",
    "endpoint": "https://example.com/authz",
    "params": { ... }
}

In the above example, FCL expects that https://example.com/authz will respond with a signature for keyId=615 on account f086a545ce3c552d. No other keys’ signatures would be valid responses.

Key-agnostic authz services would discard this assumption, instead conforming closer to the protocol-level definition of account authorization. Rather than defining authorizations in terms of account-key pairs, they would be defined as a collection signatures using any number of arbitrary keys from a given account.

For the v2.0.0 authz specification, the keyId would become an optional parameter. When describing a key-agnostic authz service, the only difference would be the removal of the "keyId" field.

{
    "f_vsn": 2.0.0,
    "f_type": "Service",
    "id": "foobar#authz-http-post",
    "type": "authz"
    "identity": {
        "address": "f086a545ce3c552d",
    },
    "method": "HTTP/POST",
    "endpoint": "https://example.com/authz",
    "params": { ... }
}

The above authz service defines an HTTP/POST request to [https://example.com/authz](https://example.com/authz) which should resolve one or more signatures for the account f086a545ce3c552d using any keys.

Authz Service Response

For key-specific authz services, the response was a CompositeSignature object defined as follows:

{
    ...polling response...
    "data": {
        "f_vsn": "1.0.0",
        "f_type": "CompositeSignature",
        "addr": "0x358529c1f0220750",
        "keyId": "1"
        "signature": "0db1718281a5c5587b5b4b026f12aed576b3e2dec5a297fab17c40a2c1e46f1b93fe537934bf0d5208c7cabe9196f24d4a0ba34a45ec958f8f9295139624cf3d"
    }
    ...
}

The notable limitation in this response is that only a single signature may be provided. The proposed response for the revised authz service would also allow arrays of signatures.

Imagine an example authz service which is responsible for resolving an authorization for 0x358529c1f0220750 (like the example in the previous subsection). Assume that this account has two keys: both with 500 weight and that the is authz service is responsible for resolving signatures for both these keys. The response from this service could be as follows:

{
    ...polling response...
    "data": [
        {
            "f_vsn": "1.0.0",
            "f_type": "CompositeSignature",
            "addr": "0x358529c1f0220750",
            "keyId": "1" // 500 weight
            "signature": "0db1718281a5c5587b5b4b026f12aed576b3e2dec5a297fab17c40a2c1e46f1b93fe537934bf0d5208c7cabe9196f24d4a0ba34a45ec958f8f9295139624cf3d"
        },
        {
            "f_vsn": "1.0.0",
            "f_type": "CompositeSignature",
            "addr": "0x358529c1f0220750",
            "keyId": "2" // 500 weight
            "signature": "0db1718281a5c5587b5b4b026f12aed576b3e2dec5a297fab17c40a2c1e46f1b93fe537934bf0d5208c7cabe9196f24d4a0ba34a45ec958f8f9295139624cf3d"
        }
    ]
    ...
}

In this case, the authz service successfully obtains a 1000-weight authorization by using a combination of two keys’ signatures. It is natural that key-agnostic authz responses are not restricted to a single CompositeSignature. This design is more compatible with the protocol-level definition of account authorizations: the account authorization process may require an aggregate of signatures from multiple keys as opposed to a single key. In some designs this can prevent redundant authz services and unnecessary API requests.

It is not a requirement that the cumulative weight of the signatures is ≥1000. Multiple authz services may have to work together in order to achieve this.

Of course, a single CompositeSignature passed as a plain object, and not an array, would still be sufficient as well; e.g.

"data": {
    "f_vsn": "1.0.0",
    "f_type": "CompositeSignature",
    "addr": "0x358529c1f0220750",
    "keyId": "1"
    "signature": "0db1718281a5c5587b5b4b026f12aed576b3e2dec5a297fab17c40a2c1e46f1b93fe537934bf0d5208c7cabe9196f24d4a0ba34a45ec958f8f9295139624cf3d"
}

Multi-signer transactions? Is there enough weight?

One question that may arise is the following: how does the removal of explicit account-key pairs from authz services affect multi-signer transactions. Specifically, how can it be ensured that the combined signature resolution of all authz services would result in a satisfactory weight for transaction approval (1000)?

Ultimately, these concerns are not relevant. It’s up to the authn-provider/wallet to provide a collection of authz services whose combination generate signatures of cumulative weight ≥1000 - this concern is out of the scope of FCL. Largely speaking, these guarantees should likely hold true due to the natural configuration of the wallet-controlled APIs which these services rely upon.

However, should these services require additional metadata for key-orchestration, this can be defined by the pre-authz step and passed through "params" field of the authz service. This may be a keyId the service is expected to sign with, a minimum signatory weight expected to be generated by the service, or other fields based on the application.

Overlapping Keys

One guarantee made by key-specific authz services is that two different authz services will not generate signatures using overlapping keys. One question may be: how do we make the same guarantee with key-agnostic authz services.

This is not a concern for the same reasons as above with multi-signer transactions - key orchestration will tend to be an inherent guarantee of the wallet’s API endpoints themselves, and should it not be, metadata can be configured via authz service "params".

Compatibility Considerations

Ensuring backward compatibility is critical when making changes to the FCL spec. Wallets which wish to switch to key-agnostic authz service strategies may do so as long as the FCL client they are interacting with supports this feature. The following outlines the version negotiation process.

Service Version Selection

The wallet and the client (FCL) will negotiate whether they are using the revised authz schema using the following technique:

  1. The wallet will advertise all of the FCL services & versions of these which they are compatible with during the authentication step. For example:

    [
    	...services...,
    	{
    		"f_vsn": "1.0.0",
    		"f_type": "Service",
    		"type": "authz"
    		...
    	},
    	{
    		"f_vsn": "2.0.0",
    		"f_type": "Service",
    		"type": "authz"
    		...
    	}
    ]
  2. When executing a given service (i.e. execService('authz')), the FCL client will choose to execute the greatest compatible version supported by the wallet.

Using these two steps, it is possible to incrementally upgrade services from both the wallet & FCL while maintaining both-sided backwards compatibility.

So, to implement key-agnostic authz services from the wallet’s standpoint they will need to do add additional versioned services for the following:

  • pre-authz - If a wallet wishes to return key-agnostic authz services during their pre-authorization step, they must provide an additional v2.0.0 pre-authz service. The prior v1.0.0 service should remain as well to support older clients.
  • authz - If a wallet wishes for their default authz service to be key-agnostic, they must provide an additional v2.0.0 authz service. However, it is important to note that key-agnostic services are not valid for proposer authorizations. If a wallet offers a key-agnostic authz service to the user, it is expected that the proposer authorization will be overridden by a key-specific authz service during a pre-authz step. If this does not happen, the transaction will fail

FCL-JS Filtering

Unfortunately, FCL-JS does not implement service version selection prior to v1.7.0. The service addition technique outlined above will not work for FCL versions <1.7.0. As a workaround, wallets will have to use the fclVersion field provided in the authentication (sign in) step before they choose to provide the additional authz/pre-authz services.

@jribbink jribbink self-assigned this Oct 6, 2023
@jribbink jribbink changed the title Authz/Pre-Authz 2.0.0 Service Specification proposal Authz/Pre-Authz 2.0.0 Service Specification Proposal Oct 7, 2023
@bluesign
Copy link

bluesign commented Oct 7, 2023

Thanks a lot for the proposal @jribbink

Can we make this a FLIP please? Very hard to read and comment like this.

it has advantages like; commenting any part of the proposal easily, better discussion, easier to reject etc

Also I think we can add a bit more background, also compare this solution with alternatives proposed ( Blocto’s solution )

I am not sure why v2 auth is needed? If wallet has to know the client version. ( not sure when 1.7.0 released )

Proposer authorizations are not key-agnostic by definition. All transaction payload signatures are generated based on a predetermined proposer key. This cannot change during the authorization step and must be established prior (i.e. pre-authz). For this reason, there must remain an authorization method valid for the transaction proposer.

AFAIK Blocto's solution was backwards compatible? Just adding extra keyId, if exists just replacing keyId if signature is not proposer signature.

FCL must guarantee compatibility with existing wallets & authz service implementations. In future iterations of the FCL protocol, it is possible that this could become a breaking change, but for now support must remain for key-specific authz services.

I think Blocto can chime in here but I think their path is backwards compatible with FCL ( please correct me if there is a case it is not )

One last thing; instead of making another authz service ( and using 2 authz services ) why not add an FCL config option to relax restriction ? FCL.config("strictKeyId") for example if set to true can check if keyId is same as requested when signing / or switch to behaviour from Blocto's patch if set to false. Even this config can be part of FCL Discovery, like a feature. ( not sure about the terminology there )

@jribbink
Copy link
Contributor Author

Hey @bluesign , thanks for your feedback on this proposal. I'll be moving this to a FLIP shortly.

I am not sure why v2 auth is needed? If wallet has to know the client version. ( not sure when 1.7.0 released )

Versioning this proposal has been a really tricky problem. Essentially, versioning of FCL specifications/features should not be FCL-JS specific. We should be trying to avoid this tight coupling with the JavaScript library & FCL-JS versions as it makes it difficult to implement the FCL spec on different platforms (i.e. Swift, Kotlin, etc.). This is supposed to be solved by the versioning system for FCL services (i.e. the v2 authz you referred to).

Unfortunately, this implementation has always been bugged (see #1785). So if we used versioned services (v2) with the current FCL-JS implementation, it would break. I.e. it isn't until this bug fix is released, 1.7.0, that versioned services become compatible.

Like I said... really nasty problem... and none of the solutions here feel really satisfactory. I would have liked to avoid checking fclVersion on the wallet end, but to me this one feels like it sets the best precedent for future changes.

AFAIK Blocto's solution was backwards compatible? Just adding extra keyId, if exists just replacing keyId if signature is not proposer signature.

Blocto's solution is backwards compatible in the sense that their modified FCL would work with existing wallets. However, it is not backwards compatible for the reverse situation - wallets using the keyId replacement will be incompatible with previous versions of FCL. Could these wallets do a version check for FCL > 1.7.0... yeah, probably... but this goes full circle to the 1st point I addressed.

@bluesign
Copy link

thanks @jribbink,

The issue I see is the requirement of providing v1 services with v2. ( for proposer go to v1 service etc ) Maybe I misunderstood that part.

My focus is not at v1 or v2 distinction, but if we make a v2 service maybe we can make it v1 compatible. ( so provider with v2 service doesn't have to also provide v1 )

@jribbink
Copy link
Contributor Author

jribbink commented Oct 31, 2023

Moving discussion to onflow/flips#215

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants