LoginSignup
4
3

More than 3 years have passed since last update.

Google App Engine + Go + Angular + Firebase Authentication でログイン画面の実装

Posted at

はじめに

  • Go と Angular で実装したクライアント・サーバーアプリケーションで Firebase Authentication を使ってログイン機能を実装します。
  • また、実装したアプリを Google App Engine 環境へデプロイします。

手順概要

  1. Firebase Authentication の設定
  2. クライアントサイドのアプリケーションを作成
  3. サーバーサイドのアプリケーションを作成
  4. ローカル環境で実行して動作を確認
  5. Google App Engine 環境へデプロイして動作を確認

Firebase Authentication の設定

  • Firebase Authentication を利用できるようにするため、Firebase のプロジェクトを作成し、Authentication の設定を行います

手順

  1. Firebase Console にログイン
  2. プロジェクトを作成
  3. 開発 > Authentication に移動し ログイン方法 > Google で Google ログインを有効化
  4. アプリを追加 > ウェブ でウェブアプリを作成
  5. ウェブアプリ作成完了画面で表示される apiKey などの認証情報をメモしておく
  6. Authentication > ログイン方法 と移動し Google のログインを有効にする

クライアントサイドのアプリケーションを作成

  • Angular CLI でアプリケーションの雛形を作成し、Firebase Admin SDK を使った認証と、認証情報を使ったサーバーアプリケーションへのアクセスする機能を実装します。

アプリケーションの雛形の作成と SDK のインストール

# アプリケーションの作成
ng new gae-firebase-auth-sample-client

# SDK インストール
npm install firebase @angular/fire --save

Firebase 認証情報の設定

  • Firebase のウェブアプリ作成時に表示された認証情報を設定ファイルに記述します
src/environments/environment.ts
export const environment = {
  production: false,
  firebase: {
    apiKey: '<your-key>',
    authDomain: '<your-project-authdomain>',
    databaseURL: '<your-database-URL>',
    projectId: '<your-project-id>',
    storageBucket: '<your-storage-bucket>',
    messagingSenderId: '<your-messaging-sender-id>'
  }
};

Firebase Admin SDK の初期化

  • Firebase Admin SDK を初期化し、Authentication や Http リクエスト用のモジュールを DI できるように読み込みます。
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import {AngularFireModule} from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import {environment} from '../environments/environment';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireAuthModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

ログイン画面の実装

  • ログイン画面とサーバーアプリケーションにアクセスする機能を実装します。
  • ログイン処理は DI で初期化される AppComponent.afAuth によって管理され、未ログインならログインボタンを表示し、ログイン済みならログアウトボタンとサーバーへアクセスするボタンが表示されます。
  • サーバーへのアクセス時には afAuth から取得できる ID トークンをヘッダに付与して認証を行います。
src/app/app.component.ts
import { Component } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { auth } from 'firebase/app';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {environment} from '../environments/environment';

@Component({
  selector: 'app-root',
  template: `
    <div *ngIf="afAuth.user | async as user; else showLogin">
      <h1>Hello {{ user.displayName }}!</h1>
      <button (click)="logout()">Logout</button>
      <button (click)="accessServer()">Access Server</button>
    </div>
    <ng-template #showLogin>
      <p>Please login.</p>
      <button (click)="login()">Login with Google</button>
    </ng-template>
  `,
})
export class AppComponent {
  constructor(public afAuth: AngularFireAuth, private http: HttpClient) {
  }
  // Firebase Authentication のログイン実行
  login() {
    this.afAuth.auth.signInWithPopup(new auth.GoogleAuthProvider());
  }
  // Firebase Authentication のログアウト実行
  logout() {
    this.afAuth.auth.signOut();
  }
  // Firebase Authentication の ID Token をヘッダに付与してサーバーアクセスする
  accessServer() {
    this.afAuth.auth.currentUser.getIdToken().then((idToken: string) => {
      const header = new HttpHeaders().set('Authorization', 'Bearer ' + idToken);
      this.http.get(environment.serverUrl, {headers: header}).subscribe((res: string) => {
        console.log(res);
      });
    });
  }
}

サーバーサイドのアプリケーションを作成

  • Go 1.12 で Firebase Authentication の ID トークンを受け取って検証を行うだけのサーバーアプリケーションを作成します
  • 依存パッケージ管理は Go Modules を使用します

