Cloud Run でのエンドユーザー認証のチュートリアル


このチュートリアルでは、以下で構成される投票サービスの作成方法について説明します。

  • ブラウザベースのクライアント。次の処理を行います。

    1. Identity Platform を使用して ID トークンを取得する。
    2. ユーザーがお気に入りの動物に投票する。
    3. 取得した ID トークンをリクエストに付加して、投票を処理する Cloud Run サーバーに送信する。
  • Cloud Run サーバー。次の処理を行います。

    1. 有効な ID トークンが提供され、エンドユーザーが正しく認証されていることを確認する。
    2. エンドユーザーの投票を処理する。
    3. 独自の認証情報を使用して、投票結果を Cloud SQL に送信して保管する。
  • 投票結果を保存する PostgreSQL データベース。

説明をわかりやすくするため、このチュートリアルでは Google をプロバイダとして使用します。ID トークンを取得するには、ユーザーは Google アカウントを使用して認証を行う必要があります。ただし、ユーザーのログインに他のプロバイダや認証方法を使用することもできます。

このサービスは、Secret Manager を使用して Cloud SQL インスタンスへの接続で使用されるセンシティブ データを保護し、セキュリティ リスクを最小限に抑えます。また、最小権限のサービス ID を使用して、データベースへのアクセスを保護します。

目標

次のことを行うサービスを作成、ビルドし、Cloud Run にデプロイします。

  • Identity Platform を使用し、Cloud Run サービスのバックエンドに対してエンドユーザーを認証します。

  • サービス用に最小権限の ID を作成して、Google Cloud リソースに対する最小のアクセス権を付与します。

  • Cloud Run サービスを PostgreSQL データベースに接続するときに Secret Manager を使用してセンシティブ データを扱います。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

始める前に

  1. Google Cloud アカウントにログインします。Google Cloud を初めて使用する場合は、アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  3. Google Cloud プロジェクトで課金が有効になっていることを確認します

  4. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタに移動

  5. Google Cloud プロジェクトで課金が有効になっていることを確認します

  6. Cloud Run, Secret Manager, Cloud SQL, Artifact Registry, and Cloud Build API を有効にします。

    API を有効にする

必要なロール

チュートリアルを完了するために必要な権限を取得するには、プロジェクトに対するオーナーroles/owner)IAM ロールを付与するよう管理者に依頼してください。ロールの付与の詳細については、アクセスの管理をご覧ください。

必要な権限は、カスタムロールや他の事前定義ロールから取得することもできます。

gcloud のデフォルトを設定する

Cloud Run サービスを gcloud のデフォルトに構成するには:

  1. デフォルト プロジェクトを設定します。

    gcloud config set project PROJECT_ID

    PROJECT_ID は、このチュートリアルで作成したプロジェクトの名前に置き換えます。

  2. 選択したリージョン向けに gcloud を構成します。

    gcloud config set run/region REGION

    REGION は、任意のサポートされている Cloud Run のリージョンに置き換えます。

Cloud Run のロケーション

Cloud Run はリージョナルです。つまり、Cloud Run サービスを実行するインフラストラクチャは特定のリージョンに配置され、そのリージョン内のすべてのゾーンで冗長的に利用できるように Google によって管理されます。

レイテンシ、可用性、耐久性の要件を満たしていることが、Cloud Run サービスを実行するリージョンを選択する際の主な判断材料になります。一般的には、ユーザーに最も近いリージョンを選択できますが、Cloud Run サービスで使用されている他の Google Cloud サービスのロケーションも考慮する必要があります。使用する Google Cloud サービスが複数のロケーションにまたがっていると、サービスの料金だけでなくレイテンシにも影響することがあります。

Cloud Run は、次のリージョンで利用できます。

