Firebase Authentication Custom Tokenの取得と利用


既存の認証システムや標準プロバイダがない場合に利用できるのがcustom tokenによる認証。
要は何か識別IDとなる文字列を渡してやればそれに対応したtokenが帰ってくる。その後はそのtokenを利用してログインすればよい。


## 取得

まずtokenの取得。admin権限が必要みたい。
hogehogeとなっているところ、既存の認証システムでのIDにすればよい。

>ここをProvider:userIdとかするといろいろ便利らしい。

```js:getToken.js
const admin = require('firebase-admin');
const serviceAccount = require('path/to/key.json');

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: 'https://staging-xxxxx.firebaseio.com',
});


admin.auth().createCustomToken('hogehoge')
    .then(token => {
        console.log(token);
    })
    .catch(e => {
        console.log(e);
    });
```

上記プログラムを実行すると下記のようなレスポンス。これがtoken。

```bash
node getToken.js

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsImlhdCI6MTU3NTkyNDIwNSwiZXhwIjoxNTc1OTI3ODA1LCJpc3MiOiJmaXJlYmFzZS1hZG1pbnNkay12YW9lYkBzdGFnaW5nLWE1OTQ2LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwic3ViIjoiZmlyZWJhc2UtYWRtaW5zZGstdmFvZWJAc3RhZ2luZy1hNTk0Ni5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsInVpZCI6ImhvZ2Vob2dlIn0.BuY23mqh26evnsN7Rh_0QkugBTpL0992P763P_n9fcYPHsTWz-VzHKqXMOviITSLFPNNiDXRcFPsPGYmfwaOcIrW8QfgNtfF12coymqK16XeqOkZEHlj-TLqxuMMRIkoZcDW6PcYsYVel_hplYOzuKD5hvNiHiNv8jQKRsLq2EbVGeOCizS0CCVK_6GsB34QUTsHp717vO6Ek2L0IPRijil9xRunCnYh2Tjz-PdyZ3H2eY6iqM7YW0qZ32TjfhEvGpJxabCKMrHmOlh4WZIxINO43fI17Kln45wniVaDN0zKt1sG1StdVXVcbD0wAX8cZl6JpP347nCskPxxxxxxxx
```

DBにアカウントと紐付けてtokenを保存しておいて利用とかする。

>tokenの有効期限は1時間なのでdbに保存するまでもないかも（どうrefreshするか問題はある）。


## 利用

利用はクライアント側。

上記で取得したtokenを利用。

```js:authClient
const firebase = require('firebase');

const firebaseConfig = {
    apiKey: "AIzaSyD7HtQGZhfL4T1ttrElxsgDwqoxxxxxxxx",
    authDomain: "test-xxxxx.firebaseapp.com",
    databaseURL: "https://test-xxxxx.firebaseio.com",
    projectId: "test-xxxxx",
    storageBucket: "test-xxxxx.appspot.com",
    messagingSenderId: "250622200000",
    appId: "1:250622212741:web:ff33a2e77b61e8990xxxxx",
    measurementId: "G-VVQ2BXXXXX"
};

firebase.initializeApp(firebaseConfig);

const token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwczovL2lkZW50aXR5dG9vbGtpdC5nb29nbGVhcGlzLmNvbS9nb29nbGUuaWRlbnRpdHkuaWRlbnRpdHl0b29sa2l0LnYxLklkZW50aXR5VG9vbGtpdCIsImlhdCI6MTU3NTkyNDIwNSwiZXhwIjoxNTc1OTI3ODA1LCJpc3MiOiJmaXJlYmFzZS1hZG1pbnNkay12YW9lYkBzdGFnaW5nLWE1OTQ2LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwic3ViIjoiZmlyZWJhc2UtYWRtaW5zZGstdmFvZWJAc3RhZ2luZy1hNTk0Ni5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsInVpZCI6ImhvZ2Vob2dlIn0.BuY23mqh26evnsN7Rh_0QkugBTpL0992P763P_n9fcYPHsTWz-VzHKqXMOviITSLFPNNiDXRcFPsPGYmfwaOcIrW8QfgNtfF12coymqK16XeqOkZEHlj-TLqxuMMRIkoZcDW6PcYsYVel_hplYOzuKD5hvNiHiNv8jQKRsLq2EbVGeOCizS0CCVK_6GsB34QUTsHp717vO6Ek2L0IPRijil9xRunCnYh2Tjz-PdyZ3H2eY6iqM7YW0qZ32TjfhEvGpJxabCKMrHmOlh4WZIxINO43fI17Kln45wniVaDN0zKt1sG1StdVXVcbD0wAX8cZl6JpP347nCskPxxxxxxxx";

firebase.auth().signInWithCustomToken(token)
    .then(res => {
        console.log(res.user.uid);
        res.user.updateEmail("hoge@hoge.com"); //emailを設定
    })
    .catch(e => {
        console.log(e);
    })
```

実行するとログインできる。

```bash
node authClient
```

Authentication UIで見ると下記のような感じ。

![スクリーンショット 2019-12-10 5.49.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/eb82275a-bc95-9fb0-0cf1-3ebdb9f49171.png "スクリーンショット 2019-12-10 5.49.22.png")

## その他

### tokenのrefreshをどうやってやるの？

いろいろなところで議論されているが明確な答えがない。最悪、一度ユーザーをdeleteして、再度createすればいいのだろうけど。。。
引き続き調査。


## 追記

createしたときにtokenしか戻らないので、どうやってrefreshしたらいいのか？という疑問がありましたが、ログインしたときにuser情報にrefresh_token情報が含まれているのでそれを保存しておいて利用する感じらしい。

>が、上記のtokenの有効期限自体が1時間なので、1時間以内にその処理をやる必要がある。

下記のようにログイン時にrefreshTokenを出力。

```js
firebase.auth().signInWithCustomToken(token)
    .then(res => {
        console.log(res.user.uid);
        console.log(res.user.refreshToken);
    })
    .catch(e => {
        console.log(e);
    })
```

で、下記のようにrefreshTokenを利用して新しいtokenを要求する。

```js
const firebase = require('firebase');
const axios = require('axios');

const firebaseConfig = {
    apiKey: "AIzaSyD7HtQGZhfL4T1ttrElxsgDwqoxxxxxxxx",
    authDomain: "test-xxxxx.firebaseapp.com",
    databaseURL: "https://test-xxxxx.firebaseio.com",
    projectId: "test-xxxxx",
    storageBucket: "test-xxxxx.appspot.com",
    messagingSenderId: "250622200000",
    appId: "1:250622212741:web:ff33a2e77b61e8990xxxxx",
    measurementId: "G-VVQ2BXXXXX"
};

firebase.initializeApp(firebaseConfig);

//上記で取得したリフレッシュToken
const refreshToken = "AEu4IL24RUu8heim0A2kn4U_Yk5lJy1-a7pdDapK08OFUDOA1hoH7oxzoa5Yb9Vafb4SpH8pVUtza4KfyvZZ2IF-r-dIpAXM9B8cnw-k_bXXXXXXXXX-XXXX";

//postパラメータを設定
const params = new URLSearchParams();
params.append('grant_type', 'refresh_token');
params.append('refresh_token', refreshToken);

//リクエスト
axios.post('https://securetoken.googleapis.com/v1/token?key=' + firebaseConfig.apiKey, params)
    .then(res => {
        // console.log(res.data.access_token);
        console.log(res);
    })
    .catch(e => {
        console.log(e);
    })
```

* refreshtokenはexpireしないみたい
* revokeRefreshToken()を呼ぶみたい

```
admin.auth().revokeRefreshTokens('UID');
```