Introduction

[Previous << Authorization Function](https://qiita.com/flowcadence/items/9ba3fc7d18f53dedc98a)
[Next >> Provable Authn](https://qiita.com/flowcadence/items/a6e223893ba142f54df6)

https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a#reference-2020832ebee1810a325a

ウォレットプロバイダーは認証と承認を処理します。ユーザーが自分達の情報を管理したりトランザクションを承認する場所として、ウォレットプロバイダーは非常に重要な役割を果たします。

FCLの中心的考えのひとつは、ユーザーが自分のデータを管理できていることであり、ウォレットプロバイダーは、まさに多くのユーザーがそのことを行う場所です。

FCLは、ウォレットプロバイダーに関する詳細情報を事前に把握する必要のないように構築されています。ウォレットプロバイダーは、ユーザーがdappに自分達を通知したい場合に発見することができます。これにより、私たちは「Bring Your Own Identity（BYOI）」という概念を導き出しました。

<h1>Identity</h1>

概念的には、FCLはアイデンティティを2つの方法で考えます。PublicとPrivateです。

Publicアイデンティティはリソースという形でチェーン上に保存されます。それはアカウントのFlow Addressを知っている人なら誰でも利用できます。

FCLでは、ユーザーのPublicアイデンティティを取得するのはこのように簡単です:
```ruby
import {user} from "@onflow/fcl"

const identity = await user(flowAddress).snapshot()
/*       ^
         `------ The public identity for `flowAddress` */

const unsub = user(flowAddress).subscribe(identity => console.log(identity))
/*                                           ^
                                             `------- The public identity for `flowAddress` */
```
Privateアイデンティはウォレットプロバイダーによって保存され、currentUser のみが利用できます。

FCLでは、currentUserのアイディンティを取得すると、Public アイデンティとPrivate アイデンティの両方が取得され、Private アイデンティが Public アイデンティにマージされます。

プライベート情報は、challengeのステップの前にスコープを通じてリクエストされる必要があり、詳細は後述します。ウォレットプロバイダーは、ユーザーに何のスコープがリクエストされるかを見せて、dappと何のスコープを共有するのかを決定させることを強くお勧めします。

FCLの中にあるアイデンティティをリクエストした者は、すべてのデータがオプショナルであると常に想定し、可能な限り最小限のデータを保存すべきで、FCLはユーザーが常に最新情報を見ることができるようにします。
```ruby
import {config, currentUser, authenticate} from "@onflow/fcl"

/* request the email scope */
config.put("challenge.scope", "email") 

const unsub = currentUser().subscribe(identity => console.log(identity))
/*                                       ^
                                         `------- The private identity for the currentUser */

/* trigger the challenge step (authenticate the user via a wallet provider) */
authenticate()
```
<h1>Identity Data</h1>

* Identities内の情報はすべてオプショナルであり、存在しない場合もあります。
* すべての値はチェーン上に保存できますが、おそらく保存すべきではありません。

私たちは、ウォレットプロバイダーがユーザーに、以下の情報を公に管理できるようにすることを強く希望します。いわば、Public プロフィール作成キットのようなものです。

FCLは常にユーザー情報の要求があった際にこれらのフィールドをpublicで取得しようと試み、ウォレットプロバイダーは、ユーザーがそれらを変更したい時に、それらがそこにあり、最新の状態になっているようにすることに責任を負います。
* 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

もし私たちが、dappの開発者に、ユーザーが直接管理できる有用な情報の確固たる基盤を最初から提供できれば、つまり上記のフィールドがその役目を果たしてくれるものと私たちは思っています。そうなれば、開発者はチェーンをより信頼し、自身のデータベースに保存する必要が少なくなると思っています。

一方、プライベートなデータは一般的データよりもユースケースがたくさんあります。何かを注文する際に、連絡先や配送先などの情報が必要になることは容易に想像できます。

最終的には、そのようなことが完全にオンチェーンで、堅牢に、プライベートに、安全に扱われることを期待していますが、それまでは、必要に応じて、また、ユーザーが許可した場合に、データのコピーをデータベースに保存することになるでしょう。

dappがプライベートなデータを受け取るプロセスは以下の通りです。
1. dappは、必要なスコープを事前に`fcl.config().put(「challenge.scope」, 「email+shippingAddress」)`としてリクエストします。
1. ユーザーは`fcl.authenticate()`で認証を行い、ウォレットプロバイダーの認証プロセス内にて、dappが`email`と`shippingAddress`を知ることがOKかどうかをユーザーが決定します。ユーザーは、どの情報を共有すか、もしあれば、決定できるはずです。
1. dappはその情報を必要とする時にFCLの現在のキャッシュデータからリクエストすることができます。もしキャッシュデータになければ、dappはそれに同意し、それに応じて調整する必要があります。

以下は、私たちがプライベートでサポートすることを考えているスコープです。FCLは、dappによって前もって指定された場合のみ、これらの取得を試みます。
* `email`
* `fullName`
* `phone`
* `textMessage`
* `address`
* `shippingAddress`
* `location`
* `publicKey`

上記はすべて、まだ初期段階であるため、今後変更される可能性があります。ウォレットプロバイダーと緊密に連携し、スコープに関する堅牢で詳細かつ一貫性のある仕様を作成したいと考えています。フィードバックやご意見はいつでも歓迎いたします。
<h1>Authentication Challenge</h1>

認証は以下の2通りの方法のうちのいずれかで行われます。
* Iframe Flow
* Redirection Flow

ウォレットプロバイダーとして、ハンドシェイク・サービスにURLエンドポイント（およびその他の情報）を登録することが求められます。（FCLは、将来、チェーン上で登録が行われ、完全にオープンソース（Apache-2.0ライセンス）なサービスを開始します）。この登録されたURLがiFrame上で表示され、dappユーザーがリダイレクトされる先となります。このドキュメントの残りの部分では、私たちはこれを*認証エンドポイント*と呼び、`GET https://provider.com/flow/authentication`のルートと一緒に言及します。

認証エンドポイントは、クエリパラメータとして以下のデータを受け取ります。

* `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
```ruby
GET https://provider.com/flow/authenticate
  ?l6n=https%3A%2F%2Fdapp.com
  &nonce=asdfasdfasdf
  &scope=email+shippingAddress
  &redirect=https%3A%2F%2Fdapp.com%2Fflow%2Fcallback

/* The values will use javascripts `encodeURIComponent` function and scopes will be `+` deliminated. */
```
リダイレクトクエリパラメータが含まれていることから、このチャレンジはリダイレクトフロー(Redirect Flow)を使用していることが分かります。Iframeフロー(Iframe Flow)は、dappsのデフォルトフローであるので引き続きサポートされる必要があります。

現時点では、ウォレットプロバイダーは、魔法のようなことを行い、ユーザーが本人であることを十分に確認する必要があります。その後ユーザーには、dappが何を要求しているのか、スコープ経由で何かのフォームで表示され、好みによってオプトインやオプトアウトができるようになっているはずです。そして、ウォレットプロバイダーがdappとFCLに制御を戻す準備ができたら、リダイレクトまたはJavaScriptの`postMessage`イベント発行によってチャレンジを完了させる必要があります。

Redirecting will look like this:
```ruby
GET https://dapp.com/flow/callback         # supplied by the redirect query param above
  ?l6n=https%3A%2F%2Fdapp.com              # the l6n supplied by FCL above
  &nonce=asdfasdfasdf                      # the nonce supplied by FCL above
  &addr=0xab4U9KMf                         # address for the users flow account (if available) -- will be used to fetch public identity information and hooks
  &padder=0xhMgqTff86                      # address for the Wallet Providers account -- will be used to fetch provider information
  &code=afseasdfsadf                       # a token supplied to FCL from the Wallet Provider, FCL will use this token when requesting private information and hooks, can be any url safe value
  &exp=1650400809517                       # when the code expires, a value of `0` will be considered as never expires
  &hks==https%3A%2F%2Fprovider.com%2Fhooks # a URL where FCL can request the private information and hooks
```
Iframe will look like this:
```ruby
parent.postMessage(
  {
    type: "FCL::CHALLENGE::RESPONSE", /* used by FCL to know what kind of message this is */
    addr: "0xab4U9KMf",
    paddr: "0xhMgqTff86",
    code: "afseasdfsadf",
    exp: 1650400809517,
    hks: "https://provider.com/hooks",
    nonce: "asdfasdfasdf",
    l6n: decodeURIComponent(l6n),
  },
  decodeURIComponent(l6n)
)
```
FCLは今、Public、 Private、 Wallet Provider Infoの情報を収集するために必要なすべての情報を持っているはずです。ウォレットプロバイダーの情報はチェーン上にあるため、ウォレットプロバイダーはここで心配する必要はありません。心配する必要があるのは、フックのリクエストを処理する際に、チャレンジのレスポンスの`https://provider.hooks`の中にある`hks`の値を介してFCLに供給されたものです。

フックのリクエストは、チャレンジのレスポンスの `hks` 値に対して行われます。リクエストには、クエリパラメータとしてコードも含まれます。
```ruby
GET https://povider.com/hooks
  ?code=afseasdfsadf
```
このリクエストは、いくつかの理由から必要です。
* 失敗した場合、FCLは何か問題が発生していることを認識し、再認証を試みます。
* 成功した場合、FCLはコードが有効であることを認識します。
* FCLがウォレットプロバイダーに対してユーザーを"検証"するための直接的な方法が、これにより作成されます。
* FCLがプライベートID情報(Private Identity Information)とフックを直接取得する方法が、これにより提供されます。
* コードはバックエンドに渡されることで、バックエンドとウォレットプロバイダー間のバックチャネルが作成することができます。

ユーザーがdappに戻った際に、FCLが保存したコードが有効期限切れでなければ、このリクエストを再度行い、FCLは最新情報を維持します。FCLはまた、重要なアクションの前に、この情報を断続的にリクエストします。

フックのリクエストは、以下のJSONで応答する必要があります。
```ruby
const privateHooks = {
  addr: "0xab4U9KMf",       // the flow address this user is using for the dapp
  keyId: 3,                 // the keyId the user wants to use when authorizing transaction
  identity: {               // the identity information fcl always wants if its there, will be deep merged into public info
    name: "Bob the Builder",
    avatar: "https://avatars.onflow.org/avatar/0xab4U9KMf.svg"
    cover: "https://placekittens.com/g/900/300",
    color: "cccc00",
    bio: "",
  },
  scoped: {                 // the private info request in the original challenge
    email: "bob@bob.bob",   // the user said it was okay for the dapp to know the email
    shippingAddress: null,  // the user said it was NOT okay for the dapp to know the shippingAddress
  },
  provider: {
    addr: "0xhMgqTff86", // the flow address for the wallet provider (used in the identity composite id)
    pid: 2345432,        // the wallet providers internal id for the user (used in the identity composite id)
    name: "Super Wallet",
    icon: "https://provider.com/assets/icon.svg",
    authn: "https://provider.com/flow/authenticate",
  }
}
```
FCLがチェーンからPublic infoを要求した際、このような情報を期待していることになります。ウォレットプロバイダーはこの情報を最新の状態に保つ責任があります。
```ruby
const publicHooks = {
  addr: "0xab4U9KMf",
  keyId: 2,
  identity: {
    name: "Bob the Builder",
    avatar: "https://avatars.onflow.org/avatar/0xab4U9KMf.svg"
    cover: "https://placekittens.com/g/900/300",
    color: "cccc00",
    bio: "",
  },
  authorizations: [
    {
      id: 345324539,
      addr: "0xhMgqTff86",
      method: "HTTP/POST",
      endpoint: "https://provider.com/flow/authorize",
      data: {
        id: 2345432
      }
    }
  ]
}
```
この時点で、FCLはcurrentUserが誰であるかについてかなり確信を持っており、ユーザーが承認できるトランザクションを開始する準備ができています。
<h1>Authorization</h1>

FCLは、ユーザーのことを知っているPublicおよびPrivate authorization hooksに対して認証リクエストを送信します。このプロセスを非同期リモート署名と呼びます。

このアイデアの主な概念は以下の通りです。
* フックがFCLに対して、認証リクエストを送信する先（ウォレットプロバイダー）を教えます
* ウォレットプロバイダーが即座に以下を返送します
  * FCLが認証の結果を要求することができるバックチャネル
  * currentUserが認証を行うことができるいくつかのオプショナルのローカルフックの方法
* FCLは、ローカルフックがcurrentUserのためのものであれば、それをトリガーします
* FCLは、承認されるか拒否されるまで、バックチャネルをポーリングして更新情報(updates)を要求します

以下は、私達が上記のチャレンジ中に受け取ったパブリック認証フックです。
```ruby
  {
    id: 345324539,
    addr: "0xhMgqTff86",
    method: "HTTP/POST",
    endpoint: "https://provider.com/flow/authorize",
    data: {
      id: 2345432
    }
  }
```
FCLは、そのフックを受け取り、次のPOSTリクエストを行います。
```ruby
POST https://provider.com/flow/authorize
  ?id=2345432
---
{
  message: "...",     // what needs to be signed (needs to be convered from hex to binary before signing)
  addr: "0xab4U9KMf", // the flow address that needs to sign
  keyId: 3,           // the flow account keyId for the private key that needs to sign
  roles: {
    proposer: true,   // this accounts sequence number will be used in the transaction
    authorizer: true, // this transaction can "move" and "modify" the accounts resources directly
    payer: true,      // this transaction will be paid for by this account (also signifies that they are signing an envelopeMessage instead of a payloadMessage)
  },
  interaction: {...}  // needed to recreate the message if the Wallet Provider wants to verify the message.
}
```
FCL は、このようなレスポンスを期待しています。
```ruby
{
  status: "PENDING",
  reason: null,
  compositeSignature: null,
  authorizationUpdates: {
    method: "HTTP/POST",
    endpoint: "https://provider.com/flow/authorizations/4323",
  },
  local: [
    {
      method: "BROWSER/IFRAME",
      endpoint: "https://provider.com/authorizations/4324",
      width: "300",
      height: "600",
      background: "#ff0066"
    }
  ]
}
```
そのローカルフックはFCLによって消化され、エンドポイントをsrcとするiframeがレンダリングされます。ユーザーがすでに認証済みの場合、この画面はウォレットプロバイダーのトランザクション承認プロセスをダイレクトにユーザーに表示します。FCLはiframeとのいかなる通信にも依存しないため、iframeを最大限に厳重に管理することができ、そして認証が完了すると、iframeを削除できます。ローカルフックを表示している間は、`authorizationUpdates`フックから認証ステータスがリクエストされています。
```ruby
POST https://provider.com/flow/authorizations/4323
```
originと同じ構造だがローカルフックを含まないレスポンスが期待されています。
```ruby
{
  status: "PENDING",
  reason: "",
  compositeSignature: null,
  authorizationUpdates: {
    method: "HTTP/POST",
    endpoint: "https://provider.com/flow/authorizations/4323",
  },
}
```
FCLはその後、ステータスが`"APPROVED"`または`"DECLINED"`に変更されるまで、新しい`authorizationUpdates`フックに従います。承認が却下された場合は、可能であればその理由を含めるべきです。
```ruby
{
  status: "DECLINED",
  reason: "They said no",
}
```
認証が承認されると、それには複合署名(composite signature)が含まれているはずです。
```ruby
{
  status: "APPROVED",
  compositeSignature: {
    addr: "0xab4U9KMf", // the flow address that needs to sign
    keyId: 3,           // the flow account keyId for the private key that needs to sign
    signature: "..."    // binary signature of message encoded as hex
  }
}
```
FClは、こうしてこのトランザクションをFlowブロックチェーンに送ることができます。
<h1>TL;DR Wallet Provider</h1>

FCLハンドシェイクでプロバイダを登録し、5つのエンドポイントを実装します。
* GET `flow/authenticate` -> `parent.postMessage(..., l6n)`
* GET `flow/hooks?code=___` -> `{ ...identityAndHooks }`
* POST `flow/authorize` -> `{ status, reason, compositeSignature, authorizationUpdates, local }`
* POST `authorizations/:authorization_id`
* GET `authorizations/:authorization_id`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3912821/b616003a-c464-c705-66e8-59e1385f65b4.png)


*Last updated on **Nov 26, 2024** by **Vishal***

翻訳元

https://developers.flow.com/tools/wallet-provider-spec/custodial

***
<b>[Previous << Authorization Function](https://qiita.com/flowcadence/items/9ba3fc7d18f53dedc98a)
</b>

[Flow blockchain / Cadence version1.0ドキュメント](https://qiita.com/flowcadence/items/3073d69c6e9895e68b5a) (Introduction)

<b>[Next >> Provable Authn](https://qiita.com/flowcadence/items/a6e223893ba142f54df6)</b>