ティア 1 料金を適用

  • asia-east1(台湾)
  • asia-northeast1(東京)
  • asia-northeast2(大阪)
  • europe-north1(フィンランド) リーフアイコン 低 CO2
  • europe-southwest1(マドリッド)
  • europe-west1(ベルギー) リーフアイコン 低 CO2
  • europe-west4(オランダ)
  • europe-west8(ミラノ)
  • europe-west9(パリ) リーフアイコン 低 CO2
  • me-west1(テルアビブ)
  • us-central1(アイオワ) リーフアイコン 低 CO2
  • us-east1(サウスカロライナ)
  • us-east4(北バージニア)
  • us-east5(コロンバス)
  • us-south1(ダラス)
  • us-west1(オレゴン) リーフアイコン 低 CO2

ティア 2 料金を適用

  • africa-south1(ヨハネスブルグ)
  • asia-east2(香港)
  • asia-northeast3(ソウル、韓国)
  • asia-southeast1(シンガポール)
  • asia-southeast2 (ジャカルタ)
  • asia-south1(ムンバイ、インド)
  • asia-south2(デリー、インド)
  • australia-southeast1(シドニー)
  • australia-southeast2(メルボルン)
  • europe-central2(ワルシャワ、ポーランド)
  • europe-west10(ベルリン)
  • europe-west12(トリノ)
  • europe-west2(ロンドン、イギリス) リーフアイコン 低 CO2
  • europe-west3(フランクフルト、ドイツ) リーフアイコン 低 CO2
  • europe-west6(チューリッヒ、スイス) リーフアイコン 低 CO2
  • me-central1(ドーハ)
  • me-central2(ダンマーム)
  • northamerica-northeast1(モントリオール) リーフアイコン 低 CO2
  • northamerica-northeast2(トロント) リーフアイコン 低 CO2
  • southamerica-east1(サンパウロ、ブラジル) リーフアイコン 低 CO2
  • southamerica-west1(サンティアゴ、チリ) リーフアイコン 低 CO2
  • us-west2(ロサンゼルス)
  • us-west3(ソルトレイクシティ)
  • us-west4(ラスベガス)

Cloud Run サービスをすでに作成している場合は、Google Cloud コンソールの Cloud Run ダッシュボードにリージョンが表示されます。

コードサンプルを取得する

使用するコードサンプルを取得するには:

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

    Node.js

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

    また、zip 形式のサンプルをダウンロードしてファイルを抽出してもかまいません。

    Python

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

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

    Java

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

    または、zip 形式のサンプルをダウンロードし、ファイルを抽出してもかまいません。

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

    Node.js

    cd nodejs-docs-samples/run/idp-sql/

    Python

    cd python-docs-samples/run/idp-sql/

    Java

    cd java-docs-samples/run/idp-sql/

アーキテクチャを可視化する

アーキテクチャ図
上の図は、エンドユーザーが Identity Platform によって表示された Google ログイン ポップアップを使用してログインした後、ユーザーの ID とともに再度 Cloud Run へリダイレクトされる様子を示しています。
  1. エンドユーザーが、Cloud Run サーバーに最初のリクエストを行います。

  2. クライアントがブラウザに読み込まれます。

  3. ユーザーが、Identity Platform による Google ログイン ポップアップ ウィンドウを使用してログイン認証情報を提供します。ログインしたユーザーにアラートが表示されます。

  4. 制御がサーバーにリダイレクトされます。エンドユーザーがクライアントを使用して投票します。クライアントは、Identity Platform から ID トークンを取得して、投票リクエスト ヘッダーに追加します。

  5. サーバーは、リクエストを受信すると、Identity Platform ID トークンを検証してエンドユーザーが適切に認証されていることを確認します。次に、サーバーは、独自の認証情報を使用して Cloud SQL に投票を送信します。

コアコードについて

このサンプルは、以下のようにクライアントとサーバーとして実装されます。

Identity Platform との統合: クライアント側のコード

このサンプルでは、Firebase SDK を使用して Identity Platform と統合し、ユーザーのログインと管理を行います。Identity Platform に接続するため、クライアント側の JavaScript はプロジェクトの認証情報への参照を構成オブジェクトとして保持し、必要な Firebase JavaScript SDK をインポートします。

