クロスアカウント保護でユーザーアカウントを保護する

アプリでユーザーがGoogleを使用して自分のアカウントにログインできる場合は、クロスアカウント保護サービスによって提供されるセキュリティイベント通知を聞いて応答することで、これらの共有ユーザーのアカウントのセキュリティを向上させることができます。

これらの通知は、ユーザーのGoogleアカウントに大きな変更があったことを警告します。これは、多くの場合、アプリのアカウントにセキュリティ上の影響を与える可能性があります。たとえば、ユーザーのGoogleアカウントが乗っ取られた場合、メールアカウントの復旧やシングルサインオンの使用を通じて、アプリでユーザーのアカウントが侵害される可能性があります。

このようなイベントの潜在的なリスクを軽減するために、Googleはセキュリティイベントトークンと呼ばれるサービスオブジェクトを送信します。これらのトークンは、セキュリティイベントの種類と発生日時、影響を受けるユーザーの識別子など、ほとんど情報を公開しませんが、これらのトークンを使用して、それに応じて適切なアクションを実行できます。たとえば、ユーザーのGoogleアカウントが侵害された場合、そのユーザーのGoogleログインを一時的に無効にして、アカウント回復メールがユーザーのGmailアドレスに送信されないようにすることができます。

クロスアカウント保護は、OpenIDFoundationで開発されたRISC標準に基づいています。

概要概要

アプリまたはサービスでクロスアカウント保護を使用するには、次のタスクを完了する必要があります。

  1. API Consoleでプロジェクトを設定します。

  2. Googleがセキュリティイベントトークンを送信するイベントレシーバーエンドポイントを作成します。このエンドポイントは、受信したトークンを検証し、選択した方法でセキュリティイベントに応答する責任があります。

  3. エンドポイントをGoogleに登録して、セキュリティイベントトークンの受信を開始します。

前提条件

プロフィール情報またはメールアドレスへのアクセスをサービスに許可したGoogleユーザーのセキュリティイベントトークンのみを受け取ります。 profileまたはemailスコープを要求することにより、この許可を取得しemailGoogleサインインSDKはデフォルトでこれらのスコープを要求しますが、デフォルト設定を使用しない場合、またはGoogleのOpenID Connectエンドポイントに直接アクセスする場合は、これらのスコープの少なくとも1つを要求していることを確認してください。

API Consoleでプロジェクトを設定します

セキュリティイベントトークンの受信を開始する前に、サービスアカウントを作成し、API ConsoleプロジェクトでRISCAPIを有効にする必要があります。アプリでGoogleログインなどのGoogleサービスにアクセスするために使用するのと同じAPI Consoleプロジェクトを使用する必要があります。

サービスアカウントを作成するには:

  1. API ConsoleCredentials pageを開きます。プロンプトが表示されたら、アプリでGoogleサービスにアクセスするために使用するAPI Consoleプロジェクトを選択します。

  2. [資格情報の作成]> [サービスアカウントキー]をクリックします

  3. 編集者の役割で新しいサービスアカウントを作成します。

    JSONキータイプを選択し、[作成]をクリックします。キーが作成されると、サービスアカウントの資格情報を含むJSONファイルをダウンロードします。このファイルは安全な場所に保管してください。ただし、イベントレシーバーエンドポイントからもアクセスできます。

プロジェクトの[クレデンシャル]ページを表示しているときに、Googleログインに使用するクライアントIDもメモしてください。通常、サポートするプラットフォームごとにクライアントIDがあります。次のセクションで説明するように、セキュリティイベントトークンを検証するには、これらのクライアントIDが必要になります。

RISC APIを有効にするには:

  1. API ConsoleのRISCAPIページを開きます。 Googleサービスへのアクセスに使用するプロジェクトがまだ選択されていることを確認してください。

  2. RISC規約を読み、要件を理解していることを確認してください。

    組織が所有するプロジェクトのAPIを有効にする場合は、組織をRISC規約にバインドする権限があることを確認してください。

  3. RISC規約に同意する場合にのみ、[有効にする]をクリックします。

