Firebase Aucentication を使って、AngularアプリでのGoogleアカウント認証をする

#きっかけ（読み飛ばし可）
Angularで、とりあえずhtmlだけ作ってみました。

ログイン画面だけ作って、ふと「認証どうしよう？」って考えたときに、Google技術を扱う会社としてはとりあえずGoogle認証やな、ってなりました。

でも、いざ調べてみるとOAuthとか使おうとしたらAPIだったり、有料のサービスだったり、、、、
で、行き着いたのがFirebaseでした。
（もっと早くに気づくべきだったんでしょうけど:sweat_smile:）

ところが。いざ調査を始めると、Firebaseでも「メールアドレス＆パスワード」の認証はけっこうあったのですけど、「Google認証」となると意外と日本語の記述でやってる方が少なくて。。。
（調べ方が悪い？！）

というわけで書いてみました。


#環境
macOS Mojave 10.14.6
※Angularは実際はdocker上で動かしてますが、今回のエントリには関係ないかな、と思ったので記載していません。
（Firebaseってバージョンどこで見るのかな。。。）

```console
$ ng version
@angular-devkit/architect         0.803.19
@angular-devkit/build-angular     0.803.19
@angular-devkit/build-optimizer   0.803.19
@angular-devkit/build-webpack     0.803.19
@angular-devkit/core              8.3.19
@angular-devkit/schematics        8.3.19
@angular/cdk                      8.2.3
@angular/cli                      8.3.19
@angular/fire                     5.2.3
@angular/material                 8.2.3
@ngtools/webpack                  8.3.19
@schematics/angular               8.3.19
@schematics/update                0.803.19
rxjs                              6.4.0
typescript                        3.5.3
webpack                           4.39.2
```

#必要なもの

- **Google アカウント**（必須）
- GCPアカウント（任意。なくても全然余裕）
- ちょっと作りかけのAngularアプリ、もしくはAngularの簡単な知識（これはなくてもいけます。でも、あるとうれしい）

#手順
ざっと手順はこんな感じです。
　1. Firebaseの準備
　2. Firebase Authentication の設定
　3. Angular アプリの改修
　
では、順番にやっていきます。

##1.Firebaseの準備
###Firebase プロジェクトの作成
まず、Firebase のプロジェクトを作成します。
Firebaseは[こちら](https://firebase.google.com/?hl=ja)から。

「使ってみる」ボタンをポチするとGoogleアカウント認証画面に移動します。
そこで、用意していただいたGoogleアカウントでログインします。

すると、こんな画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/3d066589-64a4-b65b-29a1-8056796bf848.png)

今回は、新しくプロジェクトを作成します。
GCPプロジェクトと連携されているアカウントの場合、既存のGCPプロジェクト名が選択肢として表示されます。
**※※ 注意 ※※**
紐付けたGCPプロジェクトが課金対象の場合は自動的に「従量課金」プランが選択されます。
（無料は選べない。GCPが課金なんだからFirebaseも課金するよ、ってことですかね:cry:）
ただし、従量課金コースでも、一部無料枠があるようです。詳しくは以下参照ください。
[Firebase料金](https://firebase.google.com/pricing?hl=ja)

あずきの使っているGCPプロジェクトは会社から支給されている課金対象のsandboxだったため、ちょっとビビって無料コースにしたかったのです。。。

それはさておき、早速作成します。
プロジェクト名は基本的になんでもよさげなので、~~適当に付けます~~任意の名前を付けておきます。
※後で気づきましたが、これ、認証時のURLになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/852a79ca-9a62-4c37-b6c9-74edaa2738bc.png)

次の画面でGoogleアナリティクスを選択する画面があります。
推奨通り、設定をONにして続行すると、プロジェクトが作成されます。
こんな画面になりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/ef7c8f6e-8689-a1c3-4836-496463c1fbb8.png)

##2.Authentication の設定 
今回は Authentication 機能を使いたいので、スススッと下にいった、ここをポチします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/15cc88b6-c328-89e4-ffc8-be99fe8aefba.png)

###Google認証の有効化
`ログイン方法を選択`ボタンをポチ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/3187c194-29b3-a260-7830-3a182bc254da.png)

Googleアカウントで認証したいので、Googleの右側にある編集ボタン（えんぴつマーク）を選択。
Googleアカウント以外でも、いろんなアカウントで認証できるのですね。
Firebaseすごー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/6cbf098f-9d16-fb3b-3bb6-9df3464c8ab9.png)

右上の`有効にする`をONにして、`プロジェクトのサポートメール`に自分のアドレスだけ設定しておきます。
(何に使われるのか、見るの忘れましたが・・・）
その後、`保存`ボタンを押下して保存しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/518b58c0-3346-f940-f2b2-d2a5a4200fcc.png)