const config = {
  apiKey: 'API_KEY',
  authDomain: 'PROJECT_ID.firebaseapp.com',
};
<!-- Firebase App (the core Firebase SDK) is always required and must be listed first-->
<script src="https://www.gstatic.com/firebasejs/7.18/firebase-app.js"></script>
<!-- Add Firebase Auth service-->
<script src="https://www.gstatic.com/firebasejs/7.18/firebase-auth.js"></script>

Firebase JavaScript SDK は、ポップアップ ウィンドウを表示してエンドユーザーに Google アカウントへのログインを求めることにより、ログインフローを処理します。次に、サービスにリダイレクトします。

function signIn() {
  const provider = new firebase.auth.GoogleAuthProvider();
  provider.addScope('https://www.googleapis.com/auth/userinfo.email');
  firebase
    .auth()
    .signInWithPopup(provider)
    .then(result => {
      // Returns the signed in user along with the provider's credential
      console.log(`${result.user.displayName} logged in.`);
      window.alert(`Welcome ${result.user.displayName}!`);
    })
    .catch(err => {
      console.log(`Error during sign in: ${err.message}`);
      window.alert('Sign in failed. Retry or check your browser logs.');
    });
}

ユーザーがログインに成功すると、クライアントは Firebase メソッドを使用して ID トークンを作成します。クライアントが、この ID トークンをサーバーに対するリクエストの Authorization ヘッダーに追加します。

async function vote(team) {
  if (firebase.auth().currentUser) {
    // Retrieve JWT to identify the user to the Identity Platform service.
    // Returns the current token if it has not expired. Otherwise, this will
    // refresh the token and return a new one.
    try {
      const token = await firebase.auth().currentUser.getIdToken();
      const response = await fetch('/', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          Authorization: `Bearer ${token}`,
        },
        body: 'team=' + team, // send application data (vote)
      });
      if (response.ok) {
        const text = await response.text();
        window.alert(text);
        window.location.reload();
      }
    } catch (err) {
      console.log(`Error when submitting vote: ${err}`);
      window.alert('Something went wrong... Please try again!');
    }
  } else {
    window.alert('User not signed in.');
  }
}

Identity Platform との統合: サーバーサイド コード

サーバーでは、Firebase Admin SDK を使用して、クライアントから送信されたユーザー ID トークンを確認します。提供された ID トークンが正しい形式で、期限切れではなく、適切に署名されていれば、メソッドはデコードされた ID トークンを返します。そのユーザーの Identity Platform uid が、サーバーにより抽出されます。

Node.js

const firebase = require('firebase-admin');
// Initialize Firebase Admin SDK
firebase.initializeApp();

// Extract and verify Id Token from header
const authenticateJWT = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (authHeader) {
    const token = authHeader.split(' ')[1];
    // If the provided ID token has the correct format, is not expired, and is
    // properly signed, the method returns the decoded ID token
    firebase
      .auth()
      .verifyIdToken(token)
      .then(decodedToken => {
        const uid = decodedToken.uid;
        req.uid = uid;
        next();
      })
      .catch(err => {
        req.logger.error(`Error with authentication: ${err}`);
        return res.sendStatus(403);
      });
  } else {
    return res.sendStatus(401);
  }
};

Python

def jwt_authenticated(func: Callable[..., int]) -> Callable[..., int]:
    """Use the Firebase Admin SDK to parse Authorization header to verify the
    user ID token.

    The server extracts the Identity Platform uid for that user.
    """

    @wraps(func)
    def decorated_function(*args: a, **kwargs: a) -> a:
        header = request.headers.get("Authorization", None)
        if header:
            token = header.split(" ")[1]
            try:
                decoded_token = firebase_admin.auth.verify_id_token(token)
            except Exception as e:
                logger.exception(e)
                return Response(status=403, response=f"Error with authentication: {e}")
        else:
            return Response(status=401)

        request.uid = decoded_token["uid"]
        return func(*args, **kwargs)

    return decorated_function

