Angular8でFirebaseを使ってGoogleアカウント認証したユーザー情報をFirestoreへ登録する

プログラミング
プログラミング

前回は、Angular8とBootstrap4を使って、ポートフォリオサイトの雛形を作りました。

今回は、以下を実装します。

  • Firebaseを使ったGoogleアカウント認証機能
  • 認証したアカウント情報の一部をFireStoreに格納する
  • 認証したユーザー情報を表示するプロフィールページ

環境

以下の通りです。

  • OS: Ubuntu 18.04
  • Angular: 8.2.14
  • Angular CLI: 8.3.19
  • Node.js: 12.13.0
  • firebase@7.5.0
  • @angular/fire@5.2.3

手順

大まかな流れとしては、以下の通りです。

  • Firebaseでプロジェクト作成
  • AngularからFirebaseを操作するAngularFireをセットアップ
  • トップページにGoogleアカウント認証機能を実装
  • プロフィールページを実装

それでは始めましょう。

Firebaseでプロジェクト作成

Firebaseのサイトにアクセスします。
Googleアカウントが必要なので、持っていない場合は新規に取得しましょう。

案内に従ってプロジェクトを作成したら、以下の部分をメモしておきます。

Project Overview > プロジェクトの設定

「全般タブ」のプロジェクトIDと、ウェブAPIキー

「クラウド メッセージング」タブの送信者ID

確認し終わったら、実際に、AunglarJSアプリケーションに、Googleアカウントログイン機能を実装していきます。

AngularFireのセットアップ

AngularFireとFirebaseをインストール

$ npm install @angular/fire firebase --save

プロジェクトのパラメーターファイルにFirebaseの設定を追記
「Firebaseのセットアップ」で確認した値を使います。

$ vi src/environments/environment.ts
export const environment = {
  production: false,
  firebase: {
    apiKey: '<ウェブAPIキー>',
    authDomain: "<プロジェクトID>.firebaseapp.com",
    databaseURL: "https://<プロジェクトID>.firebaseio.com",
    projectId: "<プロジェクトID>",
    storageBucket: "<プロジェクトID>.appspot.com",
    messagingSenderId: "<送信者ID>",
  }
};

Googleアカウント認証処理の実装

core moduleを作成

$ ng g module core

core.moduleで、AngularFirebaseモジュールを読み込む

$ vi src/app/core/core.module.ts
//...
import { AuthService } from './auth.service';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFirestoreModule } from '@angular/fire/firestore';
//...

@NgModule({
  declarations: [],
  imports: [
  //...
    AngularFireAuthModule,
    AngularFirestoreModule
  //...
  ],
  providers: [AuthService]
})
export class CoreModule { }

AuthServiceは、認証に使う処理をまとめたものを後程作るので、ここでは定義のみ

次に、app.moduleに”core.module”をインポートします。

$ vi src/app/app.module.ts
import { CoreModule } from './core/core.module';
import { AngularFireModule } from '@angular/fire';
import { environment } from '../environments/environment';
// ...

@NgModule({
  // ...
  imports: [
    BrowserModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebase),
    CoreModule,
  ]
})
export class AppModule { }

認証関連のサービスをAuthServiceとして作成

$ ng g service core/auth

認証関連の処理を追加

$ vi src/app/core/auth.service.ts
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

import { auth } from 'firebase/app';
import { AngularFireAuth } from '@angular/fire/auth';
import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore';

import { Observable, of } from 'rxjs';
import { switchMap} from 'rxjs/operators';

interface User {
  uid: string;
  email: string;
  photoURL?: string;
  displayName?: string;
  favoriteColor?: string;
}


@Injectable({ providedIn: 'root' })
export class AuthService {

  user: Observable<User>;

  constructor(
    private afAuth: AngularFireAuth,
    private afs: AngularFirestore,
    private router: Router
  ) {

      //// Get auth data, then get firestore user document || null
      this.user = this.afAuth.authState.pipe(
        switchMap(user => {
          if (user) {
            return this.afs.doc<User>(`users/${user.uid}`).valueChanges()
          } else {
            return of(null)
          }
        })
      )
    }

  googleLogin() {
    const provider = new auth.GoogleAuthProvider()
    return this.oAuthLogin(provider);
  }

  private oAuthLogin(provider) {
    return this.afAuth.auth.signInWithPopup(provider)
      .then((credential) => {
        this.updateUserData(credential.user)
      })
  }


