Firebaseui＋Angularで、閲覧は自由・書き込みは認証を必要とする制限機能追加

# Firebaseui＋Angularで、閲覧は自由、書き込みは認証を必要とする制限機能追加

###### 前々々々回：[Angularの新規プロジェクト作成手順 - material2とService Workerは使いたい](https://qiita.com/atomyah/items/3e125b072dd74181e582)

###### 前々々回：[AngularプロジェクトにFirebaseを導入設定](https://qiita.com/atomyah/items/750d076e3dd5449423fd)

###### 前々回：[Angular + FirebaseでCRUD機能を実現](https://qiita.com/atomyah/items/e27c6d2c08f4ac260cfe)

###### 前回：[Firebase ：FireAuthを使って管理者ページを制限したいのだが](<https://qiita.com/atomyah/items/88b61b1ff68cd1711856>)



ブログ記事などでページの末尾にコメント欄がある。そのコメントは普通誰でも読めるが書き込みは登録が必要だったり画像認証をくぐらないとできない。

Firebaseuiを使えばすごく簡単にインプリできるようなので使ってみた、の備忘録。

<br>


### FirebaseUIとは

こういう認証画面と機能がFireAuthのコーディングなしで簡単に導入できてしまう、というもの。
![1557481570713.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/1a01d677-ffe2-02cf-4cc5-404da54b2de6.png)


<br>

### Firebaseの設定

プロジェクトのAuthenticationに行って、利用したいプロバイダを有効にする。今回は以下のようにした。
![1557480293342.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/add7711b-102b-8fe4-7f73-c9d25338fca5.png)

Twitterや、（使わないけど）Facebook、GitHubはAPIキーが必要になるけど、取ってくる方法わかるよね。

<br>

念のため、

#### 例：Twitterの場合

<https://developer.twitter.com/apps>にいって**Create an app**ボタンをクリックして新しいアプリを作成。

**Keys and tokens**のところで、APIキーとシークレットをとってこれる。
![1557480695136.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/2d082a73-1f9f-75a1-3413-dab16cdcc51d.png)


Firebaseコンソールに戻って、以下のようにAPIキーとシークレットをペーストする。それからコールバックURLというのをコピーして、
![1557480880515.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/6793c851-bf4a-7fb2-efee-b0eba6dd14b0.png)


今度はまたTwitterのアプリ画面に戻って、**App details**の**Callback URL**をペーストする。
![1557481034874.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/30a77582-734c-985b-c88a-a402ceea1733.png)


Facebookとかも似たような作業だと思う。

<br>

### firebaseui-angularをインストール



そしたらローカルプロジェクトにfirebaseui-angularをnpm installする。

```cmd
C:\projectA>npm install firebaseui-angular --save
```



`firebaseui-angular` には Firebase本体 と FirebaseUI本体、それから Angular からFirebase を扱うための AngularFire2 が必要なので、それぞれ追加。

```cmd
C:\projectA>npm install firebase firebaseui @angular/fire firebaseui-angular --save
```



こんなエラーというか警告がでる。

```cmd
+ firebaseui-angular@3.4.1
updated 1 package and audited 40405 packages in 37.33s
found 10 vulnerabilities (2 low, 8 high)
  run `npm audit fix` to fix them, or `npm audit` for details
```



言われたとおりに、npm audit fixを走らせる。

```cmd
C:\projectA>npm audit fix
```



<br>

### コンポーネントを作成

```cmd
C:\projectA>ng g component compoent/LoginFirebaseUI
```

どんな名前でもよい。ここではLoginFirebaseUIとした。

<br>

### app.module.tsに追記

ここがミソで、漏れがないようにモジュールを追加していく。