イベントレシーバーエンドポイントを作成する

Googleからセキュリティイベント通知を受信するには、HTTPSPOSTリクエストを処理するHTTPSエンドポイントを作成します。このエンドポイントを登録すると(以下を参照)、Googleはセキュリティイベントトークンと呼ばれる暗号で署名された文字列をエンドポイントに投稿し始めます。セキュリティイベントトークンは、単一のセキュリティ関連イベントに関する情報を含む署名付きJWTです。

エンドポイントで受信するセキュリティイベントトークンごとに、最初にトークンを検証してデコードし、次にサービスに応じてセキュリティイベントを処理します。次のセクションでは、これらのタスクについて説明します。

1.セキュリティイベントトークンをデコードして検証します

セキュリティイベントトークンは特定の種類のJWTであるため、 jwt.ioにリストされているものなどの任意のJWTライブラリを使用して、それらをデコードおよび検証できます。どちらのライブラリを使用する場合でも、トークン検証コードは次のことを行う必要があります。

  1. https://accounts.google.com/.well-known/risc-configurationあるGoogleのRISC構成ドキュメントから、クロスアカウント保護の発行者識別子( issuer )と署名キー証明書URI( jwks_uri )を取得しhttps://accounts.google.com/.well-known/risc-configuration
  2. 選択したJWTライブラリを使用して、セキュリティイベントトークンのヘッダーから署名キーIDを取得します。
  3. Googleの署名鍵証明書ドキュメントから、前の手順で取得した鍵IDを持つ公開鍵を取得します。ドキュメントに探しているIDのキーが含まれていない場合は、セキュリティイベントトークンが無効である可能性が高く、エンドポイントはHTTPエラー400を返す必要があります。
  4. 選択したJWTライブラリを使用して、以下を確認します。
    • セキュリティイベントトークンは、前の手順で取得した公開鍵を使用して署名されます。
    • トークンのaudクレームは、アプリのクライアントIDの1つです。
    • トークンのissクレームは、RISCディスカバリドキュメントから取得した発行者識別子と一致します。セキュリティイベントトークンは履歴イベントを表すため、有効期限が切れないため、トークンの有効期限( exp )を確認する必要がないことに注意してください。

例えば:

Java

java-jwtjwks-rsa-javaの使用

public DecodedJWT validateSecurityEventToken(String token) {
    DecodedJWT jwt = null;
    try {
        // In a real implementation, get these values from
        // https://accounts.google.com/.well-known/risc-configuration
        String issuer = "accounts.google.com";
        String jwksUri = "https://www.googleapis.com/oauth2/v3/certs";

        // Get the ID of the key used to sign the token.
        DecodedJWT unverifiedJwt = JWT.decode(token);
        String keyId = unverifiedJwt.getKeyId();

        // Get the public key from Google.
        JwkProvider googleCerts = new UrlJwkProvider(new URL(jwksUri), null, null);
        PublicKey publicKey = googleCerts.get(keyId).getPublicKey();

        // Verify and decode the token.
        Algorithm rsa = Algorithm.RSA256((RSAPublicKey) publicKey, null);
        JWTVerifier verifier = JWT.require(rsa)
                .withIssuer(issuer)
                // Get your apps' client IDs from the API console:
                // https://console.developers.google.com/apis/credentials?project=_
                .withAudience("123456789-abcedfgh.apps.googleusercontent.com",
                              "123456789-ijklmnop.apps.googleusercontent.com",
                              "123456789-qrstuvwx.apps.googleusercontent.com")
                .acceptLeeway(Long.MAX_VALUE)  // Don't check for expiration.
                .build();
        jwt = verifier.verify(token);
    } catch (JwkException e) {
        // Key not found. Return HTTP 400.
    } catch (InvalidClaimException e) {

    } catch (JWTDecodeException exception) {
        // Malformed token. Return HTTP 400.
    } catch (MalformedURLException e) {
        // Invalid JWKS URI.
    }
    return jwt;
}

