Edited at

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


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


前々々々回:Angularの新規プロジェクト作成手順 - material2とService Workerは使いたい


前々々回:AngularプロジェクトにFirebaseを導入設定


前々回:Angular + FirebaseでCRUD機能を実現


前回:Firebase :FireAuthを使って管理者ページを制限したいのだが

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

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



FirebaseUIとは

こういう認証画面と機能がFireAuthのコーディングなしで簡単に導入できてしまう、というもの。

1557481570713.png



Firebaseの設定

プロジェクトのAuthenticationに行って、利用したいプロバイダを有効にする。今回は以下のようにした。

1557480293342.png

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


念のため、


例:Twitterの場合

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

Keys and tokensのところで、APIキーとシークレットをとってこれる。

1557480695136.png

Firebaseコンソールに戻って、以下のようにAPIキーとシークレットをペーストする。それからコールバックURLというのをコピーして、

1557480880515.png

今度はまたTwitterのアプリ画面に戻って、App detailsCallback URLをペーストする。

1557481034874.png

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



firebaseui-angularをインストール

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

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

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

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

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

+ 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を走らせる。

C:\projectA>npm audit fix



コンポーネントを作成

C:\projectA>ng g component compoent/LoginFirebaseUI

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



app.module.tsに追記

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

//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


app.module.ts

// 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',
*/

};



スタイルの適用

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

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

をコピペ。



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

//====================

// ルータ定義
//====================

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

<!--各コンポーネントを入れる枠内-->

<div style="position: absolute;top:86px;left:0px; width: 100%; max-width:1024px; ">
<router-outlet></router-outlet>
</div>

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

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


なにが言いたかったかというとトップページなり書き込み制限閲覧ページなりに<app-login-firebase-ui></app-login-firebase-ui>を挿入すれば、もうそれだけで画面を勝手に挿入してくれる。

下の画面はhom.component.htmlのいちばん下に<app-login-firebase-ui></app-login-firebase-ui>を挿入した。

1557483883478.png



home.component.html

    …省略…

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


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


login-firebase-ui.component.ts

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);
}

}

まんまコピペする。



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

<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>

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



Firestoreのルール変更

閲覧自由、書き込み認証ユーザーだけ、なのでこんな感じ。

1557484359281.png



動作確認

まずサインインしないで書き込んでみる。

1557484456970.png

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


次に認証してから書き込んでみる。

1557484616043.png

書き込めた。

1557484656496.png

ログアウトボタンをクリックすると元の画面にもどる。

1557484695919.png



Sign in with phoneで動作確認

Sign in with phoneでやってみると、AmazonやFacebookのような巨大サイトとまったく同じように自分のスマフォにSMSで6桁の確認コードが送られてくるので感動~!

1557489613109.png

1557489704031.png

1557553139529.png



補足

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


home.component.html

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


home.component.ts

  goToLink(url: string){

window.open(url, "_blank", "width=400,height=500");
}

サインイン画面へボタンをクリックすると別ウインドウがでる。そこでサインインすればいいだけ。ログアウトボタンもそこに表示される。

1557553527814.png



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


参考:Angular で FirebaseUI を使う方法(angularfire2)

FirebaseUI でウェブアプリに簡単にログイン機能を追加する

FirebaseUI + vueでログイン処理