Provable Authn
In order 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 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.
TL;DR Wallet Provider
- Wallet receives Authn
FCL:VIEW:READY:RESPONSE
request and parses out theappIdentifier
, andnonce
. - The wallet authenticates the user however they choose to do, and determines the user's account
address
- The wallet must validate the
appIdentifier
against the RFC 6454 origin of the request if it matches the format of a RFC 3454 URI. Requests with a mismatch should be rejected. Some legacy systems may use arbitrary strings asappIdentifier
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. - Wallet prepares and signs the message:
- Encodes the
appIdentifier
,nonce
, andaddress
along with the"FCL-ACCOUNT-PROOF-V0.0"
domain separation tag, using the encoding scheme described below. - Signs the message with the
signatureAlgorithm
andhashAlgorithm
specified on user's key. It is highly recommended that the wallet display the message data and receive user approval before signing.
- Encodes the
- Wallet sends back this new service and data along with the other service configuration when completing Authn.
Account Proof Message Encoding
The account proof message is encoded as follows:
_10MESSAGE = _10 USER_DOMAIN_TAG ||_10 RLP_ENCODE([_10 APP_IDENTIFIER, _10 ADDRESS, _10 NONCE_10 ])
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.NONCE
is an byte array with a minimum length of 32 bytes.
RLP_ENCODE
is a function that performs RLP encoding and returns the encoded value as bytes.
JavaScript Signing Example
_31// Using WalletUtils_31import {WalletUtils} from "@onflow/fcl"_31_31WalletUtils.onMessageFromFcl(_31 (data, {origin}) => {_31 const {address, nonce, appIdentifier} = data.data_31_31 // Validate the origin_31 if (origin !== appIdentifier) {_31 throw new Error("Invalid origin")_31 }_31_31 const message = WalletUtils.encodeAccountProof(_31 appIdentifier, // A human readable string to identify your application during signing_31 address, // Flow address of the user authenticating_31 nonce, // minimum 32-btye nonce_31 )_31_31 sign(privateKey, message)_31_31 // Without using FCL WalletUtils_31 const ACCOUNT_PROOF_DOMAIN_TAG = rightPaddedHexBuffer(_31 Buffer.from("FCL-ACCOUNT-PROOF-V0.0").toString("hex"),_31 32_31 )_31 const message = rlp([appIdentifier, address, nonce])_31 const prependUserDomainTag = (message) => ACCOUNT_PROOF_DOMAIN_TAG + message_31_31 sign(privateKey, prependUserDomainTag(message)) _31 }_31)
_18// Authentication Proof Service_18{_18 f_type: "Service", // Its a service!_18 f_vsn: "1.0.0", // Follows the v1.0.0 spec for the service_18 type: "account-proof", // the type of service it is_18 method: "DATA", // Its data!_18 uid: "awesome-wallet#account-proof", // A unique identifier for the service _18 data: {_18 f_type: "account-proof",_18 f_vsn: "1.0.0"_18 // The user's address (8 bytes, i.e 16 hex characters)_18 address: "0xf8d6e0586b0a20c7", _18 // Nonce signed by the current account-proof (minimum 32 bytes in total, i.e 64 hex characters)_18 nonce: "75f8587e5bd5f9dcc9909d0dae1f0ac5814458b2ae129620502cb936fde7120a",_18 signatures: [CompositeSignature],_18 appIdentifier: "https://myapp.com"_18 }_18}