```typescript
//Angularモジュールのインポート
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {HttpClient, HttpClientModule} from '@angular/common/http';
import { environment } from '../environments/environment'; 
import { AngularFireModule } from '@angular/fire'; 
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireAuthModule } from '@angular/fire/auth'; 
import { FirebaseUIModule, firebase, firebaseui } from 'firebaseui-angular'; // 追加.FirebaseUIのモジュール

//Material2モジュールのインポート
…省略…

// 作成したコンポーネントのインポート
import {RootComponent} from './component/root/root.component';
import { RouterModule } from '@angular/router';
import { AppRoutes } from './app.route';
import { HomeComponent } from './component/home/home.component';
import { LoginComponent } from './component/login/login.component'; 
import { LoginFirebaseUIComponent } from './component/login-firebase-ui/login-firebase-ui.component';　// 追加.FirebaseUIの処理用コンポーネント


// FirebaseUI初期化コード
const firebaseUiAuthConfig: firebaseui.auth.Config = {
  autoUpgradeAnonymousUsers: false, // 匿名認証ユーザー自動アップグレード
  signInFlow: 'popup', // redirect or popup
  signInOptions: [
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
    {
      scopes: [
        'public_profile',
        'email',
        'user_likes',
        'user_friends'
      ],
      customParameters: {
        'auth_type': 'reauthenticate'
      },
      provider: firebase.auth.FacebookAuthProvider.PROVIDER_ID
    },
    
    firebase.auth.TwitterAuthProvider.PROVIDER_ID,
    firebase.auth.GithubAuthProvider.PROVIDER_ID,
    {
      requireDisplayName: false,
      provider: firebase.auth.EmailAuthProvider.PROVIDER_ID
    },
    firebase.auth.PhoneAuthProvider.PROVIDER_ID,
    firebaseui.auth.AnonymousAuthProvider.PROVIDER_ID
  ],
  
  tosUrl: 'http://localhost:6200/TOS', // 'Team Of Serviceのリンク先URL'
  privacyPolicyUrl: 'プライバシーポリシーのURL',
  signInSuccessUrl: 'http://localhost:6200/home',
  credentialHelper: firebaseui.auth.CredentialHelper.ACCOUNT_CHOOSER_COM,
  siteName: 'projectA', 

};

//アプリで使用するモジュール定義
@NgModule({
   //モジュール
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    ReactiveFormsModule,
    FormsModule,
	…Material2関連省略…
    //ルーターの定義
    RouterModule.forRoot(AppRoutes),
    //Firebaseの定義
    AngularFireModule.initializeApp(environment.firebase), // 追加
    AngularFirestoreModule,  // 追加.Firestore用モジュール
    AngularFireAuthModule,  // 追加.angularfireのAuth用モジュール
    FirebaseUIModule.forRoot(firebaseUiAuthConfig),　// FirebaseUI用のモジュール

  // 作成したコンポーネント 
  declarations: [
    RootComponent,
    HomeComponent,
    LoginFirebaseUIComponent,
  ],

  // DIするサービス 
  providers: [
  ],

  // 初めに呼び出すコンポーネント
  bootstrap: [RootComponent]
})

export class AppModule { 
}
```





// FirebaseUI初期化コード以下の`const firebaseUiAuthConfig: firebaseui.auth.Config = {…処理…}`の部分がミソ。はっきりいって処理コードというより設定値を羅列してるだけだが。見ればなにを設定してるかだいたいわかる。

例えばわたしは今回FacebookとGithubは使わないのでコメントアウトする。

また、下記のように、***By continuing, you are ....Terms of Service and Privacy Policy***とかの文言もいらないのでその部分もコメントアウトする。
![1557482745247.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/5a23c139-35d5-2133-3c11-a1e5261bad2d.png)


###### app.module.ts

```typescript
// FirebaseUI初期化
const firebaseUiAuthConfig: firebaseui.auth.Config = {
  autoUpgradeAnonymousUsers: false, // 匿名認証ユーザー自動アップグレード
  signInFlow: 'popup', // redirect or popup
  signInOptions: [
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
    /*{
      scopes: [
        'public_profile',
        'email',
        'user_likes',
        'user_friends'
      ],
      customParameters: {
        'auth_type': 'reauthenticate'
      },
      provider: firebase.auth.FacebookAuthProvider.PROVIDER_ID
    },
    */
    firebase.auth.TwitterAuthProvider.PROVIDER_ID,
    // firebase.auth.GithubAuthProvider.PROVIDER_ID,
    {
      requireDisplayName: false,
      provider: firebase.auth.EmailAuthProvider.PROVIDER_ID
    },
    firebase.auth.PhoneAuthProvider.PROVIDER_ID,
    firebaseui.auth.AnonymousAuthProvider.PROVIDER_ID
  ],
  
  /*
  tosUrl: 'aaa', // 'Team Of Serviceのリンク先URL'
  privacyPolicyUrl: 'プライバシーポリシーのURL',
  signInSuccessUrl: 'http://localhost:6200/home',
  credentialHelper: firebaseui.auth.CredentialHelper.ACCOUNT_CHOOSER_COM,
  siteName: 'projectA', 
  */
};
```