Java

/** Extract and verify Id Token from header */
private String authenticateJwt(Map<String, String> headers) {
  String authHeader =
      (headers.get("authorization") != null)
          ? headers.get("authorization")
          : headers.get("Authorization");
  if (authHeader != null) {
    String idToken = authHeader.split(" ")[1];
    // If the provided ID token has the correct format, is not expired, and is
    // properly signed, the method returns the decoded ID token
    try {
      FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
      String uid = decodedToken.getUid();
      return uid;
    } catch (FirebaseAuthException e) {
      logger.error("Error with authentication: " + e.toString());
      throw new ResponseStatusException(HttpStatus.FORBIDDEN, "", e);
    }
  } else {
    logger.error("Error no authorization header");
    throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
  }
}

サーバーを Cloud SQL に接続する

サービスは、/cloudsql/CLOUD_SQL_CONNECTION_NAME の形式を使用して、Cloud SQL インスタンスの Unix ドメイン ソケットに接続します。

Node.js

/**
 * Connect to the Cloud SQL instance through UNIX Sockets
 *
 * @param {object} credConfig The Cloud SQL connection configuration from Secret Manager
 * @returns {object} Knex's PostgreSQL client
 */
const connectWithUnixSockets = async credConfig => {
  const dbSocketPath = process.env.DB_SOCKET_PATH || '/cloudsql';
  // Establish a connection to the database
  return Knex({
    client: 'pg',
    connection: {
      user: credConfig.DB_USER, // e.g. 'my-user'
      password: credConfig.DB_PASSWORD, // e.g. 'my-user-password'
      database: credConfig.DB_NAME, // e.g. 'my-database'
      host: `${dbSocketPath}/${credConfig.CLOUD_SQL_CONNECTION_NAME}`,
    },
    ...config,
  });
};

Python

def init_unix_connection_engine(
    db_config: dict[str, int]
) -> sqlalchemy.engine.base.Engine:
    """Initializes a Unix socket connection pool for a Cloud SQL instance of PostgreSQL.

    Args:
        db_config: a dictionary with connection pool config

    Returns:
        A SQLAlchemy Engine instance.
    """
    creds = credentials.get_cred_config()
    db_user = creds["DB_USER"]
    db_pass = creds["DB_PASSWORD"]
    db_name = creds["DB_NAME"]
    db_socket_dir = creds.get("DB_SOCKET_DIR", "/cloudsql")
    cloud_sql_connection_name = creds["CLOUD_SQL_CONNECTION_NAME"]

    pool = sqlalchemy.create_engine(
        # Equivalent URL:
        # postgres+pg8000://<db_user>:<db_pass>@/<db_name>
        #                         ?unix_sock=<socket_path>/<cloud_sql_instance_name>/.s.PGSQL.5432
        sqlalchemy.engine.url.URL.create(
            drivername="postgresql+pg8000",
            username=db_user,  # e.g. "my-database-user"
            password=db_pass,  # e.g. "my-database-password"
            database=db_name,  # e.g. "my-database-name"
            query={
                "unix_sock": f"{db_socket_dir}/{cloud_sql_connection_name}/.s.PGSQL.5432"
                # e.g. "/cloudsql", "<PROJECT-NAME>:<INSTANCE-REGION>:<INSTANCE-NAME>"
            },
        ),
        **db_config,
    )
    pool.dialect.description_encoding = None
    logger.info("Database engine initialized from unix connection")

    return pool

Java

Spring Cloud Google Cloud PostgreSQL スターター統合を使用して、Spring JDBC ライブラリを使用する Google Cloud SQL の PostgreSQL データベースを操作します。DataSource Bean を自動的に構成するように Cloud SQL for MySQL 構成を設定します。これは、Spring JDBC と一体となり、データベースのクエリや変更などのオペレーションを可能にする JdbcTemplate オブジェクト Bean を提供します。

