Mpurseの署名機能を使って、ユーザID、パスワード、クレカ不要の認証機能の実装

#はじめに
こんちわ、らいうです！

Ask mona 3.0のようなログイン機能を実装したいと思い、数日格闘していました。
今日、なんとかサンプルプロジェクトができたので、メモを残しておきます。

ソースに関してはこちら
https://github.com/Raiu1210/mpurse_login


#環境
・Mac OS Mojave
・node -v -> v10.16.0
・vue -V -> 3.9.2
・python 2.7.10

node.jsに関してはもうやってる人も多いと思いますがnvmを使って、プロジェクトごとにバージョンを切り替えられるようにしておきましょう。
また、bitcoreやbitcoin-jsなどのモジュールをnpm installする際にpython2.系が必要になるっぽいです。

僕は、Macのセットアップでデフォのpython2.7をアンインストールしてpyenvでpython3系を使っていたので、エラーが出ました。
pyenvでlocalを2.7.10を指定しました。


#やったことの流れ

プロジェクトの土台に関しては
https://qiita.com/y4u0t2a1r0/items/a6aea444efc8e8e65293
この記事を参考にしながら作っていきました。
この記事でローカル環境において
・node.jsで構築したバックエンドとVue.jsで作ったフロントがPOST通信できるようにします。


上の記事で作ったフロント側を、前に僕が書いた記事で作ったものに置き換えてhttps対応などすることで
window.mpurseインスタンスが使える（インジェクトされる）ようにします。
https://qiita.com/Raiu1210/items/b4031624e3829c8bd672


バックエンドもhttps化するために
https://kaworu.jpn.org/javascript/node.js%E3%81%AB%E3%82%88%E3%82%8BHTTPS%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9
この記事を参考にしました。
上でリンクを張った僕の記事で、opensslを使ってオレオレ証明書をそろえますが、同じ証明書に対してパスを通せばOKでした。


# フロントエンド
処理のイメージとしては
・フロントでメッセージにmpurseで署名をする
・バックエンドにPOSTする
　内容
　　・メッセージ
　　・アドレス
　　・署名
・署名結果を待つ、受け取る
といった形です。


```js:index.js
import axios from 'axios'

export default () => {
  return axios.create({
    baseURL: 'https://localhost:3000/'
  })
}
```
これは、参考にして作ったものとほとんど変わらないですね


これが署名を作ってPOSTする関数です。
HelloWorld.vueで作ったボタンを押した際に呼ばれます

```js:method.js
import Api from './index'

export default {
  async ask_verify_sig () {
    var date = new Date();
    var a = date.getTime();

    var address = await window.mpurse.getAddress()
    var message = "Please sign this message :" + a
    var signature = await window.mpurse.signMessage(message);

    const item = { address: address, message: message, signature: signature }
    return Api().post('/verify', item)
  }
}

```

現在のタイムスタンプを取得して、署名メッセージを作成します。
そして、それを引数としてmpurseのsignMessage()に渡します。

その後、itemにAddress, message, signatureを入れてPOSTします。

trueが返ってきた場合とfalseが返ってきた場合で適当なメッセージを決めて,alertするようにしました。
これで目に見えてデカデカと確認できます。

#バックエンド
バックエンドではbitcoinjs-messageを使って、署名を検証しました。
python3系を使っている人は注意。pyenvなどでこのディレクトリ内では2系にしましょう。

それを確認したら

```
npm install bitcoinjs-message
```
しましょう。

```js:index.js
const express = require('express')
const bodyParser = require('body-parser')
const bitcoinMessage = require('bitcoinjs-message')
const cors = require('cors')
const app = express()

const messagePrefix = "\x19Monacoin Signed Message:\n"

var fs = require('fs');
var https = require('https');
app.use(bodyParser.json())
app.use(cors())

var options = {
  key:  fs.readFileSync('../../../work/ssl/localhost.key'),
  cert: fs.readFileSync('../../../work/ssl/localhost.crt')
};

var server = https.createServer(options,app);

app.post('/verify', function(req, res) {
  var date = new Date();
  var a = date.getTime();
  var address = req.body.address
  var message = req.body.message
  var signature = req.body.signature

  var result = message.split(':');
  var time_diff = a - parseInt(result[1], 10)

  var is_varify = bitcoinMessage.verify(message, address, signature, messagePrefix)
  console.log(is_varify)

  if(time_diff < 6000 && is_varify) {
  	var return_message = true
  } else {
  	var return_message = false
  }

  res.send({
    message: return_message
  })
})

server.listen(3000);
```
まず

```js
const bitcoinMessage = require('bitcoinjs-message')
```
で、bitcoinjs-messageをインポートしてきます。
そして大事なのが

```js
const messagePrefix = "\x19Monacoin Signed Message:\n"
```
これのおかげでモナコインに対応してくれます。

ここに関しては
海野('A`)ｳﾞｧｰ山猿：https://twitter.com/CryptoMonkey_ja
さんが教えてくれました！
ありがとうございます！

bitcoin-js-messageとbitcore-messageでの検証の仕方を教えてくれました
https://gist.github.com/fkfk/7652bb480197036c31bac186f697bd65


そして検証に関して、一応
署名が直近1分以内に行われた場合にtrueを返すようにしてみました。
ただ、https通信でないとmpurseは使えないので、正直この機能はいらないかもしれません。

# future work
今日、脇Pからアドバイスもらって
https://github.com/cryptocoinjs/coininfo

こんなものがあるそうです。
Monacoinに限らずAltcoinを扱う場合にとても便利そうでした。
次はこれ使ってみようと思っているので、またその時は記事書きます。

#　ざっくりこんなかんじ
とまあ、さっくり書いてみました。
十分な説明でなかったらコメントやtwitterでDMしてください。
twitter : https://twitter.com/Mr_1484