Python

import json
import jwt       # pip install pyjwt
import requests  # pip install requests

def validate_security_token(token, client_ids):
    # Get Google's RISC configuration.
    risc_config_uri = 'https://accounts.google.com/.well-known/risc-configuration'
    risc_config = requests.get(risc_config_uri).json()

    # Get the public key used to sign the token.
    google_certs = requests.get(risc_config['jwks_uri']).json()
    jwt_header = jwt.get_unverified_header(token)
    key_id = jwt_header['kid']
    public_key = None
    for key in google_certs['keys']:
        if key['kid'] == key_id:
            public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key))
    if not public_key:
        raise Exception('Public key certificate not found.')
        # In this situation, return HTTP 400

    # Decode the token, validating its signature, audience, and issuer.
    try:
        token_data = jwt.decode(token, public_key, algorithms='RS256',
                                options={'verify_exp': False},
                                audience=client_ids, issuer=risc_config['issuer'])
    except:
        raise
        # Validation failed. Return HTTP 400.
    return token_data

# Get your apps' client IDs from the API console:
# https://console.developers.google.com/apis/credentials?project=_
client_ids = ['123456789-abcedfgh.apps.googleusercontent.com',
              '123456789-ijklmnop.apps.googleusercontent.com',
              '123456789-qrstuvwx.apps.googleusercontent.com']
token_data = validate_security_token(token, client_ids)

トークンが有効で、正常にデコードされた場合は、HTTPステータス202を返します。次に、トークンによって示されるセキュリティイベントを処理します。

2.セキュリティイベントを処理します

デコードすると、セキュリティイベントトークンは次の例のようになります。

{
  "iss": "https://accounts.google.com/",
  "aud": "123456789-abcedfgh.apps.googleusercontent.com",
  "iat": 1508184845,
  "jti": "756E69717565206964656E746966696572",
  "events": {
    "https://schemas.openid.net/secevent/risc/event-type/account-disabled": {
      "subject": {
        "subject_type": "iss-sub",
        "iss": "https://accounts.google.com/",
        "sub": "7375626A656374"
      },
      "reason": "hijacking"
    }
  }
}

issおよびaudクレームは、トークンの発行者(Google)とトークンの意図された受信者(サービス)を示します。前の手順でこれらの主張を確認しました。

jtiクレームは、単一のセキュリティイベントを識別する文字列であり、ストリームに固有です。この識別子を使用して、受信したセキュリティイベントを追跡できます。

eventsクレームには、トークンが表すセキュリティイベントに関する情報が含まれています。このクレームは、イベントタイプ識別子から、このイベントが関係するユーザーを指定するsubjectクレーム、および利用可能なイベントに関する追加の詳細へのマッピングです。

subjectクレームは、特定のユーザーをそのユーザーの一意のGoogleアカウントID( sub )で識別します。このIDは、 Googleサインインによって生成されたIDトークンに含まれている識別子と同じです。ときsubject_type請求のですid_token_claims 、それも含まれる場合がありますemailユーザーの電子メールアドレスを持つフィールドを。

eventsクレームの情報を使用して、指定したユーザーのアカウントのイベントタイプに対して適切なアクションを実行します。

サポートされているイベントタイプ

クロスアカウント保護は、次のタイプのセキュリティイベントをサポートします。

イベントタイプ属性応答する方法
https://schemas.openid.net/secevent/risc/event-type/sessions-revoked必須:現在開いているセッションを終了して、ユーザーのアカウントを再保護します。
https://schemas.openid.net/secevent/oauth/event-type/tokens-revoked

必須:トークンがGoogleサインイン用である場合は、現在開いているセッションを終了します。さらに、別のサインイン方法を設定するようにユーザーに提案することもできます。

推奨:トークンが他のGoogle APIにアクセスするためのものである場合は、保存したユーザーのOAuthトークンをすべて削除してください。