# Uncomment and add env vars for local development
# spring.datasource.username=${DB_USER}
# spring.datasource.password=${DB_PASSWORD}
# spring.cloud.gcp.sql.database-name=${DB_NAME}
# spring.cloud.gcp.sql.instance-connection-name=${CLOUD_SQL_CONNECTION_NAME}  
private final JdbcTemplate jdbcTemplate;

public VoteController(JdbcTemplate jdbcTemplate) {
  this.jdbcTemplate = jdbcTemplate;
}

Secret Manager で機密性の高い構成を処理する

Cloud SQL 構成などのセンシティブ データは、Secret Manager によって一元管理され、安全に保管されます。このサービスは、Secret Manager から環境変数を介してランタイムに Cloud SQL 認証情報を挿入します。詳しくは、Cloud Run でのシークレットの使用をご覧ください。

Node.js

// CLOUD_SQL_CREDENTIALS_SECRET is the resource ID of the secret, passed in by environment variable.
// Format: projects/PROJECT_ID/secrets/SECRET_ID/versions/VERSION
const {CLOUD_SQL_CREDENTIALS_SECRET} = process.env;
if (CLOUD_SQL_CREDENTIALS_SECRET) {
  try {
    // Parse the secret that has been added as a JSON string
    // to retrieve database credentials
    return JSON.parse(CLOUD_SQL_CREDENTIALS_SECRET.toString('utf8'));
  } catch (err) {
    throw Error(
      `Unable to parse secret from Secret Manager. Make sure that the secret is JSON formatted: ${err}`
    );
  }
}

Python

def get_cred_config() -> dict[str, str]:
    """Retrieve Cloud SQL credentials stored in Secret Manager
    or default to environment variables.

    Returns:
        A dictionary with Cloud SQL credential values
    """
    secret = os.environ.get("CLOUD_SQL_CREDENTIALS_SECRET")
    if secret:
        return json.loads(secret)

Java

/** Retrieve config from Secret Manager */
public static HashMap<String, Object> getConfig() {
  String secret = System.getenv("CLOUD_SQL_CREDENTIALS_SECRET");
  if (secret == null) {
    throw new IllegalStateException("\"CLOUD_SQL_CREDENTIALS_SECRET\" is required.");
  }
  try {
    HashMap<String, Object> config = new Gson().fromJson(secret, HashMap.class);
    return config;
  } catch (JsonSyntaxException e) {
    logger.error(
        "Unable to parse secret from Secret Manager. Make sure that it is JSON formatted: "
            + e);
    throw new RuntimeException(
        "Unable to parse secret from Secret Manager. Make sure that it is JSON formatted.");
  }
}

Identity Platform の設定

Identity Platform は、Google Cloud コンソールで手動で設定する必要があります。

  1. Google Cloud コンソールで Identity Platform の Marketplace ページに移動します。

    Identity Platform の Marketplace ページに移動

  2. [Identity Platform を有効化] をクリックします。

  3. OAuth 同意画面を作成します。

    1. 新しいウィンドウで、[API とサービス] > [認証情報] ページに移動します。

      [API とサービス] > [認証情報] ページに移動

    2. [同意画面を構成] をクリックします。

    3. テスト目的で [外部] を選択します。

    4. [作成] をクリックします。

    5. [アプリ情報] ダイアログで、次の操作を行います。

      1. アプリケーション名を入力します。
      2. 表示されたユーザー サポートメールのいずれかを選択します。
      3. デベロッパーの連絡先に使用するメールアドレスを入力します。
    6. [保存して次へ] をクリックします。

    7. [Scopes and Test Users] ダイアログで、[保存して次へ] をクリックします。

    8. [ダッシュボードに戻る] をクリックします。

    9. [公開ステータス] で、[アプリを公開] をクリックします。

    10. [確認] をクリックします。

  4. OAuth クライアント ID とシークレットを作成して取得します。

    1. [API とサービス] > [認証情報] ページに移動

    2. ページの上部にある [認証情報を作成] をクリックし、[OAuth client ID] を選択します。

    3. [アプリケーションの種類] で [ウェブ アプリケーション] を選択し、名前を入力します。

    4. client_idclient_secret をメモします。これらは後の手順で使用します。

  5. Google をプロバイダとして構成します。

    1. Cloud コンソールで [ID プロバイダ] ページに移動します。

      [ID プロバイダ] ページに移動

    2. [プロバイダを追加] をクリックします。

    3. リストから [Google] を選択します。

    4. [ウェブ SDK 構成] で、前の手順でメモした client_idclient_secret の値を入力します。

    5. [アプリケーションの構成] で [詳細を設定] をクリックします。

    6. apiKeyauthDomain の値をサンプルの static/config.js にコピーして、Identity Platform Client SDK を初期化します。

    7. [保存] をクリックします。