メインファイルの作成

  • net/http を使って ID トークンを検証するだけの HTTP サーバーを実装します
  • ローカル環境での実行時にはサーバーアプリケーションとクライアントアプリケーションが別ドメインになるため、環境変数で CORS を有効にできるように用意しておきます
app.go
package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "strings"

    firebase "firebase.google.com/go"
    "firebase.google.com/go/auth"
    "github.com/rs/cors"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", top)

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
        log.Printf("Defaulting to port %s", port)
    }
    addr := fmt.Sprintf(":%s", port)

    if os.Getenv("ENABLE_CORS") != "" {
        c := cors.New(cors.Options{
            AllowedOrigins: []string{"http://localhost:4200"},
            AllowedHeaders: []string{"Authorization"},
            Debug:          true,
        })
        log.Fatal(http.ListenAndServe(addr, c.Handler(mux)))
    } else {
        log.Fatal(http.ListenAndServe(addr, mux))
    }
}

func top(w http.ResponseWriter, r *http.Request) {
    // Firebase Auth クライアントの作成
    ctx := r.Context()
    client, err := newClient(ctx)
    if err != nil {
        log.Fatalf("error initializing app: %v\n", err)
    }

    // ヘッダから ID Token の取得
    idToken := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")

    // ID Token の検証
    token, err := client.VerifyIDToken(ctx, idToken)
    if err != nil {
        log.Fatalf("error verifying ID token: %v\n", err)
    }

    fmt.Fprintf(w, `{"result":"verified id token. %+v"}`, token)
}

func newClient(ctx context.Context) (*auth.Client, error) {
    app, err := firebase.NewApp(ctx, nil)
    if err != nil {
        return nil, err
    }
    client, err := app.Auth(ctx)
    if err != nil {
        return nil, err
    }
    return client, err
}

依存パッケージの管理

  • Go Modules を使って依存パッケージをダウンロードします
go mod init
go mod download

ローカル環境で実行して動作を確認

  • クライアントアプリケーションとサーバーアプリケーションをそれぞれローカルで実行し、ログイン及びサーバーアクセスの動作確認を行います。

サーバーアプリケーションの実行

  • サーバーアプリケーションをローカルで実行する際には Firebase へアクセスするためにサービスアカウントの秘密鍵を用意する必要があります

Firebase のサービスアカウントの秘密鍵のダウンロード

  • Firebase Console にログイン
  • 設定 > プロジェクトの設定 に移動し サービスアカウント > Firebase Admin SDK を選択
  • 新しい秘密鍵の作成 ボタン押下で JSON ファイルをダウンロード

実行

GOOGLE_APPLICATION_CREDENTIALS=${秘密鍵ファイルのパス} \
ENABLE_CORS=1 \
go run app.go

クライアントアプリケーションの実行

ng serve

動作確認

  • http://localhost:4200 へアクセスし Login with Google ボタンを押下すると Google 認証画面へリダイレクトされて認証が行われます
  • ログイン状態では Logout ボタンでログアウト処理が Access Server でサーバーアクセス処理が実行されます。サーバーアクセスの結果は Console で確認できます。
  • Firebase Authentication による認証ができていると Firebase コンソールから作成済みユーザーの一覧も確認できます。

Google App Engine 環境へデプロイして動作を確認

クライアントアプリケーションのビルド

  • クライアントアプリケーションをビルドしてサーバーアプリケーションはいかに出力し Google App Engine で配信できるようにする
ng build --prod --output-path=${PATH_TO_SERVER_APP}/client/ --base-href=/client/

サーバーアプリケーションの Google App Engine 設定

  • Google App Engine 用の設定ファイル app.yaml を作成する
  • /client/ へのリクエストをクライアントアプリケーションのファイルで処理できるように handlers 設定を行います
app.yaml
runtime: go112

handlers:
  - url: /client/(.*\.(gif|png|jpeg|jpg|css|js|ico))$
    static_files: client/\1
    upload: client/(.*)
  - url: /client/(.*)
    static_files: client/index.html
    upload: client/index.html
  - url: /.*
    script: auto

Firebase Authentication 設定更新

  • Firebase Authentication で Google App Engine のドメインから Google ログインができるように許可ドメインを追加します。

手順

  • Firebase コンソールにログイン
  • Authentication > ログイン設定 に移動し
  • 承認済みドメインプロジェクト名.appspot.com を追加

デプロイ

gcloud app deploy

動作確認

  • https://{プロジェクト名}.appsopt.com/client/ にアクセスしてローカル環境と同様に動作確認を行います

参考サイト

ソース

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3