https://schemas.openid.net/secevent/risc/event-type/account-disabled reason=hijacking
reason=bulk-account

必須:アカウントが無効にされた理由がhijackingであった場合は、現在開いているセッションを終了して、ユーザーのアカウントを再度保護します。

提案:アカウントが無効にされた理由がbulk-accountアカウントであった場合は、サービスでのユーザーのアクティビティを分析し、適切なフォローアップアクションを決定します。

提案:理由が提供されていない場合は、ユーザーのGoogleログインを無効にし、ユーザーのGoogleアカウントに関連付けられているメールアドレス(通常はGmailアカウント)を使用してアカウントの回復を無効にします。別のサインイン方法をユーザーに提供します。

https://schemas.openid.net/secevent/risc/event-type/account-enabled推奨:ユーザーのGoogleログインを再度有効にし、ユーザーのGoogleアカウントのメールアドレスを使用してアカウントの復元を再度有効にします。
https://schemas.openid.net/secevent/risc/event-type/account-purged推奨:ユーザーのアカウントを削除するか、別のサインイン方法を提供してください。
https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required提案:サービスでの疑わしいアクティビティに注意し、適切なアクションを実行してください。
https://schemas.openid.net/secevent/risc/event-type/verification状態= state推奨:テストトークンを受信したことをログに記録します。

重複したイベントと見逃したイベント

Cross-Account Protectionは、配信されていないと思われるイベントの再配信を試みます。そのため、同じイベントを複数回受信する場合があります。これにより、ユーザーに不便をかけるアクションが繰り返される可能性がある場合は、 jtiクレーム(イベントの一意の識別子)を使用してイベントを重複jtiすることを検討してください。重複排除データフローの実行に役立つ可能性のあるGoogleCloudDataflowなどの外部ツールがあります。

イベントは限られた再試行で配信されるため、レシーバーが長期間ダウンしていると、一部のイベントを永久に見逃す可能性があることに注意してください。

受信機を登録する

セキュリティイベントの受信を開始するには、RISCAPIを使用してレシーバエンドポイントを登録します。 RISC APIの呼び出しには、認証トークンを伴う必要があります。

アプリのユーザーに対してのみセキュリティイベントを受信するため、以下で説明する手順の前提条件として、GCPプロジェクトでOAuth同意画面を構成する必要があります。

1.認証トークンを生成します

RISC APIの認証トークンを生成するには、次のクレームを含むJWTを作成します。

{
  "iss": SERVICE_ACCOUNT_EMAIL,
  "sub": SERVICE_ACCOUNT_EMAIL,
  "aud": "https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService",
  "iat": CURRENT_TIME,
  "exp": CURRENT_TIME + 3600
}

サービスアカウントの秘密鍵を使用してJWTに署名します。秘密鍵は、サービスアカウントキーの作成時にダウンロードしたJSONファイルにあります。

例えば:

Java

java-jwtGoogleの認証ライブラリを使用する

public static String makeBearerToken() {
    String token = null;
    try {
        // Get signing key and client email address.
        FileInputStream is = new FileInputStream("your-service-account-credentials.json");
        ServiceAccountCredentials credentials =
               (ServiceAccountCredentials) GoogleCredentials.fromStream(is);
        PrivateKey privateKey = credentials.getPrivateKey();
        String keyId = credentials.getPrivateKeyId();
        String clientEmail = credentials.getClientEmail();

        // Token must expire in exactly one hour.
        Date issuedAt = new Date();
        Date expiresAt = new Date(issuedAt.getTime() + 3600000);

        // Create signed token.
        Algorithm rsaKey = Algorithm.RSA256(null, (RSAPrivateKey) privateKey);
        token = JWT.create()
                .withIssuer(clientEmail)
                .withSubject(clientEmail)
                .withAudience("https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService")
                .withIssuedAt(issuedAt)
                .withExpiresAt(expiresAt)
                .withKeyId(keyId)
                .sign(rsaKey);
    } catch (ClassCastException e) {
        // Credentials file doesn't contain a service account key.
    } catch (IOException e) {
        // Credentials file couldn't be loaded.
    }
    return token;
}