サービスのデプロイ

次の手順に沿って、インフラストラクチャのプロビジョニングおよびデプロイを完了するか、Cloud Shell で [Run on Google Cloud] をクリックしてプロセスを自動化します。

Google Cloud での実行

  1. Console または CLI を使用して、PostgSQL データベースの Cloud SQL インスタンスを作成します。

    gcloud sql instances create CLOUD_SQL_INSTANCE_NAME \
        --database-version=POSTGRES_12 \
        --region=CLOUD_SQL_REGION \
        --cpu=2 \
        --memory=7680MB \
        --root-password=DB_PASSWORD
  2. Cloud SQL の認証情報の値を postgres-secrets.json に追加します。

    Node.js

    {
      "CLOUD_SQL_CONNECTION_NAME": "PROJECT_ID:REGION:INSTANCE",
      "DB_NAME": "postgres",
      "DB_USER": "postgres",
      "DB_PASSWORD": "PASSWORD_SECRET"
    }
    

    Python

    {
      "CLOUD_SQL_CONNECTION_NAME": "PROJECT_ID:REGION:INSTANCE",
      "DB_NAME": "postgres",
      "DB_USER": "postgres",
      "DB_PASSWORD": "PASSWORD_SECRET"
    }
    

    Java

    {
      "spring.cloud.gcp.sql.instance-connection-name": "PROJECT_ID:REGION:INSTANCE",
      "spring.cloud.gcp.sql.database-name": "postgres",
      "spring.datasource.username": "postgres",
      "spring.datasource.password": "PASSWORD_SECRET"
    }

  3. コンソールまたは CLI を使用して、バージョニングされたシークレットを作成します。

    gcloud secrets create idp-sql-secrets \
        --replication-policy="automatic" \
        --data-file=postgres-secrets.json
  4. Console または CLI を使用して、サーバーのサービス アカウントを作成します。

    gcloud iam service-accounts create idp-sql-identity
  5. Console または CLI を使用して、Secret Manager と Cloud SQL にアクセスするためのロールを付与します。

    1. サーバーに関連付けられたサービス アカウントに、作成したシークレットへのアクセスを許可します。

      gcloud secrets add-iam-policy-binding idp-sql-secrets \
        --member serviceAccount:idp-sql-identity@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/secretmanager.secretAccessor
    2. サーバーに関連付けられたサービス アカウントに Cloud SQL へのアクセスを許可します。

      gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:idp-sql-identity@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/cloudsql.client
  6. Artifact Registry を作成します。

    gcloud artifacts repositories create REPOSITORY \
        --repository-format docker \
        --location REGION
    
    • REPOSITORY はリポジトリの名前です。プロジェクト内のリポジトリのロケーションごとに、リポジトリ名は一意でなければなりません。
  7. Cloud Build を使用してコンテナ イメージをビルドします。

    Node.js

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/idp-sql

    Python

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/idp-sql

    Java

    このサンプルでは、Jib を使用して一般的な Java ツールにより Docker イメージをビルドします。Jib は、Dockerfile や Docker をインストールせずにコンテナのビルドを最適化します。Jib を使用して Java コンテナを構築する方法の詳細を確認します。

    1. Docker を承認して Artifact Registry に push するには、gcloud 認証ヘルパーを使用します。

      gcloud auth configure-docker

    2. Jib Maven プラグインを使用して、コンテナをビルドし Artifact Registry に push します。

      mvn compile jib:build -Dimage=REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/idp-sql

  8. コンソールまたは CLI を使用して、コンテナ イメージを Cloud Run にデプロイします。このサーバーは、未認証のアクセスを許可するようにデプロイされています。これは、ユーザーがクライアントを読み込んで処理を開始できるようにするためです。サーバーは、投票リクエストに追加された ID トークンを手動で検証し、エンドユーザーを認証します。

    gcloud run deploy idp-sql \
        --image REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/idp-sql \
        --allow-unauthenticated \
        --service-account idp-sql-identity@PROJECT_ID.iam.gserviceaccount.com \
        --add-cloudsql-instances PROJECT_ID:REGION:CLOUD_SQL_INSTANCE_NAME \
        --update-secrets CLOUD_SQL_CREDENTIALS_SECRET=idp-sql-secrets:latest

    また、--service-account--add-cloudsql-instances--update-secrets の各フラグにも注意してください。これらはそれぞれ、サービス ID、Cloud SQL インスタンス接続、環境変数としてバージョン付きシークレット名を指定します。