<br>


### スタイルの適用

このままだと崩れた表示になるのでCSSを適用する。Angularの最初にロードされるindex.htmlの`<head></head>`に、

```html
 <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/3.0.0/firebaseui.css" />
```

をコピペ。


<br>

### LoginFirebaseUIComponentを表示

app.component.htmlを使ってたら`<app-login-firebase-ui></app-login-firebase-ui>`をどこかに挿入して'ng serve -o'すれば、画面に “login-firebase-ui works!” と表示される。

おいらのこのProjectAは`bootstrap: [RootComponent]`としているので, app.routes.tsをいじって、最初にLoginFirebaseUIComponentが表示されるようにした。

###### app.routes.ts

```typescript
//====================
// ルータ定義
//====================

import {Routes} from "@angular/router";
import {RootComponent} from "./component/root/root.component";
import { HomeComponent } from './component/home/home.component';
import { LoginFirebaseUIComponent } from './component/login-firebase-ui/login-firebase-ui.component';　// 追加

//urlパスと表示するコンポーネントの関連づけ
export const AppRoutes: Routes = [
    {path: "", component: LoginFirebaseUIComponent}, // 変更
    {path: "login", component: LoginFirebaseUIComponent},　// 変更
    {path: "home", component: HomeComponent},
];

```



ああ、ちなみにRootComponentのHTMLには以下のようにコンポーネント挿入タグがある。

###### route.component.html

```html
<!--各コンポーネントを入れる枠内-->
<div style="position: absolute;top:86px;left:0px; width: 100%; max-width:1024px; ">
        <router-outlet></router-outlet> 
</div>
```



RootComponentはヘッダーとフッターを全ページで表示するコンポーネントなのでね。まあこれは余談。

`<router-outlet></router-outlet>`の説明は割愛

<br>

なにが言いたかったかというとトップページなり書き込み制限閲覧ページなりに`<app-login-firebase-ui></app-login-firebase-ui>`を挿入すれば、もうそれだけで画面を勝手に挿入してくれる。
下の画面はhom.component.htmlのいちばん下に`<app-login-firebase-ui></app-login-firebase-ui>`を挿入した。
![1557483883478.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/d96f15f5-0e5e-0d85-699c-d83f222e78f7.png)

<br>

###### home.component.html

```html
    …省略…

	<div>
        <app-login-firebase-ui></app-login-firebase-ui>
    </div>
```



<br>

もちろん今のままだと“login-firebase-ui works!”ってなってるだけなので、login-firebase-ui.component.tsとlogin-firebase-ui.component.htmlを編集する。

### login-firebase-ui.component.ts

```typescript
import { Component, OnInit } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Observable } from 'rxjs';
import { FirebaseUISignInSuccessWithAuthResult, FirebaseUISignInFailure } from 'firebaseui-angular';

@Component({
  selector: 'app-login-firebase-ui',
  templateUrl: './login-firebase-ui.component.html',
  styleUrls: ['./login-firebase-ui.component.css']
})
export class LoginFirebaseUIComponent implements OnInit {
  user: Observable<firebase.User>;

  constructor(private angularFireAuth: AngularFireAuth) { }

  ngOnInit() {
    this.user = this.angularFireAuth.authState;
  }

  // ログアウト
  async logout() {
    this.angularFireAuth.auth.signOut();
  }

  // 成功時のコールバック
  successCallback(signInSuccessData: FirebaseUISignInSuccessWithAuthResult) {
    console.log(signInSuccessData);
  }

  // 失敗時のコールバック
  async errorCallback(errorData: FirebaseUISignInFailure) {
    console.log(errorData);
  }
  
}

```



