Firebase を使用した App Engine 上でのユーザーの認証

このチュートリアルでは、Firebase Authentication、Google App Engine スタンダード環境、Google Cloud Datastore を使用してユーザー認証情報を取得、確認、保存する方法を示します。

本書では、Firenotes という名前のシンプルなメモ編集アプリケーションの使い方について説明します。このアプリケーションは、ユーザーのメモを個人用のノートブックに保存します。ノートブックはユーザーごとに保存され、各ユーザーの一意の Firebase Authentication ID によって識別されます。アプリケーションには次のコンポーネントがあります。

  • フロントエンドはログイン ユーザー インターフェースを構成し、Firebase Authentication ID を取得します。また、認証状態の変更を処理し、ユーザーにメモを表示します。

  • FirebaseUI は、ユーザー ログイン、複数のプロバイダの 1 つのアカウントへのリンク、パスワードの回復などを処理するオープンソースのドロップイン ソリューションです。認証のおすすめの方法を実装して、スムーズで安全なログイン方式を実現します。

    FirebaseUI

  • バックエンドは、ユーザーの認証状態を検証し、ユーザーのプロフィール情報およびメモを返します。

このアプリケーションは、NDB クライアント ライブラリを使用してユーザー認証情報を Cloud Datastore に保存しますが、必要に応じて、自分で認証情報をデータベースに保存することもできます。

次の図は、フロントエンドとバックエンドが相互に通信する方法、およびユーザー認証情報が Firebase からデータベースに移動する方法を示しています。

アーキテクチャ図

Firenotes は、Flask ウェブ アプリケーション フレームワークをベースにしています。サンプルアプリが Flask を使用しているのは、単純で使いやすいからです。ただし、ここで説明する概念やテクノロジーは、使用するフレームワークに関係なく、適用可能です。

目標

  • Firebase Authentication ユーザー インターフェースを構成する。
  • Firebase ID トークンを取得し、サーバー側の認証を使用して確認する。
  • ユーザー認証情報と関連データを Cloud Datastore に保存する。
  • NDB クライアント ライブラリを使用してデータベースに問い合わせる。
  • アプリを App Engine にデプロイする。

費用

このチュートリアルでは、以下を含む、Cloud Platform の課金対象となるコンポーネントを使用しています。

  • Google Cloud Datastore

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを出すことができます。 Cloud Platform を初めて使用する方は、無料トライアルをご利用いただけます。

始める前に

  1. GitPython 2.7 をインストールします。最新バージョンの Python のインストールなど、Python 開発環境の設定の詳細については、Google Cloud Platform の Python 開発環境の設定をご覧ください。
  2. Google アカウントにログインします。

    Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。

  3. GCP プロジェクトを選択または作成します。

    [リソースの管理] ページに移動

  4. Cloud SDK をインストールして初期化します。

すでに SDK をインストールして別のプロジェクトに初期化してある場合は、gcloud プロジェクトを Firenotes に使用している App Engine プロジェクト ID に設定します。gcloud ツールを使用してプロジェクトを更新するための特定のコマンドについては、Cloud SDK 構成の管理をご覧ください。

サンプルアプリのクローンを作成する

サンプルをローカルマシンにダウンロードするには、次のようにします。

  1. サンプル アプリケーション レポジトリをローカルマシンにクローン作成します。

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    または、zip ファイルとしてサンプルをダウンロードして解凍することもできます。

  2. サンプルコードが含まれるディレクトリに移動します。

    cd python-docs-samples/appengine/standard/firebase/firenotes
    

Firebase Authentication ユーザー インターフェースを追加する