最後の仕上げ

Identity Platform では、ユーザーがログインした後に、Cloud Run サービスの URL を許可されたリダイレクトとして承認する必要があります。

  1. [ID プロバイダ] ページでペンのアイコンをクリックして、Google プロバイダを編集します。

  2. 右側の [承認済みドメイン] で [ドメインを追加] をクリックし、Cloud Run サービスの URL を入力します。

    ビルドまたはデプロイの後、サービス URL はログで確認できます。また、次のコマンドを使用して確認することもできます。

    gcloud run services describe idp-sql --format 'value(status.url)'
  3. [API とサービス] > [認証情報] ページに移動

    1. OAuth クライアント ID の横にある鉛筆アイコンをクリックして編集し、Authorized redirect URIs click the [URI を追加] ボタンをクリックします。

    2. フィールドに、次の URL をコピーして貼り付け、ページ下部の [保存] ボタンをクリックします。

    https://PROJECT_ID.firebaseapp.com/__/auth/handler

試してみる

完成したサービスを試すには:

  1. ブラウザで、前述のデプロイの手順により提供された URL に移動します。

  2. [Google でログイン] ボタンをクリックして、認証フローを行います。

  3. 投票します。

    次のようになります。

    ユーザー インターフェースのスクリーンショットには、各チームへの投票数と投票の一覧が示されます。

これらのサービスを開発し続けることにした場合、Google Cloud の他のサービスへの Identity and Access Management(IAM)アクセスが制限されます。他の多くのサービスにアクセスするには、追加の IAM ロールをこれらのサービスに与える必要があることにご注意ください。

クリーンアップ

このチュートリアル用に新規プロジェクトを作成した場合は、そのプロジェクトを削除します。既存のプロジェクトを使用し、このチュートリアルで変更を加えずに残す場合は、チュートリアル用に作成したリソースを削除します。

プロジェクトを削除する

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

プロジェクトを削除するには:

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。

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

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

チュートリアル リソースを削除する

  1. このチュートリアルでデプロイした Cloud Run サービスを削除します。

    gcloud run services delete SERVICE-NAME

    SERVICE-NAME は、選択したサービス名です。

    Cloud Run サービスは Google Cloud コンソールから削除することもできます。

  2. チュートリアルの設定時に追加した gcloud のデフォルト リージョン構成を削除します。

     gcloud config unset run/region
    
  3. プロジェクト構成を削除します。

     gcloud config unset project
    
  4. このチュートリアルで作成した他の Google Cloud リソースを削除します。

次のステップ