Third Generation FCL Compatible Wallet Provider Docs
Status
- Last Updated: Dec 1st 2020
- Stable: Yes
- Risk of Breaking Change: Medium
- Compatibility:
>= @onflow/fcl@0.0.67
This document is a rough draft and very much a work in progress, the concepts here are an extension and refinement on the Second Generation FCL Compatible Wallet Provider Docs. We will try as hard as possible to not introduce any breaking changes for wallets that already exist.
Overview
In general one of the main goals of FCL is to create a system of back-channel communications between the applications and the service providers. The user should be able to initiate this back-channel via the application, discover their desired service and then verify the connection from the service. FCL aims to standardise the discovery and communications between these three parties (User, Application, Service).
Table of Contents
- Overview
- Discovery of Identity
- Identity as Configuration
- Key Services
- authn
- authz
- pre-authz
- back-channel-rpc
- frame
- Service Methods
- HTTP/POST
- HTTP/RPC
- IFRAME/RPC
- Responses
- Polling Response
- Composite Signature
- PreAuthzResponse
Discovery of Identity
TODO: Write
Identity as Configuration
TODO: Write
Key Services
Services are abilities an account can perform. You can kind of think of them as a function where its behaviour and input is defined by the service. The returned value will be dependant on the type of service it is.
authn
Not yet implemented
Returns a __TODO__
with a __TODO__
as the data.
{
f_type: "Service",
f_vsn: "1.0.0",
type: "authn",
uid: "____",
endpoint: "https://____",
id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", // wallets internal id for the user
identity: {
address: "0x_____", // users flow address
},
provider: {
address: "0x______", // providers flow address
name: "Best Wallet", // Name of wallet
icon: "https://___", // Img url for wallet logo
description: "Description of the best wallet",
},
}
authz
Returns a PollingResponse
with a CompositeSignature
as the data.
{
f_type: "Service",
f_vsn: "1.0.0",
type: "authz",
uid: "____",
endpoint: "https://___",
method: "HTTP/POST", // Service Methods: HTTP/POST | IFRAME/RPC | HTTP/RPC
identity: {
address: "0x_________",
keyId: 0
},
data: {}, // included in body of authz request
params: {}, // included as query params on endpoint url
}
pre-authz
Returns a PollingResponse
with a PreAuthzResponse
as the data.
{
f_type: "Service",
f_vsn: "1.0.0",
type: "pre-authz",
uid: "____",
endpoint: "https://___",
method: "HTTP/POST", // Service Methods: HTTP/POST | IFRAME/RPC | HTTP/RPC
data: {}, // included in body of pre-authz request
params: {}, // included as query params on endpoint url
}
back-channel-rpc
Returns a PollingResponse
with an inherited return value
as the data.
{
f_type: "Service",
f_vsn: "1.0.0",
type: "back-channel-rpc",
endpoint: "https://___",
method: "HTTP/GET", // HTTP/GET | HTTP/POST (these are actual http methods, not a service method)
data: {}, // included in body of back-channel-rpc request, if supplied method must be "HTTP/POST"
params: {}, // included as query params on endpoint url
}
frame
Returns a PollingResponse
with an inherited return value
as the data.
{
f_type: "Service",
f_vsn: "1.0.0",
type: "frame",
endpoint: "https://___",
data: {}, // included in body of ready message
params: {}, // included as query params on endpoint url
}
Service Methods
HTTP/POST
FCL ENDPOINT
| |
|---[fetchService(service, body)]--->|
| |
|<-[PollingResponse(PENDING)]--------|
| |
?-----[render(resp.local)] |
| |
+-PENDING-->|-----[poll(resp.updates)]---------->|
| | |
+<----------+-------[PollingResponse]------------|
| |
+-APPROVED->|-----[CONTINUE]
|
+-DECLINED--------[ERROR]
// Example of calling fetchService with an HTTP/POST Service to showcase endpoint/params/data
async function fetchService(service, body) {
return fetch(url(service.endpoint, service.params), {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({...body, data: service.data}),
}).then(d => d.json())
} // REturns a PollingResonse Data Type
HTTP/RPC
This is currently aliased to HTTP/POST, if you want this functionality return a PollingResponse that is either
APPROVED
orDECLINED
.
IFRAME/RPC
FCL
|
|----------------------------+-[frame(service, body)]--+
| | |
| | ENDPOINT |
| | | |
|<------[message(FCL:FRAME:READY)]--------| |
| | | |
|----------[message(fcl:sign)]----------->| // Will eventually be FCL:FRAME:RPC
| | | |
|<------[message(PollingResponse)]--------| |
| | | |
| | |
|-----[CLOSE]--------------->+-------------------------+
// Example of calling frame(service, body) to showcase endpoint/params/data
async function frame(service, body) {
const [sendMessage, on] = renderFrame(url(service.endpoint, service.params))
on("FCL:FRAME:READY", () => sendMessage({...body, data: service.data}))
}
Responses
PollingResponse
{
f_type: "PollingResponse",
f_vsn: "1.0.0",
status: "PENDING", // PENDING | APPROVED | DECLINED
reason: null, // if status is DECLINED this is a string that specifies why
data: null, // if status is APPROVED this is the value FCL needs
updates: null, // Optional `back-channel-rpc` Service (Required if status is PENDING)
local: null, // Optional `frame` Service
}
CompositeSignature
{
f_type: "CompositeSignature",
f_vsn: "1.0.0",
addr: "____", // Flow Address (sans-prefix)
keyId: 3,
signature: "______", // Signature as a hex string
}
PreAuthzResponse
If the same Flow Address/KeyId pair is used in multiple different roles, they will need to show up in the result once for each role.
{
f_type: "PreAuthzResponse",
f_vsn: "1.0.0",
proposer: null, // Singular Authz Service,
payer: [], // Multiple Authz Services (for same Flow Address (different KeyId))
authorization: [], // Multiple Authz Services (for same Flow Address (different KeyId))
}