Googleが有効になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/9c57970e-4952-2a5d-fc57-01c9da0de8a2.png)


###アプリの登録（認証情報の取得）
認証機能をアプリで使用するためには、アプリに認証情報の設定が必要です。
ここで、認証情報を取得します。

今回はただのwebアプリなので、赤い枠の付いたマークをポチします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/d53dd10a-c0a0-3685-d66f-d877dd5a57dc.png)

`アプリのニックネーム`はなんでも良さげです。
自分が他のアプリと区別できるような名前を付けておくといいかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/fd34d595-52b8-bd95-3fac-d19f46fea1bc.png)

`アプリを登録`ボタンをポチると、接続するためのキーが表示されます。
ここには、「\<body>タグの下部に貼り付け〜」と書かれていますが、実際は後で設定しますので、そっとしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/adb3312d-a0f2-bc3f-4038-2c57e9172694.png)


##3.Angularアプリの修正
いよいよここからアプリの修正に入ります。
必要なもの、でも記載しましたが、前提として既に存在するAngularアプリに対して、Firebase Authentication の設定を行います。
新規で作成する場合は、[このあたりのチュートリアル](https://angular.jp/tutorial/toh-pt0)を参考にしてみてください。

ちなみに、作成するイメージはこんな感じです。
login画面からGoogle認証を行い、dashboardに遷移します。
dashboardではheaderコンポーネントを読み込んでおり、headerコンポーネント内ではGoogle認証で取得したユーザ情報のうち、「表示名」項目を表示したいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/710f082e-637d-8e39-6d10-649ac42f0167.png)




###firebase ライブラリのインストール
以下のコマンドを実行します。

```console
$ npm install firebase @angular/fire --save
```

###environment.ts
インストールが完了したら、以下のファイルを修正していきます。

`src > environments > environment.ts`

```typescript:environment.ts
export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    databaseURL: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    projectId: "xxxxxxxxxxxxx",
    storageBucket: "xxxxxxxxxxxxxxx",
    messagingSenderId: "xxxxxxxxxxxxxx",
    appId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
};
```

※ ここは`アプリの登録`にて取得した認証情報をコピペします。

※ 認証情報消しちゃったよー！という場合は、以下の「プロジェクトの設定」からでも確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/54381e9e-d93e-8fac-08cf-7ac50a5623c5.png)


###app.module.ts
こちらに記載の内容を追記してください。
（全コピペしちゃいけませんよ！！）

※ `AuthService` は後で作成しますので、今はエラーになっても大丈夫です。

`src > app > app.module.ts`

```typescript:app.module.ts

/* Firebase services */
import { AngularFireModule } from "@angular/fire";
import { AngularFireAuthModule } from "@angular/fire/auth";
import { environment } from '../environments/environment';

/* Auth service */
import { AuthenticationService } from './shared/authentication.service';

@NgModule({
  declarations: [...],
  imports: [
    AngularFireAuthModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
  ],
  providers: [AuthenticationService],
  bootstrap: [...],
  schemas: [...]
})

export class AppModule { }
```

###auth.service.ts
まずは認証を行うソース。
デフォルトでは存在しないファイルのため、以下のコマンドを実行して作成します。

```console
$ ng g s shared/auth
```
作成できたら、以下の内容に置き換えます。
ログインできたら`dashboard`コンポーネントに遷移します。

`src > app > share > auth.service.ts`

```typescript:auth.service.ts
import { Injectable, NgZone } from '@angular/core';
import { auth } from 'firebase/app';
import { User } from "./user";
import { Router } from "@angular/router";
import { AngularFireAuth } from "@angular/fire/auth";

@Injectable({
    providedIn: 'root'
})

export class AuthService {
    user: User;

    constructor(
        public router: Router,
        public ngZone: NgZone,
        public afAuth: AngularFireAuth,
        private angularFireAuth: AngularFireAuth
    ) {
        this.afAuth.authState.subscribe(user => {
            this.user = user;
        })
    }

    // Firebase SignInWithPopup
    OAuthProvider(provider) {
        return this.afAuth.auth.signInWithPopup(provider)
            .then((res) => {
                this.ngZone.run(() => {
                    this.router.navigate(['dashboard']);
                })
            }).catch((error) => {
                window.alert(error)
            })
    }

    // Firebase Google Sign-in
    SigninWithGoogle() {
        return this.OAuthProvider(new auth.GoogleAuthProvider())
            .then(res => {
                console.log('Successfully logged in!')
            }).catch(error => {
                console.log(error)
            });
    }

    // Firebase Logout 
    SignOut() {
        return this.afAuth.auth.signOut().then(() => {
            this.router.navigate(['login']);
        })
    }

}
```


###login.component.ts