Python

import json
import time

import jwt  # pip install pyjwt

def make_bearer_token(credentials_file):
    with open(credentials_file) as service_json:
        service_account = json.load(service_json)
        issuer = service_account['client_email']
        subject = service_account['client_email']
        private_key_id = service_account['private_key_id']
        private_key = service_account['private_key']
    issued_at = int(time.time())
    expires_at = issued_at + 3600
    payload = {'iss': issuer,
               'sub': subject,
               'aud': 'https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService',
               'iat': issued_at,
               'exp': expires_at}
    encoded = jwt.encode(payload, private_key, algorithm='RS256',
                         headers={'kid': private_key_id})
    return encoded

auth_token = make_bearer_token('your-service-account-credentials.json')

この認証トークンを使用して、RISCAPI呼び出しを1時間行うことができます。トークンの有効期限が切れたら、新しいトークンを生成して、引き続きRISCAPI呼び出しを行います。

2.RISCストリーム構成APIを呼び出します

承認トークンを取得したので、RISC APIを使用して、レシーバーエンドポイントの登録など、プロジェクトのセキュリティイベントストリームを構成できます。

これを行うには、 https://risc.googleapis.com/v1beta/stream:update updateにHTTPS POSTリクエストを送信し、受信者のエンドポイントと関心のあるセキュリティイベント種類を指定します。

POST /v1beta/stream:update HTTP/1.1
Host: risc.googleapis.com
Authorization: Bearer AUTH_TOKEN

{
  "delivery": {
    "delivery_method":
      "https://schemas.openid.net/secevent/risc/delivery-method/push",
    "url": RECEIVER_ENDPOINT
  },
  "events_requested": [
    SECURITY_EVENT_TYPES
  ]
}

例えば:

Java

public static void configureEventStream(final String receiverEndpoint,
                                        final List<String> eventsRequested,
                                        String authToken) throws IOException {
    ObjectMapper jsonMapper = new ObjectMapper();
    String streamConfig = jsonMapper.writeValueAsString(new Object() {
        public Object delivery = new Object() {
            public String delivery_method =
                    "https://schemas.openid.net/secevent/risc/delivery-method/push";
            public String url = receiverEndpoint;
        };
        public List<String> events_requested = eventsRequested;
    });

    HttpPost updateRequest = new HttpPost("https://risc.googleapis.com/v1beta/stream:update");
    updateRequest.addHeader("Content-Type", "application/json");
    updateRequest.addHeader("Authorization", "Bearer " + authToken);
    updateRequest.setEntity(new StringEntity(streamConfig));

    HttpResponse updateResponse = new DefaultHttpClient().execute(updateRequest);
    Header[] responseContentTypeHeaders = updateResponse.getHeaders("Content-Type");
    StatusLine responseStatus = updateResponse.getStatusLine();
    int statusCode = responseStatus.getStatusCode();
    HttpEntity entity = updateResponse.getEntity();
    // Now handle response
}

// ...

configureEventStream(
        "https://your-service.example.com/security-event-receiver",
        Arrays.asList(
                "https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required",
                "https://schemas.openid.net/secevent/risc/event-type/account-disabled"),
        authToken);

Python

import requests

def configure_event_stream(auth_token, receiver_endpoint, events_requested):
    stream_update_endpoint = 'https://risc.googleapis.com/v1beta/stream:update'
    headers = {'Authorization': 'Bearer {}'.format(auth_token)}
    stream_cfg = {'delivery': {'delivery_method': 'https://schemas.openid.net/secevent/risc/delivery-method/push',
                               'url': receiver_endpoint},
                  'events_requested': events_requested}
    response = requests.post(stream_update_endpoint, json=stream_cfg, headers=headers)
    response.raise_for_status()  # Raise exception for unsuccessful requests