FirebaseUI を設定して ID プロバイダを有効にするには、次のようにします。

  1. Firebase をアプリに追加します。
  2. backend/app.yaml ファイルを編集し、Firebase プロジェクト ID を環境変数に入力します。

    runtime: python27
    api_version: 1
    threadsafe: true
    service: backend
    
    handlers:
    - url: /.*
      script: main.app
    
    env_variables:
      # Replace with your Firebase project ID.
      FIREBASE_PROJECT_ID: '<PROJECT_ID>'
    

  3. ユーザーに提供するプロバイダを選択して、FirebaseUI ログイン ウィジェットを構成します。

    // Firebase log-in widget
    function configureFirebaseLoginWidget() {
      var uiConfig = {
        'signInSuccessUrl': '/',
        'signInOptions': [
          // Leave the lines as is for the providers you want to offer your users.
          firebase.auth.GoogleAuthProvider.PROVIDER_ID,
          firebase.auth.FacebookAuthProvider.PROVIDER_ID,
          firebase.auth.TwitterAuthProvider.PROVIDER_ID,
          firebase.auth.GithubAuthProvider.PROVIDER_ID,
          firebase.auth.EmailAuthProvider.PROVIDER_ID
        ],
        // Terms of service url
        'tosUrl': '<your-tos-url>',
      };
    
      var ui = new firebaseui.auth.AuthUI(firebase.auth());
      ui.start('#firebaseui-auth-container', uiConfig);
    }

  4. [認証] > [ログイン方法] をクリックして、選択したプロバイダを Firebase コンソール内に保持できるようにします。その後、[ログイン プロバイダ] で、カーソルをプロバイダの上に移動し、鉛筆アイコンをクリックします。

    プロバイダにログイン

    1. [有効] ボタンを切り替え、サードパーティ ID プロバイダの場合は、プロバイダのデベロッパー サイトからプロバイダ ID とシークレットを入力します。Firebase ドキュメントの、FacebookTwitter、および GitHub ガイドの「始める前に」に、具体的な指示が記載されています。プロバイダを有効にしたら、[保存] をクリックします。

      有効ボタンの切り替え

    2. Firebase コンソールの [承認済みドメイン] で [ドメインを追加] をクリックし、App Engine でアプリのドメインを次の形式で入力します。

      [PROJECT_ID].appspot.com
      

      ドメイン名の前に http:// を付けないでください。

依存関係のインストール

backend ディレクトリに移動して、アプリケーション セットアップを完了します。

  1. 仮想環境をセットアップして起動します。

    pip install virtualenv
    virtualenv env
    source env/bin/activate
    
  2. App Engine SDK に含まれていないサードパーティ要件をインストールします。

    pip install -r requirements.txt -t lib
    

    appengine_config.py で、vendor.add() メソッドがライブラリを lib ディレクトリに登録します。

アプリケーションをローカルで実行する

アプリケーションをローカルで実行するには、App Engine ローカル開発用サーバーを使用します。

  1. main.js で次の URL を backendHostURL として追加します。

    http://localhost:8081

  2. アプリケーションのルート ディレクトリに移動します。その後で、開発用サーバーを始動します。

    dev_appserver.py frontend/app.yaml backend/app.yaml
    
  3. ウェブブラウザで http://localhost:8080/ にアクセスします。

サーバーでユーザーを認証する

これで、プロジェクトのセットアップと開発用のアプリケーションの初期化が完了しました。コードを辿りながら、サーバー上で Firebase ID トークンを取得して確認する方法を見てみましょう。

Firebase から ID トークンを取得する

サーバー側の認証の最初の手順は、アクセス トークンの取得と確認です。認証リクエストは、Firebase からの onAuthStateChanged() リスナーを使用して処理されます。

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    $('#logged-out').hide();
    var name = user.displayName;

    /* If the provider gives a display name, use the name for the
    personal welcome message. Otherwise, use the user's email. */
    var welcomeName = name ? name : user.email;

    user.getToken().then(function(idToken) {
      userIdToken = idToken;

      /* Now that the user is authenicated, fetch the notes. */
      fetchNotes();

      $('#user').text(welcomeName);
      $('#logged-in').show();

    });

  } else {
    $('#logged-in').hide();
    $('#logged-out').show();

  }

ユーザーがログインすると、コールバックの Firebase getToken() メソッドが JSON Web Token(JWT)形式で Firebase ID トークンを返します。

サーバーでトークンを確認する

ユーザーがログインすると、フロントエンド サービスが AJAX GET リクエストを通してユーザーのノートブック内の既存のメモをフェッチします。これには、ユーザーのデータにアクセスする許可が必要なため、JWT が Bearer スキーマを使用してリクエストの Authorization ヘッダーで送信されます。

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  })