こちらも、デフォルトでは存在しないソース。
確か、以下のコマンドで作れたはず。

```console
$ ng g c login
```
デフォルトから追記したのは、`constructor`に`AuthService`の紐付けを行うだけです。

`src > app > login > login.component.ts`

```typescript:login.component.ts

import { Component, OnInit } from '@angular/core';
// Add
import { AuthService } from 'src/app/shared/auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})

export class LoginComponent implements OnInit {

  // 引数を追加
  constructor(private authService: AuthService) { }

  ngOnInit() { }

}
```

###login.component.html

htmlは簡単に。`css`はちょっとここでは割愛します。
ポイントは\<button>タグに`(click)="authService.SigninWithGoogle()"`を付けることくらい。

`src > app > login > login.component.html`

```html:login.component.html

<form class="sign-up">
    <h1 class="sign-up-title">Sign in</h1>
    <button (click)="authService.SigninWithGoogle()" class="sign-up-button">
      Sign in with Google
    </button>
</form>
```

###dashboard.component.ts

遷移先の`dashboard`コンポーネントを作成していきます。
以下のコマンドでワンセット作成されます。

```console
$ ng gc dashboard
```

tsファイルはデフォルトから変更なし。

`src > app > dashboard > dashboard.component.ts`

```typescript:dashboard.component.ts
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}
```

###dashboard.component.html
こちらもheaderコンポーネント化を表示するだけ。

`src > app > dashboard > dashboard.component.html`

```html:dashboard.component.html
<app-header></app-header>
```

###header.component.ts

headerは部品として使いたかったので、ちょっとだけ階層深くなってます。
なので、最初に`import`するときの相対パスが一層深くなっています。
`AuthService`を`import`しているのは、ヘッダーでログインユーザ名を表示させたいのと、ログアウトボタンを制御するため。

こちらも`constructor`の引数を追加するだけです。

`src > app > common-cmp > header > header.component.ts`

```typescript:header.component.ts

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../shared/auth.service';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {

  // 引数追加
  constructor(public authService: AuthService) { }
  ngOnInit() {
  }
}
```

###header.component.html

`authService.user.emailVerified`で、認証できているどうかの状態を取得できます。
認証できていたら、`{{authService.user.displayName}}`で表示名を表示します。


`src > app > common-cmp > header > header.component.html`

```html:header.component.html
<div class="header">
  <span class="header_title">Title</span>
  <span *ngIf="authService.user.emailVerified" class="header_user_name">Hi!  {{authService.user.displayName}}</span>
  <button (click)="authService.SignOut()" *ngIf="authService.user.emailVerified" class="logout-button">Logout</button>
</div>
```

###app-routing.module.ts

最後にroutingの制御を追記します。
こちらのソースは標準で用意されている物なので、自分の環境に合わせて変更してください。

`src > app > app-routing.module.ts`

```typescript
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { DashboardComponent } from './dashboard/dashboard.component';

/*
 ここに追記。pathに合わせて、紐付けるコンポーネントを設定します。
*/
const routes: Routes = [
  {path: '', redirectTo: '/login', pathMatch: 'full' },
  {path: 'login', component: LoginComponent },
  {path: 'dashboard', component: DashboardComponent }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes),
    ReactiveFormsModule,  
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }
```

#で、どうなった？
これで、ログインからやってみます。
`Sign in with Google`ボタンをぽち。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/5fafbfc3-5f10-9f21-1a35-82dd77974bfc.png)

見慣れたログイン画面がポップアップで表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/1f543b82-a6b1-afa2-97a2-2e4da39bdd77.png)

こんな感じでログインできました。
ちょっとモザイク多めですが。。。
認証されたら表示されるログアウトボタンも無事表示されております。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525955/38a3eed4-759a-a8cb-41dc-7e200d282153.png)


#最後に
色々とあっちこっち行ったりしましたが、おかげさまでなんとかできました。
ただ、これまた課題は残ります。

- Googleアカウントを持っている人なら誰でもいける（でいいのか？）
- 認証はできたものの、そこからのセキュリティも考えなきゃ

とはいえ、思ったよりは簡単に認証機能が作れました。
他のSNSアカウントでも同じようにできそうです。
便利になりましたねー。





#参考
※メールアドレス、パスワードでの認証を日本語で丁寧に教えてくれる（日本語）
https://webbibouroku.com/Blog/Article/firebase-auth

※Firebaseの設定ならこちら（英語）
https://www.positronx.io/setup-angularfire2-library-in-angular-7-project/

※メールアドレスとパスワードの認証をするならここが参考になるかと（英語）
https://www.positronx.io/firebase-authentication-in-angular-8-with-angularfire2/

※これが一番近かったかも（英語）
https://www.positronx.io/angular-8-firebase-google-sign-in-tutorial/