configure_event_stream(auth_token, 'https://your-service.example.com/security-event-receiver',
                       ['https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required',
                        'https://schemas.openid.net/secevent/risc/event-type/account-disabled'])

リクエストがHTTP200を返す場合、イベントストリームは正常に構成されており、レシーバエンドポイントはセキュリティイベントトークンの受信を開始する必要があります。次のセクションでは、ストリーム構成とエンドポイントをテストして、すべてが正しく連携していることを確認する方法について説明します。

現在のストリーム構成を取得して更新します

将来、ストリーム構成を変更したい場合は、 https://risc.googleapis.com/v1beta/streamに承認されたGETリクエストを送信して、現在のストリーム構成を取得し、応答本文を変更することで変更できます。 、次に、上記のように、変更した構成をhttps://risc.googleapis.com/v1beta/stream:update POSTします。

イベントストリームを停止して再開します

Googleからのイベントストリームを停止する必要がある場合は、リクエスト本文に{ "status": "disabled" }して、 https://risc.googleapis.com/v1beta/stream/status:update承認済みのPOSTリクエストを送信してhttps://risc.googleapis.com/v1beta/stream/status:update 。ストリームが非アクティブ化されている間、Googleはイベントをエンドポイントに送信せず、セキュリティイベントが発生したときにバッファリングしません。イベントストリームを再度{ "status": "enabled" }するには、同じエンドポイントにPOST { "status": "enabled" }を送信します。

3.オプション:ストリーム構成をテストします

イベントストリームを介して検証トークンを送信することにより、ストリーム構成とレシーバーエンドポイントが正しく連携していることを確認できます。このトークンには、トークンがエンドポイントで受信されたことを確認するために使用できる一意の文字列を含めることができます。

確認トークンをリクエストするには、 https://risc.googleapis.com/v1beta/stream:verify verifyに承認済みのHTTPSPOSTリクエストを送信しhttps://risc.googleapis.com/v1beta/stream:verify 。リクエストの本文で、識別文字列を指定します。

{
  "state": "ANYTHING"
}

例えば:

Java

public static void testEventStream(final String stateString,
                                   String authToken) throws IOException {
    ObjectMapper jsonMapper = new ObjectMapper();
    String json = jsonMapper.writeValueAsString(new Object() {
        public String state = stateString;
    });

    HttpPost updateRequest = new HttpPost("https://risc.googleapis.com/v1beta/stream:verify");
    updateRequest.addHeader("Content-Type", "application/json");
    updateRequest.addHeader("Authorization", "Bearer " + authToken);
    updateRequest.setEntity(new StringEntity(json));

    HttpResponse updateResponse = new DefaultHttpClient().execute(updateRequest);
    Header[] responseContentTypeHeaders = updateResponse.getHeaders("Content-Type");
    StatusLine responseStatus = updateResponse.getStatusLine();
    int statusCode = responseStatus.getStatusCode();
    HttpEntity entity = updateResponse.getEntity();
    // Now handle response
}

// ...

testEventStream("Test token requested at " + new Date().toString(), authToken);

Python

import requests
import time

def test_event_stream(auth_token, nonce):
    stream_verify_endpoint = 'https://risc.googleapis.com/v1beta/stream:verify'
    headers = {'Authorization': 'Bearer {}'.format(auth_token)}
    state = {'state': nonce}
    response = requests.post(stream_verify_endpoint, json=state, headers=headers)
    response.raise_for_status()  # Raise exception for unsuccessful requests

test_event_stream(auth_token, 'Test token requested at {}'.format(time.ctime()))

リクエストが成功すると、登録したエンドポイントに検証トークンが送信されます。次に、たとえば、エンドポイントが検証トークンをログに記録するだけで処理する場合、ログを調べてトークンが受信されたことを確認できます。

エラーコードリファレンス

次のエラーは、RISCAPIによって返される可能性があります。

