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. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Enable the Cloud Run, Secret Manager, Cloud SQL, Artifact Registry, and Cloud Build APIs.

    Enable the APIs

必要なロール

チュートリアルを完了するために必要な権限を取得するには、プロジェクトに対する次の 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(大阪)
  • asia-south1(ムンバイ、インド)
  • europe-north1(フィンランド) リーフアイコン 低 CO2
  • europe-southwest1(マドリッド) リーフアイコン 低 CO2
  • europe-west1(ベルギー) リーフアイコン 低 CO2
  • europe-west4(オランダ) リーフアイコン 低 CO2
  • europe-west8(ミラノ)
  • europe-west9(パリ) リーフアイコン 低 CO2
  • me-west1(テルアビブ)
  • us-central1(アイオワ) リーフアイコン 低 CO2
  • us-east1(サウスカロライナ)
  • us-east4(北バージニア)
  • us-east5(コロンバス)
  • us-south1(ダラス) リーフアイコン 低 CO2
  • us-west1(オレゴン) リーフアイコン 低 CO2

ティア 2 料金を適用

  • africa-south1(ヨハネスブルグ)
  • asia-east2(香港)
  • asia-northeast3(ソウル、韓国)
  • asia-southeast1(シンガポール)
  • asia-southeast2 (ジャカルタ)
  • asia-south2(デリー、インド)
  • australia-southeast1(シドニー)
  • australia-southeast2(メルボルン)
  • europe-central2(ワルシャワ、ポーランド)
  • europe-west10(ベルリン) リーフアイコン 低 CO2
  • 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 ライブラリを使用する 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. OAuth 同意画面のページを選択します。

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

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

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

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

    7. [スコープ] ダイアログで、[保存して次へ] をクリックします。

    8. [テストユーザー] ダイアログで、[保存して次へ] をクリックします。

    9. [概要] ダイアログで、[ダッシュボードに戻る] をクリックします。

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

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

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

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

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

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

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

    5. 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. コンソール または 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. Console または CLI を使用して、バージョニングされたシークレットを作成します。

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

    gcloud iam service-accounts create idp-sql-identity
  5. コンソール または 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. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

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

  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 リソースを削除します。

次のステップ