  private updateUserData(user) {
    // Sets user data to firestore on login

    const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/${user.uid}`);

    const data: User = {
      uid: user.uid,
      email: user.email,
      displayName: user.displayName,
      photoURL: user.photoURL
    }

    return userRef.set(data, { merge: true })

  }


  signOut() {
    this.afAuth.auth.signOut().then(() => {
        this.router.navigate(['/']);
    });
  }
}

ユーザープロファイルページを生成

$ ng g component user-profile

認証サービスをインポート

$ vi src/app/user-profile/user-profile.component.ts
import { AuthService } from '../core/auth.service';
//...

export class UserProfileComponent implements OnInit {

  constructor(private auth: AuthService) { }


//...
}

ユーザープロフィール画面を作成

$ vi src/app/user-profile/user-profile.component.html
<div *ngIf="auth.user | async; then authenticated else guest">
        <!-- template will replace this div -->
</div>


<!-- User NOT logged in -->
<ng-template #guest>
    <h3>Howdy, GUEST</h3>
    <p>Login to get started...</p>

    <button (click)="auth.googleLogin()">
        <i class="fa fa-google"></i> Connect Google
    </button>

</ng-template>


<!-- User logged in -->
<ng-template #authenticated>
    <div *ngIf="auth.user | async as user">
        <h3>Howdy, {{ user.displayName }}</h3>
        <img  [src]="user.photoURL">
        <p>UID: {{ user.uid }}</p>
        <p>Favorite Color: {{ user?.favoriteColor }} </p>
        <button (click)="auth.signOut()">Logout</button>
    </div>
</ng-template>

ルーターを追加

$ vi src/app/app-routing.module.ts
import { UserProfileComponent } from './user-profile/user-profile.component';
//...

const routes: Routes = [
  ///...
  { path: 'user/profile', component: UserProfileComponent},
];

app.componentにAuthServiceをインジェクションして、ログイン/ログアウト処理を実装

$ vi src/app/app.component.ts
import { AuthService } from './core/auth.service';
//...

export class AppComponent {
  //...
  constructor(private authService: AuthService) { }

  login() {
    this.authService.googleLogin();
  }
  logout() {
    this.authService.signOut();
  }
  //...
}

ヘッダーナビゲーション内に、ログインボタンを配置
ログイン後は、プロファイルページへのリンクがついたユーザー名を表示します。

$ vi src/app/app.component.html
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
  <a class="navbar-brand" href="#">ポートフォリオ</a>
  <div class="collapse navbar-collapse" id="navbarCollapse">
  <!-- ここから -->
    <div class="ml-auto">
      <div *ngIf="authService.user | async as user; else showLogin">
        <span style="color: white"> ようこそ<a routerLink="/user/profile">{{ user.displayName }}</a></span>
        <button class="btn btn-primary ml-2" (click)="logout()">Logout</button>
      </div>
      <ng-template #showLogin>
        <button class="btn btn-primary ml-2" (click)="login()">Login with Google</button>
      </ng-template>
    </div>
  <!-- ここまで -->
  </div>
</nav>
...

結果

トップページを確認すると、画面右上にログインボタンが表示されています。

トップページを確認すると、画面右上にログインボタンが表示されています。

ログインすると、ボタンがログアウトボタンに変わり、隣にGoogleアカウントのユーザー名が表示されます。

ログインすると、ボタンがログアウトボタンに変わり、隣にGoogleアカウントのユーザー名が表示されます。

ユーザー名をクリックすると、ユーザーのプロフィールページへ遷移します。

ユーザー名をクリックすると、ユーザーのプロフィールページへ遷移します。

ログインしていない状態で、プロフィールページへアクセスすると、ログイン画面が表示されます。

ログインしていない状態で、プロフィールページへアクセスすると、ログイン画面が表示されます。

FirebaseStoreを見ると、データが登録されています。

Database > Cloud Firestore > データ
FirebaseStoreを見ると、データが登録されています。

以上で実装完了です。

まとめ

今回は、AngularFirebaseを使って、ログイン認証処理と、Cloud FireStoreへデータを登録してみました。

次回は、ユーザーデータを使った実践的なアプリケーションを作りたいと思います。

参考

GitHub公式

GitHub - angular/angularfire: The official Angular library for Firebase.
The official Angular library for Firebase. Contribute to angular/angularfire development by creating an account on GitHub.

AngularFirebase Lesson

Google OAuth with @angular/fire
Build a Firebase Google signin user authentication flow with @angular/fire that saves custom user data to the Firestore.

コメント

タイトルとURLをコピーしました