Firebaseui+Angularで、閲覧は自由、書き込みは認証を必要とする制限機能追加
前々々々回:Angularの新規プロジェクト作成手順 - material2とService Workerは使いたい
前々々回:AngularプロジェクトにFirebaseを導入設定
前々回:Angular + FirebaseでCRUD機能を実現
前回:Firebase :FireAuthを使って管理者ページを制限したいのだが
ブログ記事などでページの末尾にコメント欄がある。そのコメントは普通誰でも読めるが書き込みは登録が必要だったり画像認証をくぐらないとできない。
Firebaseuiを使えばすごく簡単にインプリできるようなので使ってみた、の備忘録。
FirebaseUIとは
こういう認証画面と機能がFireAuthのコーディングなしで簡単に導入できてしまう、というもの。
Firebaseの設定
プロジェクトのAuthenticationに行って、利用したいプロバイダを有効にする。今回は以下のようにした。
Twitterや、(使わないけど)Facebook、GitHubはAPIキーが必要になるけど、取ってくる方法わかるよね。
念のため、
例:Twitterの場合
https://developer.twitter.com/appsにいってCreate an appボタンをクリックして新しいアプリを作成。
Keys and tokensのところで、APIキーとシークレットをとってこれる。
Firebaseコンソールに戻って、以下のようにAPIキーとシークレットをペーストする。それからコールバックURLというのをコピーして、
今度はまたTwitterのアプリ画面に戻って、App detailsのCallback URLをペーストする。
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とかの文言もいらないのでその部分もコメントアウトする。
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>
を挿入した。
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のルール変更
動作確認
一瞬新しいレコードが表示されるけど、Firestoreには書き込めず今まで通りの画面が表示される
Sign in with phoneで動作確認
Sign in with phoneでやってみると、AmazonやFacebookのような巨大サイトとまったく同じように自分のスマフォにSMSで6桁の確認コードが送られてくるので感動~!
補足
見た目がカッコ悪い、別画面で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");
}
サインイン画面へボタンをクリックすると別ウインドウがでる。そこでサインインすればいいだけ。ログアウトボタンもそこに表示される。
日本語化したいが、ちと調べたがわからない・・・
参考:Angular で FirebaseUI を使う方法(angularfire2)