Firebase 匿名認証でログイン後、メールとパスワードで匿名アカウントを永久アカウントに変換する

#はじめに
アプリなんかで**「最初は匿名認証で、後からアカウント登録してもらう」**と言う手順を踏みたい場合があったりする。

公式さんではこのように説明される。
>匿名ユーザーがアプリへ登録した後、新しいアカウントの下で引き続き従来の作業を行えるようにしなければならない場合があります。たとえば、アプリへの登録前にユーザーがショッピング カートに追加したアイテムを、新しいアカウントのショッピング カートにも入れておく、といったケースです。ステップは次のとおりです。
[匿名アカウントを永久アカウントに変換する](https://firebase.google.com/docs/auth/web/anonymous-auth?hl=ja#convert-an-anonymous-account-to-a-permanent-account)

僕の場合はアカウント登録のハードルを下げたかったから、この方法を使おうと考えた。
多くの人にとっては
####新規アプリダウンロード→アカウント登録が必要→「めんどくせ〜。即アンインストール！」
と言った感じで、ノリでダウンロードしたアプリは中々使用してもらえないw

そして僕が運営しているアプリ「〇活カレンダー」もネタアプリに思われがちなので、ノリでダウンロードされる事が多い:expressionless:
[Apple Store](https://apps.apple.com/jp/app/性活カレンダー/id1484151444?mt=8)
[Google Play](https://play.google.com/store/apps/details?id=com.hCalendar.app)

#環境
・[Ioinc](https://ionicframework.com)で開発したモバイルアプリ
・Angularと[angularfire](https://github.com/angular/angularfire)
・シミュレーターと実機でテスト

#匿名アカウントに認証情報をリンクする
匿名アカウントのユーザーが「このアプリ気に入ったからアカウント作ろう」と思った場合、アプリ内のアカウント登録画面から認証情報を入力してもらう。

そうすることで匿名アカウントの状態で保存したデータが引き継がれたまま、永久アカウントに変換される。

##tsファイル

```typescript:ano-signup.page.ts
import { LoadingController, AlertController } from '@ionic/angular';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AngularFireAuth } from '@angular/fire/auth';
import * as firebase from 'firebase/app';

@Component({
  selector: 'app-ano-signup',
  templateUrl: './ano-signup.page.html',
  styleUrls: ['./ano-signup.page.scss'],
})

export class AnoSignupPage implements OnInit {
signup: {
    email: string;
    password: string;
    name: string;
  } = {
    email: '',
    password: '',
    name: '',
  };

constructor(
    private router: Router,
    private afAuth: AngularFireAuth,
    private loadingCtrl: LoadingController,
    private alertCtrl: AlertController,
  ) {}

ngOnInit() {}

async signupForAno() {

// ローディングを出す
    const loading = await this.loadingCtrl.create({
      message: 'アカウント登録中です',
    });
    this.presentLoading(loading);

// 認証情報（credential）を変数「credential」に入れる
// makeCredential()へ入力されたメアドとパスワードを引数で渡す
    const credential = this.makeCredential(
      this.signup.email,
      this.signup.password
    );

    const user = this.afAuth.auth.currentUser;
    console.log('credentialはこちら ', credential);

// 現在の匿名userに認証情報を持ったcredentialで、リンクさせる
    user
      .linkWithCredential(credential)
      .then((usercred) => {
// 入力された名前をdisplayNameに登録
        const upgradedUser = usercred.user;
        upgradedUser.updateProfile({
          displayName: this.signup.name,
          photoURL: '',
        });
    this.router.navigateByUrl('/home');
        console.log(
          'Anonymous account successfully upgraded',
          upgradedUser.uid
        );
        loading.dismiss();
      })
      .catch((error) => {
        console.log('Error upgrading anonymous account', error);
        loading.dismiss();
        const header = 'アカウント登録に失敗';
        const message = 'エラーが発生しました。もう一度お試しください。。。';
        this.showAlert(header, message);
      });
  }

// ローディングを表示させる
  private async presentLoading(loading) {
    return await loading.present();
  }

// 認証情報を作成して、それを返す
  private makeCredential(email: string, password: string) {
    const credential = firebase.auth.EmailAuthProvider.credential(
      email,
      password
    );
    return credential;
  }

// エラーの時のアラート
  private showAlert(header: string, message: string) {
    this.alertCtrl
      .create({
        header: header,
        message: message,
        buttons: ['OK'],
      })
      .then((alertEl) => alertEl.present());
  }
}
```

ちなみに「credential（クレデンシャル）」は「証明書」みたいな意味

```typescript
private makeCredential(email: string, password: string) {
    const credential = firebase.auth.EmailAuthProvider.credential(
      email,
      password
    );
    return credential;
  }
```


##HTMLファイル

```html:ano-signup.page.html
<ion-header>
  <ion-toolbar color="primary">
    <ion-title>アカウント登録</ion-title>
     <ion-buttons slot="start">
          <ion-back-button defaultHref="/home"></ion-back-button>
        </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
  <ion-grid>
    <ion-row>
      <ion-col size="12" size-sm="8" offset-sm="2">
        <form #f="ngForm" class="signupForm">
          <ion-item class="formItem">
            <ion-label position="floating">
              <ion-icon name="mail"></ion-icon>メールアドレス
            </ion-label>
            <ion-input required email [(ngModel)]="signup.email" type="email" name="signup.email" #emailCtrl="ngModel">
            </ion-input>
          </ion-item>
          <ion-item *ngIf="!emailCtrl.valid && emailCtrl.touched" lines="none">
            <ion-label color="danger">メールアドレスを入力してね!</ion-label>
          </ion-item>
    
          <ion-item class="formItem">
            <ion-label position="floating">
              <ion-icon name="key"></ion-icon>パスワード
            </ion-label>
            <ion-input required [(ngModel)]="signup.password" type="password" name="signup.password" minlength="6"
              #passwordCtrl="ngModel">
            </ion-input>
          </ion-item>
          <ion-item *ngIf="!passwordCtrl.valid && passwordCtrl.touched" lines="none">
            <ion-label color="danger">パスワードは6文字以上</ion-label>
          </ion-item>
    
          <ion-item class="ion-margin-bottom">
            <ion-label position="floating">
              <ion-icon name="person"></ion-icon>名前
            </ion-label>
            <ion-input required [(ngModel)]="signup.name" type="text" name="signup.name"></ion-input>
          </ion-item>
    
          <ion-button [disabled]="!f.form.valid" size="large" expand="block" (click)="signupForAno()">
            アカウント登録
          </ion-button>

        </form>
      </ion-col>
    </ion-row>
  </ion-grid>
  </ion-content>
```


#参考
匿名認証でずっとログイン状態にする方法はこちらを参考に:point_down:
[Firebase 匿名認証の永続性を変更して、ずっと匿名でのログイン状態にする](https://qiita.com/kokogento/items/7e51a2513cd85e0c9615)