クライアントがサーバー データにアクセスするには、サーバーでトークンが Firebase によって署名されていることを確認する必要があります。このトークンは、Python 用の Google 認証ライブラリを使用して確認できます。認証ライブラリの verify_firebase_token 関数を使用して、署名なしトークンを確認し、要求を抽出します。

id_token = request.headers['Authorization'].split(' ').pop()
claims = google.oauth2.id_token.verify_firebase_token(
    id_token, HTTP_REQUEST)
if not claims:
    return 'Unauthorized', 401

各 ID プロバイダは別々の要求セットを送信しますが、それぞれに一意のユーザー ID を持つ 1 つ以上の sub 要求と nameemail などの特定のプロフィール情報を提供する 1 つの要求が含まれています。これを使用して、アプリのユーザー エクスペリエンスをパーソナライズすることができます。

Cloud Datastore でのユーザーデータの管理

ユーザーを認証したら、それに関するユーザーのデータを保存してログイン セッションが終わるまで維持する必要があります。以降のセクションでは、メモを Cloud Datastore エンティティとして保存し、それらをユーザー ID で区別する方法について説明します。

ユーザー データを保存するエンティティの作成

Cloud Datastore のエンティティ作成は、整数や文字列などの特定のプロパティを使用して NDB モデルクラスを宣言することによってできます。Cloud Datastore は、種類でエンティティにインデックスを付けます。Firenotes の場合は、各エンティティの種類は Note です。クエリを出すために、各 Noteキー名付きで保存されます。キー名は、前のセクションの sub 要求から取得されたユーザー ID です。

次のコードは、エンティティのプロパティを設定するために、エンティティの作成時にモデルクラスとしてコンストラクタ メソッドを使用する方法と、作成後に個別のプロパティを割り当てる方法の両方を示しています。

data = request.get_json()

# Populates note properties according to the model,
# with the user ID as the key name.
note = Note(
    parent=ndb.Key(Note, claims['sub']),
    message=data['message'])

# Some providers do not provide one of these so either can be used.
note.friendly_id = claims.get('name', claims.get('email', 'Unknown'))

新しく作成した Note を Cloud Datastore に書き込むには、note オブジェクトに対して put() メソッドを呼び出します。

ユーザー データの取得

特定のユーザー ID に関連付けられたユーザー データを取得するには、NDB query() メソッドを使用してデータベースを検索し、同じエンティティ グループ内のメモを見つけます。同じグループ内のエンティティ、つまり、祖先パスは、共通のキー名(この場合はユーザー ID)を共有します。

def query_database(user_id):
    """Fetches all notes associated with user_id.

    Notes are ordered them by date created, with most recent note added
    first.
    """
    ancestor_key = ndb.Key(Note, user_id)
    query = Note.query(ancestor=ancestor_key).order(-Note.created)
    notes = query.fetch()

    note_messages = []

    for note in notes:
        note_messages.append({
            'friendly_id': note.friendly_id,
            'message': note.message,
            'created': note.created
        })

    return note_messages

クエリ データをフェッチして、メモをクライアントに表示することができます。

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  }).then(function(data){
    $('#notes-container').empty();
    // Iterate over user data to display user's notes from database.
    data.forEach(function(note){
      $('#notes-container').append($('<p>').text(note.message));
    });
  });
}

アプリをデプロイする

これで Firebase Authentication と App Engine アプリケーションが正常に統合されました。実動環境で動作しているアプリケーションを確認するには、次の手順を実行します。

  1. main.js のバックエンド ホスト URL を https://backend-dot-[PROJECT_ID].appspot.com に変更します。[PROJECT_ID] を自分のプロジェクト ID に置き換えます。
  2. Cloud SDK コマンドライン インターフェースを使用してアプリケーションをデプロイします。

    gcloud app deploy backend/index.yaml frontend/app.yaml backend/app.yaml
    
  3. https://[PROJECT_ID].appspot.com で実動しているアプリケーションを表示します。

クリーンアップ

このチュートリアルで使用したリソースの Google Cloud Platform アカウントが課金されないようにするために、App Engine プロジェクトを削除します。

プロジェクトの削除

課金を停止する最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除する手順は次のとおりです。

  1. GCP Console で [プロジェクト] ページに移動します。

    プロジェクト ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

次のステップ

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Python の App Engine スタンダード環境