まんまコピペする。
<br>

### login-firebase-ui.component.html

これは以下のように簡素でもよい。


```
<firebase-ui
  (signInSuccessWithAuthResult)="successCallback($event)"
  (signInFailure)="errorCallback($event)">
</firebase-ui>
```

けれどもこれではログアウトボタンがないので、認証されている時はログアウトボタンを、認証されていない時はfirebaseuiのサインイン画面と一緒に注意書きでも書いておくよう、login-firebase-ui.component.htmlを作成する。

###### login-firebase-ui.component.html

```html
<div *ngIf="(user | async) || {} as user">
    <div *ngIf="user.uid; else notLoggedIn">
      <!-- 認証されている場合 -->
      <p>ログイン中。uid: {{user.uid}}</p>
      <button mat-flat-button color="accent" (click)="logout()">ログアウト</button>
    </div>
    <ng-template #notLoggedIn>
        <!-- 認証されていない場合 -->
        <p>書き込みする場合は下記いずれかの方法でサインインしてください。</p>
    </ng-template>
  </div>

<firebase-ui
  (signInSuccessWithAuthResult)="successCallback($event)"
  (signInFailure)="errorCallback($event)">
</firebase-ui>
```

これで認証画面が出現してるはず。


<br>

### Firestoreのルール変更

閲覧自由、書き込み認証ユーザーだけ、なのでこんな感じ。
![1557484359281.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/eb81320c-95d9-7628-dbdc-40aa09b9c9bd.png)

<br>

### 動作確認

まずサインインしないで書き込んでみる。
![1557484456970.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/3a336dc9-d4eb-6e43-832b-bb6d039aa0ed.png)

一瞬新しいレコードが表示されるけど、Firestoreには書き込めず今まで通りの画面が表示される

<br>

次に認証してから書き込んでみる。
![1557484616043.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/a7c38051-1f35-4386-b413-1b75c33c154f.png)


書き込めた。
![1557484656496.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/ee99f8fb-d3ce-44e1-5647-cf5f669842b5.png)


ログアウトボタンをクリックすると元の画面にもどる。
![1557484695919.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/6cece69c-2267-570e-4c47-0ae8d733ec4c.png)

<br>
### Sign in with phoneで動作確認

**Sign in with phone**でやってみると、AmazonやFacebookのような巨大サイトとまったく同じように自分のスマフォにSMSで6桁の確認コードが送られてくるので感動～！
![1557489613109.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/ca76e801-c502-bac4-0490-f6706f8ed2fa.png)


![1557489704031.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/09c7ef31-a7ba-8061-eab2-5f3bb934effa.png)


![1557553139529.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/81fe9a64-d02d-9e9a-55b2-5d9228ec5f7b.png)


<br>

### 補足

見た目がカッコ悪い、別画面でfirebaseuiを出したいと思ったら以下のコードで。

###### home.component.html

```html
書き込みする場合はまずサインインしてください。<button mat-flat-button color="accent" (click)="goToLink('login')">サインイン画面へ</button>
```



###### home.component.ts

```typescript
  goToLink(url: string){
    window.open(url, "_blank", "width=400,height=500");
  }
```



**サインイン画面へ**ボタンをクリックすると別ウインドウがでる。そこでサインインすればいいだけ。ログアウトボタンもそこに表示される。
![1557553527814.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307441/8aad8685-4fc9-4a0b-fdb6-65536fb0f1a0.png)

<br>

<br>

日本語化したいが、ちと調べたがわからない・・・


<br>


参考：[Angular で FirebaseUI を使う方法(angularfire2)](<https://webbibouroku.com/Blog/Article/angularfire-firebaseui>)

[FirebaseUI でウェブアプリに簡単にログイン機能を追加する](<https://firebase.google.com/docs/auth/web/firebaseui?hl=ja>)

[FirebaseUI + vueでログイン処理](https://qiita.com/okdyy75/items/24e78fdd0f12742b9e82)