エラーコードエラーメッセージ推奨されるアクション
400ストリーム構成には、 $ fieldnameフィールドが含まれている必要があります。 https://risc.googleapis.com/v1beta/stream:updateエンドポイントへのリクエストが無効であるか、解析できません。リクエストに$ fieldnameを含めてください。
401許可されていません。承認に失敗しました。リクエストに認証トークンを添付し、トークンが有効で有効期限が切れていないことを確認してください。
403配信エンドポイントはHTTPSURLである必要があります。配信エンドポイント(つまり、RISCイベントの配信先となるエンドポイント)はHTTPSである必要があります。 RISCイベントをHTTPURLに送信しません。
403既存のストリーム構成には、RISCの仕様に準拠した配信方法がありません。 GoogleCloudプロジェクトにはすでにRISC構成が必要です。 Firebaseを使用していて、Googleサインインが有効になっている場合、FirebaseがプロジェクトのRISCを管理します。カスタム構成を作成することはできません。 FirebaseプロジェクトでGoogleログインを使用していない場合は、無効にしてから、1時間後にもう一度更新してみてください。
403プロジェクトが見つかりませんでした。正しいプロジェクトに正しいサービスアカウントを使用していることを確認してください。削除されたプロジェクトに関連付けられたサービスアカウントを使用している可能性があります。 プロジェクトに関連付けられているすべてのサービスアカウントを表示する方法を学びます
403サービスアカウントには、プロジェクトでの編集者権限が必要です。プロジェクトのGoogleCloud Platformコンソールに移動し、次の手順に従って、通話の編集者/所有者にプロジェクトへのアクセス許可を与えるサービスアカウントを付与します。
403ストリーム管理APIは、サービスアカウントからのみ呼び出す必要があります。サービスアカウントを使用してGoogleAPIを呼び出す方法の詳細は次のとおりです
403配信エンドポイントは、プロジェクトのどのドメインにも属していません。すべてのプロジェクトには、承認されたドメインのセットがあります。配信エンドポイント(つまり、RISCイベントが配信されると予想されるエンドポイント)がそれらのいずれかでホストされていない場合は、エンドポイントのドメインをそのセットに追加する必要があります。
403このAPIを使用するには、プロジェクトに少なくとも1つのOAuthクライアントが構成されている必要があります。 RISCは、 Googleログインをサポートするアプリを作成した場合にのみ機能します。この接続にはOAuthクライアントが必要です。プロジェクトにOAuthクライアントがない場合は、RISCが役に立たない可能性があります。 GoogleによるAPIでのOAuthの使用の詳細をご覧ください。
403

サポートされていないステータス。

ステータスが無効です。

現時点では、ストリームステータス「 enabled 」と「 disabled 」のみをサポートしています。
404

プロジェクトにはRISC構成がありません。

プロジェクトには既存のRISC構成がなく、ステータスを更新できません。

https://risc.googleapis.com/v1beta/stream:updateエンドポイントを呼び出して、新しいストリーム構成を作成します。
4XX / 5XXステータスを更新できません。詳細については、詳細なエラーメッセージを確認してください。

アクセストークンスコープ

RISC APIへの認証にアクセストークンを使用することにした場合、アプリケーションが要求する必要のあるスコープは次のとおりです。

終点範囲
https://risc.googleapis.com/v1beta/stream/status https://www.googleapis.com/auth/risc.status.readonlyまたはhttps://www.googleapis.com/auth/risc.status.readwrite
https://risc.googleapis.com/v1beta/stream/status:update https://www.googleapis.com/auth/risc.status.readwrite
https://risc.googleapis.com/v1beta/stream https://www.googleapis.com/auth/risc.configuration.readonlyまたはhttps://www.googleapis.com/auth/risc.configuration.readwrite
https://risc.googleapis.com/v1beta/stream:update https://www.googleapis.com/auth/risc.configuration.readwrite
https://risc.googleapis.com/v1beta/stream:verify https://www.googleapis.com/auth/risc.verify

助けが必要?

まず、エラーコードリファレンスセクションを確認してください。それでも質問がある場合は、 #SecEventsタグを付けてStackOverflowに投稿してください