diff --git a/docs/build/tools/wallet-provider-spec/authorization-function.md b/docs/build/tools/wallet-provider-spec/authorization-function.md index 4206442ed2..c6a05e6858 100644 --- a/docs/build/tools/wallet-provider-spec/authorization-function.md +++ b/docs/build/tools/wallet-provider-spec/authorization-function.md @@ -2,15 +2,15 @@ ## Overview -An Authorization Function is a function which enables the JS-SDK and FCL to know which Flow account fulfills which signatory role in a transaction and how to recieve a signature on behalf of the supplied account. +An Authorization Function is a function which allows the JS-SDK and Flow Client Library (FCL) to know which Flow account fulfills which signatory role in a transaction and how to recieve a signature on behalf of the supplied account. -## How to Use an Authorization Function +## How to use an Authorization Function -An authorization function is a function that you may use in place of an authorization in the Flow JS-SDK and FCL. An authorization is a concept that is used when denoting a proposer, payer or authorizer for a transaction. An authorization can either be a data structure represenating an authorization, or a function which when called returns an authorization called an Authorization Function. In this document we discuss the latter. +An Authorization Function is a function that you may use in place of an authorization in the Flow JS-SDK and FCL. An authorization is a concept that is used to denote a proposer, payer or authorizer for a transaction. An authorization can either be a data structure that represents an authorization, or a function which when called returns an authorization called an Authorization Function. In this document, we discuss the latter. To use an Authorization Function, you specify that Authorization Function as the authorization for a proposer, payer or authorizer for a transaction. -> `fcl.currentUser().authorization` which is aliased to `fcl.authz` is itself an authorization function. It tells the underlying js-sdk the current users flow account will be used for the signatory role and supplies a signing function that enables the application to request a signature from the users wallet. +> `fcl.currentUser().authorization` which is aliased to `fcl.authz` is itself an authorization function. It tells the underlying js-sdk the current users flow account will be used for the signatory role and supplies a signing function that allows the application to request a signature from the users wallet. Example 1: ```javascript @@ -28,19 +28,19 @@ const response = fcl.send([ The builder functions, `fcl.proposer`, `fcl.payer` and `fcl.authorizations` each consume the Authorization Function and set it as the resolve field on the internal Account object it creates. -During the resolve phase of the Flow JS-SDK and FCL, when [`resolveAccounts`](https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/resolve/resolve.js#L58) is called, the resolve field on each internal Account object is called, which means each Authorization Function is called appropriately and the account is _resolved_ into the data structure the authorizationFunction returns. These accounts are then deduped based on the a mix of the `addr`, `keyId` and `tempId` so that only a single signature request happens per `address` `keyId` pair. When [`resolveSignatures`](https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/resolve/resolve.js#L62) is called the signing function for each `address` `keyId` pair is called returning a composite signature for each signatory role. +During the resolve phase of the Flow JS-SDK and FCL, when [`resolveAccounts`] is called, the resolve field on each internal Account object is called, which means each Authorization Function is called appropriately and the account is _resolved_ into the data structure the authorizationFunction returns. These accounts are then deduped based on the a mix of the `addr`, `keyId` and `tempId` so that only a single signature request happens per `address` `keyId` pair. When [`resolveSignatures`] is called, the signing function for each `address` `keyId` pair is called, and returns a composite signature for each signatory role. -## How to Create An Authorization Function +## How to create An Authorization Function -Fortunately, creating an Authorization Function is relatively straight forward. +Fortunately, it's relatively straight forward to create an Authorization Function. -An Authorization Function needs to be able to do at minimum two things. +An Authorization Function needs to be able to do at minimum two things: - Who will sign -- Know which account is going to sign and the keyId of the key it will use to sign - How they sign -- Know how to get a signature for the supplied account and key from the first piece. -The Authorization Function has a concept of an account. An account represent a possible signatory for the transaction, it includes the who is signing as well as the how it will be signed. The Authorization Function is passed an empty Account and needs to return an Account, your job when making an Authorization Function is mostly to fill in this Account with the information so that the account you want to sign things can. +The Authorization Function has a concept of an account. An account represent a possible signatory for the transaction. It includes who signs it as well as how it will be signed. The Authorization Function is passed an empty Account and needs to return an Account, your job when you make an Authorization Function is mostly to fill in this Account with the information so that the account you want to sign things can. -Lets say we knew up front the account, keyId and had a function that could sign things. +Let's say we knew up front the account, keyId, and had a function that could sign things. ```javascript const ADDRESS = "0xba1132bc08f82fe2" @@ -74,7 +74,7 @@ const authorizationFunction = async (account) => { ## Async stuff -Both the Authorization Function, and the accounts Signing Function can be asynchronous. This means both of these functions can go and get the information needed elsewhere. Say each of your users had a `userId`. From this `userId` say you had an api call that could return the corresponding address and key that is needed for the Authorization Functions account. You could also have another endpoint that when posted the signable (includes what needs to be signed) and the `userId` it can return with the composite signature if your api decides its okay to sign (the signable has all sorts of info to help you decide). An authorization function that can do that could look something like this. +Both the Authorization Function, and the account's Signing Function can be asynchronous. This means both of these functions can go and get the information needed elsewhere. Say each of your users had a `userId`. From this `userId` say you had an API call that could return the address and key needed for the Authorization Functions account. You could also have another endpoint that when posted the signable (includes what needs to be signed) and the `userId` it can return with the composite signature if your API decides its okay to sign (the signable has all sorts of info to help you decide). An Authorization Function that can do that could look something like this. Example 3: ```javascript @@ -107,7 +107,7 @@ The above **Example 3** is the same as **Example 2**, but the information is gat Creating a signing function is also relatively simple! -To create a signing function you specify a function which consumes a payload and returns a signature data structure. +To create a signing function, you specify a function which consumes a payload and returns a signature data structure. Example 3: ```javascript @@ -129,3 +129,7 @@ const signingFunction = ({ } } ``` + + +[`resolveAccounts`]: https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/resolve/resolve.js#L58 +[`resolveSignatures`]: https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/resolve/resolve.js#L62 \ No newline at end of file diff --git a/docs/build/tools/wallet-provider-spec/custodial.md b/docs/build/tools/wallet-provider-spec/custodial.md index e2be12e13d..ab9029f302 100644 --- a/docs/build/tools/wallet-provider-spec/custodial.md +++ b/docs/build/tools/wallet-provider-spec/custodial.md @@ -2,17 +2,17 @@ A Wallet Provider handles Authentications and Authorizations. They play a very important role of being the place the users control their information and approve transactions. -One of FCLs core ideals is for the user to be in control of their data, a wallet provider is where many users will do just that. +One of Flow CLient Library's (FCL) core ideals is for the user to be in control of their data, a wallet provider is where many users will do just that. -FCL has been built in a way that it doesn't need to know any intimate details about a wallet provider up front, they can be discovered when the users wishes to let the dapp know about them. This gives us a concept we have been calling Bring Your Own Identity. +FCL has been built in a way that it doesn't need to know any intimate details about a Wallet Provider up front; they can be discovered when the users wishes to let the dApp know about them. This gives us a concept we call Bring Your Own Identity. # Identity Conceptually, FCL thinks of identity in two ways: Public and Private. -Public identity will be stored on chain as a resource, it will be publicly available to anyone that knows the Flow Address for the account. +Public identity will be stored on chain as a resource and publicly available to anyone that knows the Flow Address for the account. -In FCL getting a users public identity will be as easy as: +In FCL, you can easily retrieve a user's public identity: ```javascript import { user } from '@onflow/fcl'; @@ -26,14 +26,13 @@ const unsub = user(flowAddress).subscribe((identity) => console.log(identity)); // `------- The public identity for `flowAddress` ``` -Private identity will be stored by the Wallet Provider, it will only be available to the currentUser. +Private identity will be stored by the Wallet Provider and only be available to the currentUser. -In FCL getting the currentUsers identity will fetch both the public and the private identities, merging the private into the public. +In FCL, when you retrieve the currentUsers identity, it fetches both the public and the private identities, which merges the private into the public. Private info needs to be requested via scopes before the challenge step, more on that later. -Private info needs to be requested via scopes before the challenge step, more on that later. -We highly recommend Wallet Providers let the user see what scopes are being requested, and decide what scopes to share with the dapp. +We highly recommend Wallet Providers let the user see what scopes are being requested, and decide what scopes to share with the dApp. -Consumers of identities in FCL should always assume all data is optional, and should store as little as possible, FCL will make sure the users always see the latest. +Consumers of identities in FCL should always assume all data is optional, and should store as little as possible. FCL will make sure the users always see the latest. ```javascript import { config, currentUser, authenticate } from '@onflow/fcl'; @@ -47,35 +46,35 @@ const unsub = currentUser().subscribe((identity) => console.log(identity)); authenticate(); // trigger the challenge step (authenticate the user via a wallet provider) ``` -# Identity Data +# Identity data - All information in Identities are optional and may not be there. - All values can be stored on chain, but most probably shouldn't be. -We would love to see Wallet Providers enable the user to control the following info publicly, sort of a public profile starter kit if you will. +We would love to see Wallet Providers allow the user to control the following info publicly, sort of a public profile starter kit if you will. -FCL will always publicly try to fetch these fields when asked for a users information and it will be up to the Wallet provider to make sure they are there and keep them up to date if the user wants to change them. +FCL will always publicly try to fetch these fields when asked for a user's information, and it will be up to the Wallet provider to make sure they are there and keep them up to date if the user wants to change them. -- **`name`** -- A human readable name/alias/nym for a dapp users display name -- **`avatar`** -- A fully qualified url to a smaller image used to visually represent the dapp user -- **`cover`** -- A fully qualified url to a bigger image, could be used by the dapp for personalization -- **`color`** -- A 6 character hex color, could be used by the dapp for personalization -- **`bio`** -- A small amount of text that a user can use to express themselves +- **`name`** -- A human readable name, alias, or nym for a dApp user's display name. +- **`avatar`** -- A fully qualified url to a smaller image used to visually represent the dApp user. +- **`cover`** -- A fully qualified url to a bigger image, could be used by the dApp for personalization. +- **`color`** -- A six character hex color, could be used by the dApp for personalization. +- **`bio`** -- A small amount of text that a user can use to express themselves. -If we can give dapp developers a solid foundation of usable information that is in the direct control of the users from the very start, which we belive the above fields would do, our hopes are they can rely more on the chain and will need to store less in their own database. +If we can give dApp developers a solid foundation of usable information that is in the direct control of the users from the very start, which we belive the above fields would do, our hopes are they can rely more on the chain and will need to store less in their own database. -Private data on the other hand has more use cases than general data. It is pretty easy to imagine ordering something and needing information like contact details and where to ship something. +Private data on the other hand has more use cases than general data. It is pretty easy to imagine that you'd order something and need information like contact details and where to ship something. -Eventually we would love to see that sort of thing handled completely onchain, securely, privately and safely, but in the interm it probably means storing a copy of data in a database when its needed, and allowed by a user. +Eventually, we would love to see that sort of thing handled completely onchain, securely, privately and safely. In the interm ,it probably means that you'll store a copy of data in a database when it's needed, and a user allows it. -The process of a dapp receiving private data is as follows: +The process for a dApp to receive private data is as follows: -1. The dapp requests the scopes they want up front `fcl.config().put("challenge.scope", "email+shippingAddress")`. -2. The User authenticates `fcl.authenticate()` and inside the Wallet Providers authentication process decides its okay for the dapp to know both the `email` and the `shippingAddress`. The User should be able to decide which information to share, if any at all. -3. When the dapp needs the information they can request it from FCLs current cache of data, if it isnt there the dapp needs to be okay with that and adjust accodingly. +1. The dApp requests the scopes they want up front `fcl.config().put("challenge.scope", "email+shippingAddress")`. +2. The user authenticates `fcl.authenticate()` and inside the Wallet Providers authentication process decides its okay for the dapp to know both the `email` and the `shippingAddress`. The user should be able to decide which information to share, if any at all. +3. When the dApp needs the information they can request it from FCLs current cache of data, if it isn't there, the dApp needs to be okay with that and adjust accodingly. -Below are the scopes we are thinking of supporting privately: -FCL will only publicly and privately try to fetch these when specified up front by a dapp. +Below are the scopes we want to support privately: +FCL will only publicly and privately try to fetch these when specified up front by a dApp. - **`email`** - **`fullName`** @@ -86,25 +85,25 @@ FCL will only publicly and privately try to fetch these when specified up front - **`location`** - **`publicKey`** -All of the above are still subject to change as it is still early days, we would like to work closely with Wallet Providers to produce a robust, detailed and consitent spec regarding scopes. Feedback and thoughts are always welcome. +All of the above are still subject to change as it is still early days. We would like to work closely with Wallet Providers to produce a robust, detailed and consistent spec for scopes. Feedback and thoughts are always welcome. -# Authentication Challenge +# Authentication challenge Authentication can happen one of two ways: - Iframe Flow - Redirection Flow -As a Wallet Provider you will be expected to register a URL endpoint (and some other information) with a handshake service (FCL will be launching with one in which registration happens on chain and is completely open source (Apache-2.0 lincense)). +As a Wallet Provider, you will be expected to register a URL endpoint (and some other information) with a handshake service (FCL launches with one in which registration happens on chain and is completely open source (Apache-2.0 lincense)). This registered URL will be what is shown inside the iFrame or where the dapp users will be redirected. -For the remainder of this documentation we will refere to it as the _Authentication Endpoint_ and pair it with the `GET https://provider.com/flow/authentication` route. +For the remainder of this documentation, we will refere to it as the _Authentication Endpoint_ and pair it with the `GET https://provider.com/flow/authentication` route. The Authentication Endpoint will receive the following data as query params: -- `l6n` _(required)_ -- location (origin) of dapp -- `nonce` _(required)_ -- a random string supplied by the FCL -- `scope` _(optional)_ -- the scopes requested by the dapp -- `redirect` _(optional)_ -- where to redirect once the authentication challenge is complete +- `l6n` _(required)_ -- location (origin) of dApp. +- `nonce` _(required)_ -- a random string supplied by the FCL. +- `scope` _(optional)_ -- the scopes requested by the dApp. +- `redirect` _(optional)_ -- where to redirect after the authentication challenge is complete. ``` GET https://provider.com/flow/authenticate @@ -116,12 +115,11 @@ GET https://provider.com/flow/authenticate The values will use javascripts `encodeURIComponent` function and scopes will be `+` deliminated. ``` -We can tell that this challenge is using the Redirect Flow because of the inclusion of the redirect query param. -The Iframe Flow will still need to be supported as it will be the default flow for dapps. +We can tell that this challenge uses the Redirect Flow because of the inclusion of the redirect query param. The Iframe Flow will still need to be supported, as it will be the default flow for dapps. -At this point its on the Wallet Provider to do their magic and be confident enough that the user is who they say they are. -The user should then be shown in some form what the dapp is requesting via the scopes and allow them to opt in or out of anything they want. -Once the Wallet Provider is ready to hand back control to the dapp and FCL it needs to complete the challenge by redirecting or emiting a javascript `postMessage` event. +At this point, it's on the Wallet Provider to do their magic and be confident enough that the user is who they say they are. The user should then be shown in some form what the dApp wants via the scopes and allow them to opt in or out of anything they want. + +After the Wallet Provider is ready to hand back control to the dapp and FCL, it needs to redirect or emit a javascript `postMessage` event to complete the challenge. Redirecting will look like this: @@ -154,11 +152,9 @@ parent.postMessage( ); ``` -FCL should now have everything it needs to collect the Public, Private and Wallet Provider Info. -The Wallet Provider info will be on chain so its not something that needs to be worried about here by the Wallet Provider. -What does need to be worried about handling the hooks request which was supplied to FCL via the `hks` value in the challenge response `https://provider.hooks`. +FCL should now have everything it needs to collect the Public, Private and Wallet Provider Info. The Wallet Provider info will be on chain so its not something that needs to be worried about here by the Wallet Provider. -The hooks request will be to the `hks` value supplied in the challenge response. The request will also include the code as a query param +You should be aware of how to handle the hooks request, which was supplied to FCL via the `hks` value in the challenge response `https://provider.hooks`. The hooks request will be to the `hks` value supplied in the challenge response. The request will also include the code as a query param. ``` GET https://povider.com/hooks @@ -167,13 +163,13 @@ GET https://povider.com/hooks This request needs to happen for a number of reasons. -- If it fails FCL knows something is wrong and will attempt to re-authenticate. -- If is succeeds FCL knows that the code it has is valid. +- If it fails, FCL knows something is wrong and will attempt to re-authenticate. +- If is succeeds, FCL knows that the code it has is valid. - It creates a direct way for FCL to "verify" the user against the Wallet Provider. - It gives FCL a direct way to get Private Identity Information and Hooks. - The code can be passed to the backend to create a back-channel between the backend and the Wallet Provider. -When users return to a dapp, if the code FCL stored hasnt expired, FCL will make this request again in order to stay up to date with the latest informtaion. FCL may also intermitently request this information before some critial actions. +When users return to a dApp, if the code FCL stored hasnt expired, FCL will make this request again to stay up to date with the latest informtaion. FCL may also intermitently request this information before some critial actions. The hooks request should respond with the following JSON @@ -202,8 +198,7 @@ const privateHooks = { } ``` -When FCL requested the Public info from the chain it is expecting something like this. -It will be on the Wallet Provider to keep this information up to date. +When FCL requested the Public info from the chain, it expects something like this. It will be on the Wallet Provider to keep this information up to date. ```javascript const publicHooks = { @@ -230,7 +225,7 @@ const publicHooks = { } ``` -At this point FCL can be fairly confident who the currentUser is and is ready to initiate transactions the user can authorize. +At this point, FCL can be fairly confident who the currentUser is and is ready to initiate transactions the user can authorize. # Authorization @@ -240,10 +235,10 @@ The core concepts to this idea are: - Hooks tell FCL where to send authorization requests (Wallet Provider) - Wallet Provider responds imediately with: - - a back-channel where FCL can request the results of the authorization - - some optional local hooks ways the currentUser can authorize -- FCL will trigger the local hooks if they are for the currentUser -- FCL will poll the back-channel requesting updates until an approval or denial is given + - a back-channel where FCL can request the results of the authorization. + - some optional local hooks ways the currentUser can authorize. +- FCL will trigger the local hooks if they are for the currentUser. +- FCL will poll the back-channel requesting updates until an approval or denial is given. Below is the public authorization hook we received during the challenge above. @@ -278,7 +273,7 @@ POST https://provider.com/flow/authorize } ``` -FCL ise expecting something like this in response: +FCL expects something like this in response: ```javascript { @@ -301,15 +296,15 @@ FCL ise expecting something like this in response: } ``` -That local hook will be consumed by FCL, rendering an iframe with the endpoint as the src. If the user is already authenticated this screen could show them the Wallet Providers transaction approval process directly. -Because FCL isnt relying on any communication to or from the Iframe it can lock it down as much as possible, and remove it once the authorization is complete. -While displaying the local hook, it will request the status of the authorization from the `authorizationUpdates` hook. +That local hook will be consumed by FCL, which renders an iframe with the endpoint as the SRC. If the user is already authenticated, this screen could show them the Wallet Providers transaction approval process directly. + +Because FCL isnt relying on any communication to or from the Iframe, it can lock it down as much as possible, and remove it once the authorization is complete. While it displays the local hook, it will request the status of the authorization from the `authorizationUpdates` hook. ``` POST https://provider.com/flow/authorizations/4323 ``` -Expecting a response that has the same structure as the origin but without the local hooks: +We expect a response that has the same structure as the origin, but without the local hooks: ```javascript { @@ -323,8 +318,7 @@ Expecting a response that has the same structure as the origin but without the l } ``` -FCL will then follow the new `authorizationUpdates` hooks until the status changes to `"APPROVED"` or `"DECLINED"`. -If the authorization is declined it should include a reason if possible. +FCL will then follow the new `authorizationUpdates` hooks until the status changes to `"APPROVED"` or `"DECLINED"`. If the authorization is declined, it should include a reason if possible. ```javascript { @@ -333,7 +327,7 @@ If the authorization is declined it should include a reason if possible. } ``` -If the authorization is approved it should include a composite signature: +If the authorization is approved, it should include a composite signature: ```javascript { @@ -346,11 +340,11 @@ If the authorization is approved it should include a composite signature: } ``` -FCl can now submit the transaction to the Flow blockchain. +FCL can now submit the transaction to the Flow blockchain. # TL;DR Wallet Provider -Register Provider with FCL Handshake and implement 5 Endpoints. +Register Provider with FCL Handshake and implement five Endpoints. - `GET flow/authenticate` -> `parent.postMessage(..., l6n)` - `GET flow/hooks?code=___` -> `{ ...identityAndHooks }` @@ -358,4 +352,4 @@ Register Provider with FCL Handshake and implement 5 Endpoints. - `POST authorizations/:authorization_id` - `GET authorizations/:authorization_id` -![diagram showing current fcl authn and authz flow](./assets/fcl-ars-auth-v3.2.png) +![diagram showing current fcl authn and authz flow](./assets/fcl-ars-auth-v3.2.png) \ No newline at end of file diff --git a/docs/build/tools/wallet-provider-spec/index.md b/docs/build/tools/wallet-provider-spec/index.md index 33687bec50..4020ac7059 100644 --- a/docs/build/tools/wallet-provider-spec/index.md +++ b/docs/build/tools/wallet-provider-spec/index.md @@ -21,25 +21,26 @@ Flow Client Library (FCL) approaches the idea of blockchain wallets on Flow in a FCL acts in many ways as a protocol to facilitate communication and configuration between the different parties involved in a blockchain application. An _Application_ can use FCL to _authenticate_ users, and request _authorizations_ for transactions, as well as mutate and query the _Blockchain_. An application using FCL offers its _Users_ a way to connect and select any number of Wallet Providers and their Wallet Services. A selected _Wallet_ provides an Application's instance of FCL with configuration information about itself and its Wallet Services, allowing the _User_ and _Application_ to interact with them. -In the following paragraphs we'll explore ways in which you can integrate with FCL by providing implementations of various FCL services. +In the following paragraphs, we'll explore ways in which you can integrate with FCL and provide implementations of various FCL services. -The following services will be covered: +We'll cover the following services: - Authentication (Authn) Service - Authorization (Authz) Service - User Signature Service - Pre-Authz Service -# Service Methods +# Service methods -FCL Services are your way as a Wallet Provider of configuring FCL with information about what your wallet can do. FCL uses what it calls `Service Methods` to perform your supported FCL services. Service Methods are the ways FCL can talk to your wallet. Your wallet gets to decide which of these service methods each of your supported services use to communicate with you. +FCL Services are your way as a Wallet Provider to configure FCL with information about what your wallet can do. FCL uses what it calls `Service Methods` to perform your supported FCL services. Service Methods are the ways FCL can talk to your wallet. Your wallet gets to decide which of these service methods each of your supported services use to communicate with you. Sometimes services just configure FCL and that's it. An example of this can be seen with the Authentication Service and the OpenID Service. -With those two services you are simply telling FCL "here is a bunch of info about the current user". (You will see that those two services both have a `method: "DATA"` field in them. +With those two services, you simply tell FCL "here is a bunch of info about the current user". (You will see that those two services both have a `method: "DATA"` field in them. Currently these are the only two cases that can be a data service.) Other services can be a little more complex. For example, they might require a back and forth communication between FCL and the Service in question. -Ultimately we want to do this back and forth via a secure back-channel (https requests to servers), **but in some situations that isn't a viable option, so there is also a front-channel option**. + +Ultimately, we want to do this back and forth via a secure back-channel (https requests to servers), **but in some situations that isn't a viable option, so there is also a front-channel option**. Where possible, you should aim to provide a back-channel support for services, and only fall back to a front-channel if absolutely necessary. Back-channel communications use `method: "HTTP/POST"`, while front-channel communications use `method: "IFRAME/RPC"`, `method: "POP/RPC"`, `method: "TAB/RPC` and `method: "EXT/RPC"`. @@ -56,17 +57,17 @@ It's important to note that regardless of the method of communication, the data # Protocol schema definitions -In this section we define the schema of objects used in the protocol. While they are JavaScript objects, only features supported by JSON should be used. (Meaning that conversion of an object to and from JSON should not result in any loss.) +In this section, we define the schema of objects used in the protocol. While they are JavaScript objects, only features supported by JSON should be used. (Which means that conversion of an object to and from JSON should not result in any loss.) -For the schema definition language we choose TypeScript, so that the schema closely resembles the actual type definitions one would use when making an FCL implementation. +For the schema definition language, we choose TypeScript, so that the schema closely resembles the actual type definitions one would use to make an FCL implementation. -**Note that currently there are no official type definitions available for FCL. If you are using TypeScript, you will have to create your own type definitions (possibly based on the schema definitions presented in this document).** +**Note that currently there are no official type definitions available for FCL. If you use TypeScript, you will have to create your own type definitions (possibly based on the schema definitions presented in this document).** ## Common definitions -In this section we introduce some common definitions that the individual object definitions will be deriving from. +In this section, we introduce some common definitions that the individual object definitions will derive from. -First, let us define the kinds of FCL objects available: +First, lets define the kinds of FCL objects available: ```typescript type ObjectType = @@ -95,7 +96,7 @@ All FCL objects carry an `f_type` field so that their types can be identified at ## FCL objects -In this section we will define the FCL objects with each `ObjectType`. +In this section, we will define the FCL objects with each `ObjectType`. We also define the union of them to mean any FCL object: @@ -133,7 +134,7 @@ Each response back to FCL must be "wrapped" in a `PollingResponse`. The `status` In summary, zero or more `PENDING` responses should be followed by a non-pending response. It is entirely acceptable for your service to immediately return an `APPROVED` Polling Response, skipping a `PENDING` state. -See also [PollingResponse](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/polling-response.js). +See also [PollingResponse]. Here are some examples of valid `PollingResponse` objects: @@ -253,7 +254,7 @@ The meaning of the fields is as follows. - `method`: The service method this service uses. `DATA` means that the purpose of this service is just to provide the information in this `Service` object, and no active communication services are provided. - `uid`: A unique identifier for the service. A common scheme for deriving this is to use `'wallet-name#${type}'`, where `${type}` refers to the type of this service. - `endpoint`: Defines where to communicate with the service. - - When `method` is `EXT/RPC`, this can be an arbitrary unique string, and the extension will need to use it to identify its own services. A common scheme for deriving the `endpoint` is to use `'ext:${address}'`, where `${address}` refers to the wallet's address. (See `ServiceProvider` for more information.) + - When `method` is `EXT/RPC`, this can be an arbitrary unique string, and the extension will need to use it to identify its own services. A common scheme to derive the `endpoint` is to use `'ext:${address}'`, where `${address}` refers to the wallet's address. (See `ServiceProvider` for more information.) - `id`: The wallet's internal identifier for the user. If no other identifier is used, simply the user's flow account address can be used here. - `identity`: Information about the identity of the user. - `provider`: Information about the wallet. @@ -261,16 +262,16 @@ The meaning of the fields is as follows. See also: -- [authn](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/authn.js) -- [authz](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/authz.js) -- [user-signature](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/user-signature.js) -- [pre-authz](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/pre-authz.js) -- [open-id](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/open-id.js) -- [back-channel-rpc](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/back-channel-rpc.js) +- [authn] +- [authz] +- [user-signature] +- [pre-authz] +- [open-id] +- [back-channel-rpc] ### `Identity` -This object is used to define the identity of the user. +This object is used to define the user's identity. ```typescript interface Identity extends ObjectBase { @@ -304,7 +305,7 @@ interface ServiceProvider extends ObjectBase { The meaning of the fields is as follows. -- `address`: A flow account address owned by the wallet. It is unspecified what this will be used for. +- `address`: A flow account addressthat the wallet owns. It is unspecified what this will be used for. - `name`: The name of the wallet. - `description`: A short description for the wallet. - `icon`: An image URL for the wallet's icon. @@ -327,7 +328,7 @@ interface AuthnResponse extends ObjectBase { The meaning of the fields is as follows. - `addr`: The flow account address of the user. -- `services`: The list of services provided by the wallet. +- `services`: The list of services the wallet provides. ### `Signable` @@ -368,7 +369,7 @@ interface CompositeSignature extends ObjectBase { } ``` -See also [CompositeSignature](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/composite-signature.js). +See also [CompositeSignature]. ### `OpenID` @@ -406,8 +407,8 @@ This object is used to invoke a service when the `EXT/RPC` service method is use ## See also -- [local-view](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/local-view.js) -- [frame](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/frame.js) +- [local-view] +- [frame] # Service Methods @@ -419,8 +420,8 @@ This object is used to invoke a service when the `EXT/RPC` service method is use - The rendered iframe adds a listener and sends the `"FCL:VIEW:READY"` message. This can be simplified `WalletUtils.ready(callback)` - FCL will send the data to be dealt with: - Where `body` is the stuff you care about, `params` and `data` are additional information you can provide in the service object. -- The wallet sends back an `"APPROVED"` or `"DECLINED"` post message. (It will be a `f_type: "PollingResponse"`, which we will get to in a bit). This can be simplified using `WalletUtils.approve` and `WalletUtils.decline` - - If it's approved, the polling response's data field will need to be what FCL is expecting. +- The wallet sends back an `"APPROVED"` or `"DECLINED"` post message. (It will be a `f_type: "PollingResponse"`, which we will get to in a bit). This can be simplified with `WalletUtils.approve` and `WalletUtils.decline` + - If it's approved, the polling response's data field will need to be what FCL expects. - If it's declined, the polling response's reason field should say why it was declined. ```javascript @@ -465,48 +466,47 @@ graph LR ## HTTP/POST (Back Channel) -`HTTP/POST` initially sends a post request to the `endpoint` specified in the service, which should immediately return a `f_type: "PollingResponse"`. +`HTTP/POST` initially sends a post request to the `endpoint` specified in the service, which will immediately return a `f_type: "PollingResponse"`. Like `IFRAME/RPC`, `POP/RPC` or `TAB/RPC`, our goal is to eventually get an `APPROVED` or `DECLINED` polling response, and technically this endpoint could return one of those immediately. -But more than likely that isn't the case and it will be in a `PENDING` state (`PENDING` is not available to `IFRAME/RPC`, `POP/RPC` or `TAB/RPC`). +But more than likely, that isn't the case and it will be in a `PENDING` state (`PENDING` is not available to `IFRAME/RPC`, `POP/RPC` or `TAB/RPC`). When the polling response is `PENDING` it requires an `updates` field that includes a service, `BackChannelRpc`, that FCL can use to request an updated `PollingResponse` from. FCL will use that `BackChannelRpc` to request a new `PollingResponse` which itself can be `APPROVED`, `DECLINED` or `PENDING`. If it is `APPROVED` FCL will return, otherwise if it is `DECLINED` FCL will error. However, if it is `PENDING`, it will use the `BackChannelRpc` supplied in the new `PollingResponse` updates field. It will repeat this cycle until it is either `APPROVED` or `DECLINED`. -There is an additional optional feature that `HTTP/POST` enables in the first `PollingResponse` that is returned. -This optional feature is the ability for FCL to render an iframe, popup or new tab, and it can be triggered by supplying a service `type: "VIEW/IFRAME"`, `type: "VIEW/POP"` or `type: "VIEW/TAB"` and the `endpoint` that the wallet wishes to render in the `local` field of the `PollingResponse`. This is a great way for a wallet provider to switch to a webpage if displaying a UI is necessary for the service it is performing. +There is an additional optional feature that `HTTP/POST` activates in the first `PollingResponse` that is returned. This optional feature is the ability for FCL to render an iframe, popup or new tab, and you can trigger it when you supply a service `type: "VIEW/IFRAME"`, `type: "VIEW/POP"` or `type: "VIEW/TAB"` and the `endpoint` that the wallet wishes to render in the `local` field of the `PollingResponse`. This is a great way for a wallet provider to switch to a webpage if it's necessary to display a UI for the service it performs. ![HTTP/POST Diagram](https://raw.githubusercontent.com/onflow/fcl-js/master/packages/fcl-core/assets/service-method-diagrams/http-post.png) ## EXT/RPC (Front Channel) -`EXT/RPC` is used to enable and communicate between FCL and an installed web browser extension. (Though this specification is geared towards Chromium based browsers, it should be implementable in any browser with similar extension APIs available. From now on we will be using the word _Chrome_ to refer to Chromium based browsers.) +`EXT/RPC` is used to activate and communicate between FCL and an installed web browser extension. (Though this specification is geared towards Chromium based browsers, it should be implementable in any browser with similar extension APIs available. From now on we will be using the word _Chrome_ to refer to Chromium based browsers.) -An implementation of `EXT/RPC` needs to somehow enable communication between the application and the extension context. Implementing this is a bit more complex and usually relies on 3 key scripts to allow message passing between an installed extension and FCL. The separation of contexts enforced by Chrome and the availability of different Chrome APIs within those contexts require these scripts to be set up in a particular sequence so that the communication channels needed by FCL's `EXT/RPC` service method will work. +An implementation of `EXT/RPC` needs to somehow activate communication between the application and the extension context. Implementing this is a bit more complex and usually relies on three key scripts to allow message passing between an installed extension and FCL. The separation of contexts enforced by Chrome and the availability of different Chrome APIs within those contexts require these scripts to be set up in a particular sequence so that the communication channels needed by FCL's `EXT/RPC` service method will work. The following is an overview of these scripts and the functionality they need to support FCL: -- `background.js`: Used to launch the extension popup with `chrome.windows.create` if selected by the user from Discovery or set directly via `fcl.config.discovery.wallet` +- `background.js`: Used to launch the extension popup with `chrome.windows.create` if selected by the user from Discovery or set directly via `fcl.config.discovery.wallet`. - `content.js`: Used to proxy messages between the application to the extension via `chrome.runtime.sendMessage`. - `script.js`: Injected by `content.js` into the application's HTML page. It appends the extension authn service to the `window.fcl_extensions` array on page load. This allows FCL to confirm installation and send extension details to Discovery or launch your wallet as the default wallet. -An example and guide showing how to build an FCL compatible wallet extension on Flow can be found [here](https://github.com/onflow/wallet-extension-example). +An example and guide that shows how to build an FCL compatible wallet extension on Flow can be found [here]. -Once the extension is enabled (for example when the user selects it through the discovery service), the following communication protocol applies. (The term _send_ should specifically refer to using `window.postMessage` in the application context, as this is the only interface between the application and the extension. Note that since `window.postMessage` broadcasts messages to all message event handlers, care should be taken by each party to filter only the messages targeted at them.) +After the extension is activated (for example when the user selects it through the discovery service), the following communication protocol applies. (The term _send_ should specifically refer to using `window.postMessage` in the application context, as this is the only interface between the application and the extension. Since `window.postMessage` broadcasts messages to all message event handlers, each party should be careful to filter only the messages targeted at them.) -- An `ExtensionServiceInitiationMessage` object is sent by FCL. It is the extension's responsibility to inspect the `endpoint` field of the service, and only activate itself (e.g. by opening a popup) if it is the provider of this service. -- The extension should respond by sending a `Message` with type `FCL:VIEW:READY`. (Usually this message will originate from the extension popup, and be relayed to the application context.) +- FCL sends an `ExtensionServiceInitiationMessage` object. It is the extension's responsibility to inspect the `endpoint` field of the service, and only activate itself (for example, via a popup) if it is the provider of this service. +- To respond, the extension should send a `Message` with type `FCL:VIEW:READY`. (Usually this message will originate from the extension popup, and be relayed to the application context.) - FCL will send a `Message` with type `FCL:VIEW:READY:RESPONSE`. Additional fields specific to the service (such as `body`, `params` or `data`) are usually present. See the section on the specific service for a description of these fields. - The wallet sends back a `Message & PollingResponse` with type `FCL:VIEW:RESPONSE` with either an `APPROVED` or `DECLINED` status. - - If it's approved, the polling response's data field will need to be what FCL is expecting. - - If it's declined, the polling response's reason field should say why it was declined. + - If it's approved, the polling response's data field will need to be what FCL expects. + - If it's declined, the polling response's reason field should say why. -The extension can send a `Message` with type `FCL:VIEW:CLOSE` at any point during this protocol to indicate an interruption. This will halt FCL's current routine. On the other hand, once a `PollingResponse` with either an `APPROVED` or `DECLINED` status was sent, the protocol is considered finished, and the extension should not send any further messages as part of this exchange. +The extension can send a `Message` with type `FCL:VIEW:CLOSE` at any point during this protocol to indicate an interruption. This will halt FCL's current routine. On the other hand, after a `PollingResponse` with either an `APPROVED` or `DECLINED` status is sent, the protocol is considered finished, and the extension should not send any further messages as part of this exchange. Conversely, when FCL sends a new `ExtensionServiceInitiationMessage`, the previous routine is interrupted. (This is the case even when the new service invocation is targeted at a different extension.) -Note that as a consequence of the above restrictions, only single service invocation can be in progress at a time. +Because of the above restrictions, only single service invocation can be in progress at a time. Here is a code example for how an extension popup might send its response: @@ -534,9 +534,9 @@ chrome.tabs.sendMessage(tabs[0].id, { - `params` will be added onto the `endpoint` as query params. - `data` will be included in the body of the `HTTP/POST` request or in the `FCL:VIEW:READY:RESPONSE` for a `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. -# Authentication Service +# Authentication service -In the following examples, we'll walk you through the process of building an authentication service. +In the following examples, we'll walk you through the process of how to build an authentication service. In FCL, wallets are configured by passing in a wallet provider's authentication URL or extension endpoint as the `discovery.wallet` config variable. @@ -553,9 +553,9 @@ config({ }); ``` -If the method specified is `IFRAME/RPC`, `POP/RPC` or `TAB/RPC`, then the URL specified as `discovery.wallet` will be rendered as a webpage. If the configured method is `EXT/RPC`, `discovery.wallet` should be set to the extension's `authn` `endpoint`. Otherwise, if the method specified is `HTTP/POST`, then the authentication process will happen over HTTP requests. (While authentication can be accomplished using any of those service methods, this example will use the `IFRAME/RPC` service method.) +If the method specified is `IFRAME/RPC`, `POP/RPC` or `TAB/RPC`, then the URL specified as `discovery.wallet` will be rendered as a webpage. If the configured method is `EXT/RPC`, `discovery.wallet` should be set to the extension's `authn` `endpoint`. Otherwise, if the method specified is `HTTP/POST`, then the authentication process will happen over HTTP requests. (While authentication can be accomplished with any of those service methods, this example will use the `IFRAME/RPC` service method.) -Once the Authentication webpage is rendered, the extension popup is enabled, or the API is ready, you then need to tell FCL that it is ready. You will do this by sending a message to FCL, and FCL will send back a message with some additional information that you can use about the application requesting authentication on behalf of the user. +After the Authentication webpage is rendered, the extension popup is activated, or the API is ready, you then need to tell FCL that it is ready. To do this, send a message to FCL, and FCL will send back a message with some additional information that you can use about the application that requests authentication on behalf of the user. The following example is using the `IFRAME/RPC` method. Your authentication webpage will likely resemble the following code: @@ -601,34 +601,31 @@ WalletUtils.ready(callback) During authentication, the application has a chance to request to you what they would like you to send back to them. These requests are included in the `FCL:VIEW:READY:RESPONSE` message sent to the wallet from FCL. -An example of such a request is the OpenID service. The application can request for example that you to send them the email address of the current user. The application requesting this information does not mean you need to send it. It's entirely optional for you to do so. However, some applications may depend on you sending the requested information back, and should you decline to do so it may cause the application to not work. +An example of such a request is the OpenID service. The application can request for example that you to send them the email address of the current user. The application that requests this information does not mean you need to send it. It's entirely optional for you to do so. However, some applications may depend on whether you send the requested information back, and should you decline to do so, it may cause the application to not work. -In the config they can also tell you a variety of things about them, such as the name of their application or a url for an icon of their application. You can use these pieces of information to customize your wallet's user experience should you desire to do so. +In the config, they can also tell you a variety of things about them, such as the name of their application or a URL for an icon of their application. You can use these pieces of information to customize your wallet's user experience should you desire to do so. Your wallet having a visual distinction from the application, but still a seamless and connected experience is our goal here. -Whether your authentication process happens using a webpage with the `IFRAME/RPC`, `POP/RPC` or `TAB/RPC` methods, via an enabled extension using the `EXT/RPC` method, or using a backchannel to an API with the `HTTP/POST` method, the handshake is the same. The same messages are sent in all methods, however the transport mechanism changes. For `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC` methods, the transport is `window.postMessage()`, with the `HTTP/POST` method, the transport is HTTP post messages. +Whether your authentication process happens using a webpage with the `IFRAME/RPC`, `POP/RPC` or `TAB/RPC` methods, via an actiated extension using the `EXT/RPC` method, or via a backchannel to an API with the `HTTP/POST` method, the handshake is the same. The same messages are sent in all methods, however the transport mechanism changes. For `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC` methods, the transport is `window.postMessage()`, with the `HTTP/POST` method, the transport is HTTP post messages. As always, you must never trust anything you receive from an application. Always do your due-diligence and be alert as you are the user's first line of defense against potentially malicious applications. -### Authenticate your User +### Authenticate your user It's important that you are confident that the user is who the user claims to be. -Have them provide enough proof to you that you are okay with passing their details back to FCL. -Using Blocto as an example, an authentication code is sent to the email a user enters at login. -This code can be used as validation and is everything Blocto needs to be confident in the user's identity. +Have them provide enough proof to you that you are okay with passing their details back to FCL. As an example, a Blotco authentication code is sent to the email a user enters at login. This code can be used as validation and is everything Blocto needs to be confident in the user's identity. -### Once you know who your User is +### When you know who your user is -Once you're confident in the user's identity, we can complete the authentication process. +When you're confident in the user's identity, we can complete the authentication process. -The authentication process is complete once FCL receives back a response that configures FCL with FCL Services for the current user. This response is extremely important to FCL. At its core it tells FCL who the user is, and then via the included services it tells FCL how the user authenticated, how to request transaction signatures, how to get a personal message signed and the user's email and other details if requested. In the future it may also include many more things! +The authentication process is complete when FCL receives back a response that configures FCL with FCL Services for the current user. This response is extremely important to FCL. At its core it tells FCL who the user is, and then via the included services it tells FCL how the user authenticated, how to request transaction signatures, how to get a personal message signed and the user's email and other details if requested. In the future it may also include many more things! You can kind of think of FCL as a plugin system. But since those plugins exist elsewhere outside of FCL, FCL needs to be configured with information on how to communicate with them. -What you are sending back to FCL is everything that it needs to communicate with the plugins that you are supplying. -Your wallet is like a plugin to FCL, and these details tell FCL how to use you as a plugin. +What you send back to FCL is everything that it needs to communicate with the plugins that you supply. Your wallet is like a plugin to FCL, and these details tell FCL how to use you as a plugin. Here is an example of an authentication response: @@ -729,7 +726,7 @@ WalletUtils.approve({ }) ``` -### Stopping an Authentication Process +### Stop an authentication process From any frame, you can send a `FCL:VIEW:CLOSE` post message to FCL, which will halt FCL's current routine and close the frame. @@ -739,7 +736,7 @@ import { WalletUtils } from '@onflow/fcl'; WalletUtils.sendMsgToFCL('FCL:VIEW:CLOSE'); ``` -# Authorization Service +# Authorization service Authorization services are depicted with with a `type: "authz"`, and a `method` of either `HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. They are expected to eventually return a `f_type: "CompositeSignature"`. @@ -767,10 +764,9 @@ An authorization service is expected to know the Account and the Key that will b FCL will use the `method` provided to request an array of composite signature from authorization service (Wrapped in a `PollingResponse`). The authorization service will be sent a `Signable`. -The service is expected to construct an encoded message to sign from `Signable.voucher`. -It then needs to hash the encoded message, and prepend a required [transaction domain tag](https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/encode/encode.ts#L18-L21). -Finally it signs the payload with the user/s keys, producing a signature. -This signature, as a HEX string, is sent back to FCL as part of the `CompositeSignature` which includes the user address and keyID in the data property of a `PollingResponse`. +The service is expected to construct an encoded message to sign from `Signable.voucher`. It then needs to hash the encoded message, and prepend a required [transaction domain tag]. + +Finally it signs the payload with the user/s keys, producing a signature. This signature, as a HEX string, is sent back to FCL as part of the `CompositeSignature` which includes the user address and keyID in the data property of a `PollingResponse`. ```elixir signature = @@ -782,7 +778,7 @@ signature = |> convert_to_hex ``` -The eventual response back from the authorization service should resolve to something like this: +The eventual response back from the authorization service will resolve to something like this: ```javascript { @@ -808,12 +804,12 @@ WalletUtils.CompositeSignature(addr: String, keyId: Number, signature: Hex) ``` -# User Signature Service +# User Signature service User Signature services are depicted with a `type: "user-signature"` and a `method` of either `HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. They are expected to eventually return an array of `f_type: "CompositeSignature"`. -The User Signature service is a stock/standard service. +The User Signature service is a stock (standard) service. ```javascript { @@ -828,10 +824,9 @@ The User Signature service is a stock/standard service. } ``` -FCL will use the `method` provided to request an array of composite signatures from the user signature service (Wrapped in a `PollingResponse`). -The user signature service will be sent a `Signable`. -The service is expected to tag the `Signable.message` and then sign it with enough keys to produce a full weight. -The signatures need to be sent back to FCL as HEX strings in an array of `CompositeSignatures`. +FCL will use the `method` provided to request an array of composite signatures from the user signature service (Wrapped in a `PollingResponse`). The user signature service will be sent a `Signable`. + +The service is expected to tag the `Signable.message` and then sign it with enough keys to produce a full weight. The signatures need to be sent back to FCL as HEX strings in an array of `CompositeSignatures`. ```javascript // Pseudocode: @@ -872,14 +867,13 @@ The eventual response back from the user signature service should resolve to som } ``` -# Pre Authz Service +# Pre Authz service -This is a strange one, but extremely powerful. This service should be used when a wallet is responsible for an account that's signing as multiple roles of a transaction, and wants the ability to change the accounts on a per role basis. +This is a strange one, but extremely powerful. Use this service when a wallet is responsible for an account that signs as multiple roles of a transaction, and wants the ability to change the accounts on a per role basis. -Pre Authz Services are depicted with a `type: "pre-authz"` and a `method` of either `HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. -They are expected to eventually return a `f_type: "PreAuthzResponse"`. +Pre Authz Services are depicted with a `type: "pre-authz"` and a `method` of either `HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, `TAB/RPC` or `EXT/RPC`. They are expected to eventually return a `f_type: "PreAuthzResponse"`. -The Pre Authz Service is a stock/standard service. +The Pre Authz Service is a stock (standard) service. ```javascript { @@ -894,11 +888,9 @@ The Pre Authz Service is a stock/standard service. } ``` -FCL will use the `method` provided to request a `PreAuthzReponse` (Wrapped in a `PollingResponse`). -The Authorizations service will be sent a `PreSignable`. -The pre-authz service is expected to look at the `PreSignable` and determine the breakdown of accounts to be used. -The pre-authz service is expected to return `Authz` services for each role it is responsible for. -A pre-authz service can only supply roles it is responsible for. +FCL will use the `method` provided to request a `PreAuthzReponse` (Wrapped in a `PollingResponse`). The Authorizations service will be sent a `PreSignable`. + +The pre-authz service is expected to look at the `PreSignable` and determine the breakdown of accounts to be used. The pre-authz service is expected to return `Authz` services for each role it is responsible for. A pre-authz service can only supply roles it is responsible for. If a pre-authz service is responsible for multiple roles, but it wants the same account to be responsible for all the roles, it will need to supply an Authz service per role. The eventual response back from the pre-authz service should resolve to something like this: @@ -939,16 +931,15 @@ The eventual response back from the pre-authz service should resolve to somethin # Authentication Refresh Service -Since synchronization of a user's session is important to provide a seamless user experience when using an app and transacting with the Flow Blockchain, a way to confirm, extend, and refresh a user session can be provided by the wallet. +Since synchronization of a user's session is important to provide a seamless user experience when you use an app and transact with the Flow Blockchain, a way to confirm, extend, and refresh a user session can be provided by the wallet. Authentication Refresh Services should include a `type: "authn-refresh"`, `endpoint`, and supported `method` (`HTTP/POST`, `IFRAME/RPC`, `POP/RPC`, or `EXT/RPC`). -FCL will use the `endpoint` and service `method` provided to request updated authentication data. -The `authn-refresh` service should refresh the user's session if necessary and return updated authentication configuration and user session data. +FCL will use the `endpoint` and service `method` provided to request updated authentication data. The `authn-refresh` service should refresh the user's session if necessary and return updated authentication configuration and user session data. The service is expected to return a `PollingResponse` with a new `AuthnResponse` as data. If user input is required, a `PENDING` `PollingResponse` can be returned with a `local` view for approval/re-submission of user details. -The Authentication Refresh Service is a stock/standard service. +The Authentication Refresh Service is a stock (standard) service. ```javascript { @@ -1004,3 +995,18 @@ The eventual response back from the `authn-refresh` service should resolve to an } } ``` + + + +[PollingResponse]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/polling-response.js +[authn]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/authn.js +[authz]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/authz.js +[user-signature]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/user-signature.js +[pre-authz]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/pre-authz.js +[open-id]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/open-id.js +[back-channel-rpc]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/back-channel-rpc.js +[CompositeSignature]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/composite-signature.js +[local-view]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/local-view.js +[frame]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/frame.js +[here]: https://github.com/onflow/wallet-extension-example +[transaction domain tag]: https://github.com/onflow/fcl-js/blob/master/packages/sdk/src/encode/encode.ts#L18-L21 \ No newline at end of file diff --git a/docs/build/tools/wallet-provider-spec/provable-authn.md b/docs/build/tools/wallet-provider-spec/provable-authn.md index fde05e11f2..9d0304404f 100644 --- a/docs/build/tools/wallet-provider-spec/provable-authn.md +++ b/docs/build/tools/wallet-provider-spec/provable-authn.md @@ -1,28 +1,28 @@ # Provable Authn -In order to improve UX/DX and encourage seamless integration with App backends and services, `fcl.authenticate` has been upgraded. +To improve UX/DX and encourage seamless integration with App backends and services, `fcl.authenticate` has been upgraded. + +Additional data is sent in the body of `FCL:VIEW:READY:RESPONSE`. This data includes what the wallet needs to build a message for signing with the user’s private keys. -Additional data is sent in the body of `FCL:VIEW:READY:RESPONSE`. This data includes what the wallet needs to build a message for signing with the user’s private key/s. The signature can be returned as part of an optional `account-proof` service with the `FCL:VIEW:RESPONSE`. When provided by the wallet, this **signature** and additional **account-proof data** is available to the App via `fcl.currentUser` services. The service data can be used to recreate the message, and verify the signature on the Flow Blockchain. -For example, it can be sent to the App’s backend and after validating the signature and the other account-proof data, it can safely associate the included account address to a user and log them in. +For example, it can be sent to the App’s backend and after it validates the signature and the other account-proof data, it can safely associate the included account address to a user and log them in. --- ## TL;DR Wallet Provider 1. Wallet receives Authn `FCL:VIEW:READY:RESPONSE` request and parses out the `appIdentifier`, and `nonce`. -2. The wallet authenticates the user however they choose to do, and determines the user's account `address` -4. The wallet must validate the `appIdentifier` against the RFC 6454 origin of the request if it matches the - format of a [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) URI. Requests with a mismatch should be rejected. Some legacy systems may use arbitrary strings as `appIdentifier` and not [RFC 6454](https://www.rfc-editor.org/rfc/rfc6454.html) origins. In this case, wallets should display a warning to the user that the app identifier does not match the origin of the request. -5. Wallet prepares and signs the message: - - Encodes the `appIdentifier`, `nonce`, and `address` along with the `"FCL-ACCOUNT-PROOF-V0.0"` domain separation tag, [using the encoding scheme described below](#account-proof-message-encoding). - - Signs the message with the `signatureAlgorithm` and `hashAlgorithm` specified on user's key. **It is highly recommended that the wallet display the message data and receive user approval before signing.** -6. Wallet sends back this new service and data along with the other service configuration when completing Authn. +2. The Wallet authenticates the user however they choose to do, and determines the user's account `address`. +3. The wallet must validate the `appIdentifier` against the RFC 6454 origin of the request if it matches the format of a [RFC 3986] URI. Requests with a mismatch should be rejected. Some legacy systems may use arbitrary strings as `appIdentifier` and not [RFC 6454] origins. In this case, wallets should display a warning to the user that the app identifier does not match the origin of the request. +4. Wallet prepares and signs the message: + - Encodes the `appIdentifier`, `nonce`, and `address` along with the `"FCL-ACCOUNT-PROOF-V0.0"` domain separation tag, [via the encoding scheme described below]. + - Signs the message with the `signatureAlgorithm` and `hashAlgorithm` specified on user's key. **We strongly recommend that the wallet display the message data and receive user approval before signing.** +6. Wallet sends back this new service and data along with the other service configuration when it completes Authn. -### Account Proof Message Encoding +### Account proof message encoding The account proof message is encoded as follows: @@ -40,12 +40,12 @@ with the following values: - `ACCOUNT_PROOF_DOMAIN_TAG` is the constant `"FCL-ACCOUNT-PROOF-V0.0"`, encoded as UTF-8 byte array and right-padded with zero bytes to a length of 32 bytes. - `APP_IDENTIFIER` is an arbitrary length string. -- `ADDRESS` is a byte array containing the address bytes, left-padded with zero bytes to a length of 8 bytes. +- `ADDRESS` is a byte array that contains the address bytes, left-padded with zero bytes to a length of eight bytes. - `NONCE` is an byte array with a minimum length of 32 bytes. -`RLP_ENCODE` is a function that performs [RLP encoding](https://eth.wiki/fundamentals/rlp) and returns the encoded value as bytes. +`RLP_ENCODE` is a function that performs [RLP encoding] and returns the encoded value as bytes. -### JavaScript Signing Example +### JavaScript signing example ```javascript // Using WalletUtils @@ -103,3 +103,10 @@ WalletUtils.onMessageFromFcl( } } ``` + + + +[RFC 3986]: https://www.rfc-editor.org/rfc/rfc3986 +[RFC 6454]: https://www.rfc-editor.org/rfc/rfc6454.html +[via the encoding scheme described below]: #account-proof-message-encoding +[RLP encoding]: https://eth.wiki/fundamentals/rlp \ No newline at end of file diff --git a/docs/build/tools/wallet-provider-spec/user-signature.md b/docs/build/tools/wallet-provider-spec/user-signature.md index b1d751c67e..efbc7442c7 100644 --- a/docs/build/tools/wallet-provider-spec/user-signature.md +++ b/docs/build/tools/wallet-provider-spec/user-signature.md @@ -2,69 +2,67 @@ ## Status -- **Last Updated:** June 1st 2021 +- **Last Updated:** June 1st, 2021 - **Stable:** Yes - **Risk of Breaking Change:** Low - **Compatibility:** `>= @onflow/fcl@0.0.71` -# Overview and Introduction +# Overview and introduction **Personally sign data via FCL Compatible Wallets** -**FCL** now incldues **`signUserMessage()`** which allows for the sending of unencrypted message data to a connected wallet provider or service to be signed with a user's private key. +**Flow Client Library (FCL)** now incldues **`signUserMessage()`**, which allows you to send unencrypted message data to a connected wallet provider or service to be signed with a user's private key. -An application or service can verify a signature against a user's public key on the **Flow Blockchain**, providing proof a user controls the account's private key. +An application or service can verify a signature against a user's public key on the **Flow Blockchain**, which provides proof a user controls the account's private key. **Use Cases** -- **Authentication**: Cryptographically verify the ownership of a **Flow** account by signing a piece of data using a private key +- **Authentication**: Sign a piece of data with a provate key to cryptographically verify the ownership of a **Flow** account. - **Improved Application Login** - - **Increased security**: Arguably more secure than proof of ownership by email/password - - **Simplified UX**: No application password required - - **Increased privacy**: No email or third party authentication service needed -- **Message Validation**: Assuring that a message sent or received has not been tampered with + - **Increased security**: Arguably more secure than proof of ownership by email and password. + - **Simplified UX**: No application password required. + - **Increased privacy**: No email or third party authentication service needed. +- **Message Validation**: Assure that a message sent or received wasn't tampered with. - **Multisig contracts** - **Decentralised exchanges** - **Meta transactions** -# Config and Authentication +# Config and authentication As a prerequisite, **FCL** is configured to point to the Wallet Provider's Authentication Endpoint. No additional configuration is required. -> During development (and on mainnet) FCL can be configured to use the wallet directly by -> setting the **Wallet Discovery Url** to the wallet provider's **Authentication Endpoint** -> by configuring fcl like this `config().put("discovery.wallet", "https://my-awesome-wallet-provider.com/fcl/authenticate")`. +> During development (and on mainnet), you can configure Flow Client Library (FCL) to use the wallet directly. To do this, set the **Wallet Discovery Url** to the wallet provider's **Authentication Endpoint** by configuring FCL like this `config().put("discovery.wallet", "https://my-awesome-wallet-provider.com/fcl/authenticate")`. -Common Configuration Keys and additional info can be found here [How to Configure FCL](../clients/fcl-js/packages-docs/fcl/index.md#common-configuration-keys) +For more information about common Configuration Keys, see [How to Configure FCL] -1. A user initiates authentication with the wallet provider via application UI -2. The wallet confirms a user's identity and sends back information used to configure **FCL** for future user actions in the application -3. Included in the authentication response should be the provider's [Key Services](#) including a **`user-signature`** service for use with **`signUserMessage()`** +1. A user initiates authentication with the wallet provider via application UI. +2. The wallet confirms a user's identity and sends back information used to configure **FCL** for future user actions in the application. +3. Included in the authentication response should be the provider's [Key Services](#) which includes a **`user-signature`** service for use with **`signUserMessage()`**. -# User Signature Service +# User signature service -A [user-signature service](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/user-signature.js) is a standard service, with methods for **IFRAME/RPC** or **HTTP/POST**. +A [user-signature service] is a standard service, with methods for **IFRAME/RPC** or **HTTP/POST**. -The `user-signature` service receives a signable message from **FCL** and returns a standard [PollingResponse](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/polling-response.js#L5) with an array of [CompositeSignatures](https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/composite-signature.js#L4) or `null` as the data. +The `user-signature` service receives a signable message from **FCL** and returns a standard [PollingResponse] with an array of [CompositeSignatures] or `null` as the data. A status of **Approved** needs to have an array of composite signatures as data. A status of **Declined** needs to include a reason why. A **Pending** status needs to include an updates service and can include a local. -A service using the **`IFRAME/RPC`** method can only respond with approved or declined, as pending is not valid for iframes. +A service that uses the **`IFRAME/RPC`** method can only respond with `approved` or `declined`, as `pending` is not valid for iframes. When `signUserMessage()` is called by the application, **FCL** uses the service method to decide how to send the signable to the wallet. The Wallet is responsible for prepending the signable with the correct `UserDomainTag`, hashing, and signing the message. -# Signing Sequence +# Signing sequence -1. Application sends message to signing service. **FCL expects a hexadecimal string** -3. Wallet/Service tags the message with required `UserDomainTag` (see below), hashes, and signs using the `signatureAlgorithm` specified on account key -2. Wallet makes available a Composite Signature consisting of `addr`, `keyId`, and `signature` **as a hex string** +1. Application sends message to signing service. **FCL expects a hexadecimal string**. +3. Wallet and Service tags the message with required `UserDomainTag` (see below), hashes, and signs with the `signatureAlgorithm` specified on account key. +2. Wallet makes available a Composite Signature that consists of `addr`, `keyId`, and `signature` **as a hex string**. ### UserDomainTag The **`UserDomainTag`** is the prefix of all signed user space payloads. @@ -75,7 +73,7 @@ Before hashing and signing the message, the wallet must add a specified DOMAIN T A domain tag is encoded as **UTF-8 bytes, right padded to a total length of 32 bytes**, prepended to the message. -The signature can now be verified on the Flow blockchain. The following illustrates an example using `fcl.verifyUserSignatures` +The signature can now be verified on the Flow blockchain. The following illustrates an example with `fcl.verifyUserSignatures` ```javascript /** @@ -100,7 +98,13 @@ The signature can now be verified on the Flow blockchain. The following illustra ## TL;DR Wallet Provider - Register with **FCL** and provide signing service endpoint. No further configuration is needed. -- On receipt of message, prompt user to approve or decline -- Prepend `UserDomainTag`, hash and sign the message with the signatureAlgorithm specified on user's key -- Return a standard `PollingResponse` with an array of `CompositeSignatures` as data or `null` and `reason` if declined +- On receipt of message, prompt user to approve or decline. +- Prepend `UserDomainTag`, hash and sign the message with the signatureAlgorithm specified on user's key. +- Return a standard `PollingResponse` with an array of `CompositeSignatures` as data or `null` and `reason` if declined. + + +[How to Configure FCL]: ../clients/fcl-js/packages-docs/fcl/index.md#common-configuration-keys +[user-signature service]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/user-signature.js +[PollingResponse]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/polling-response.js#L5 +[CompositeSignatures]: https://github.com/onflow/fcl-js/blob/master/packages/fcl-core/src/normalizers/service/composite-signature.js#L4 \ No newline at end of file