透過跨帳戶防護功能保護使用者帳戶

如果應用程式允許使用者透過 Google 登入帳戶,您可以監聽並回應跨帳戶防護服務提供的安全性事件通知,提升這些共用使用者帳戶的安全性。

這些通知會提醒您使用者 Google 帳戶的重大變更,這通常也會對他們在您應用程式中的帳戶造成安全影響。舉例來說,如果使用者的 Google 帳戶遭入侵,他們在您應用程式中的帳戶,也有可能透過電子郵件帳戶救援或單一登入機制遭到破解。

為協助您降低這類事件的潛在風險,Google 會傳送名為安全事件權杖的服務物件。這些權杖只會揭露極少資訊,包括安全事件類型、發生時間和受影響的使用者 ID,但您可以使用這些權杖採取適當的因應措施。舉例來說,如果使用者的 Google 帳戶遭入侵,您可以暫時為該使用者停用「使用 Google 帳戶登入」,並防止系統將帳戶救援電子郵件傳送至使用者的 Gmail 地址。

跨帳戶防護功能是以 OpenID 基金會開發的 RISC 標準為基礎。

總覽

如要在應用程式或服務中使用跨帳戶防護功能,請完成下列工作:

  1. 在 API Console中設定專案。

  2. 建立事件接收器端點,Google 會將安全事件權杖傳送至該端點。這個端點負責驗證收到的權杖,然後以您選擇的任何方式回應安全事件。

  3. 向 Google 註冊端點,開始接收安全性事件權杖。

修課條件

只有在 Google 使用者授予服務存取其個人資料或電子郵件地址的權限時,您才會收到安全事件權杖。如要取得這項權限,請要求 profileemail 範圍。新版使用 Google 帳戶登入或舊版 Google 登入 SDK 預設會要求這些範圍,但如果您未使用預設設定,或直接存取 Google 的 OpenID Connect 端點,請務必要求至少一個這類範圍。

在 API Console中設定專案。

您必須先建立服務帳戶,並在API Console 專案中啟用 RISC API,才能開始接收安全事件權杖。您必須使用與應用程式中存取 Google 服務 (例如 Google 登入) 時相同的API Console 專案。

如何建立服務帳戶:

  1. 開啟 API Console Credentials page。系統顯示提示訊息時,請選擇您在應用程式中用來存取 Google 服務的專案。API Console

  2. 依序按一下「建立憑證」>「服務帳戶」

  3. 按照這些操作說明,建立具備 RISC 設定管理員角色 (roles/riscconfigs.admin) 的新服務帳戶。

  4. 為新建立的服務帳戶建立金鑰。選擇 JSON 金鑰類型,然後按一下「建立」。建立金鑰後,您會下載包含服務帳戶憑證的 JSON 檔案。請將這個檔案儲存在安全的位置,但活動接收端點也必須能夠存取。

在專案的「憑證」頁面中,請記下您用於「使用 Google 帳戶登入」或「Google 登入 (舊版)」的用戶端 ID。通常,您支援的每個平台都有一個用戶端 ID。您需要這些用戶端 ID 來驗證安全性事件權杖,詳情請見下一節。

如要啟用 RISC API,請按照下列步驟操作:

  1. 在API Console中開啟 RISC API 頁面。確認您用來存取 Google 服務的專案仍處於選取狀態。

  2. 詳閱 RISC 條款,確保瞭解相關規定。

    如果您要為機構擁有的專案啟用 API,請確認您有權將機構繫結至 RISC 條款。

  3. 只有在同意 RISC 條款時,才點選「啟用」

建立事件接收器端點

如要接收 Google 的安全性事件通知,請建立可處理 HTTPS POST 要求的 HTTPS 端點。註冊這個端點後 (請參閱下文),Google 就會開始將稱為安全事件權杖的加密簽署字串,發布至該端點。安全性事件權杖是已簽署的 JWT,內含單一安全性相關事件的資訊。

針對端點收到的每個安全事件權杖,請先驗證並解碼權杖,然後視服務需求處理安全事件。請務必先驗證事件權杖,再進行解碼,以免遭到不肖人士的惡意攻擊。以下各節說明這些工作:

1. 解碼及驗證安全性事件權杖

由於安全事件權杖是特定類型的 JWT,因此您可以使用任何 JWT 程式庫 (例如 jwt.io 上列出的程式庫) 解碼及驗證權杖。無論使用哪個程式庫,權杖驗證碼都必須執行下列操作:

  1. 從 Google 的 RISC 設定文件中取得跨帳戶保護發行者 ID (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 之一。
    • 權杖的 iss 聲明與您從 RISC 探索文件中取得的核發者 ID 相符。請注意,您不需要驗證權杖的到期時間 (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"
    }
  }
}

issaud 聲明會指出權杖的簽發者 (Google) 和權杖的預期接收者 (您的服務)。您已在先前的步驟中驗證這些聲明。

jti 聲明是識別單一安全事件的字串,且串流專屬。您可以使用這個 ID,追蹤已收到的安全性事件。

events 聲明包含權杖代表的安全性事件相關資訊。這項聲明是從事件類型 ID 對應至 subject 聲明,後者會指定事件所涉使用者,以及事件可能提供的任何其他詳細資料。

subject 聲明會使用使用者的專屬 Google 帳戶 ID (sub) 識別特定使用者。這個 Google 帳戶 ID 與新版「使用 Google 帳戶登入」JavaScriptHTML 程式庫、舊版 Google 登入程式庫或 OpenID Connect 所核發 JWT ID 權杖中包含的 ID (sub) 相同。如果聲明的 subject_typeid_token_claims,也可能包含 email 欄位,內含使用者的電子郵件地址。

使用 events 聲明中的資訊,對指定使用者帳戶中的事件類型採取適當行動。

OAuth 權杖 ID

如果是與個別權杖相關的 OAuth 事件,權杖主體 ID 類型會包含下列欄位:

  • token_type:僅支援 refresh_token

  • token_identifier_alg:請參閱下表,瞭解可能的值。

  • token:請參閱下表。

token_identifier_alg 權杖
prefix 權杖的前 16 個字元。
hash_base64_sha512_sha512 使用 SHA-512 進行雙重雜湊處理的權杖。

如果整合這些事件,建議您根據這些可能的值為權杖建立索引,確保收到事件時能快速比對。

支援的事件類型

跨帳戶防護功能支援下列類型的安全性事件:

事件類型 屬性 如何回覆
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/oauth/event-type/token-revoked 如需權杖 ID,請參閱「OAuth 權杖 ID」一節

必要:如果您儲存對應的重新整理權杖,請刪除該權杖,並要求使用者在下次需要存取權杖時重新同意。

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-credential-change-required 建議:留意服務中的可疑活動,並採取適當行動。
https://schemas.openid.net/secevent/risc/event-type/verification state=state 建議:記錄已收到測試權杖。

重複和遺漏的事件

跨帳戶防護功能會嘗試重新傳送系統認為尚未傳送的事件。因此,您有時可能會多次收到相同事件。如果這可能導致重複動作,造成使用者不便,請考慮使用 jti 聲明 (事件的專屬 ID) 來移除重複事件。您可以使用 Google Cloud Dataflow 等外部工具,執行重複資料刪除資料流。

請注意,系統只會重試傳送事件幾次,因此如果接收端長時間無法運作,您可能會永久錯過部分事件。

註冊接收端

如要開始接收安全性事件,請使用 RISC API 註冊接收端點。呼叫 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')

這個授權權杖可用於發出 RISC API 呼叫,效期為一小時。權杖到期後,請產生新權杖,繼續發出 RISC API 呼叫。

2. 呼叫 RISC 串流設定 API

取得授權權杖後,您就可以使用 RISC API 設定專案的安全事件串流,包括註冊接收端端點。

如要這麼做,請向 https://risc.googleapis.com/v1beta/stream: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'])

如果要求傳回 HTTP 200,表示事件串流已成功設定,接收端點應會開始接收安全事件權杖。下一節說明如何測試串流設定和端點,確認一切運作正常。

取得及更新目前的串流設定

日後如要修改串流設定,可以對 https://risc.googleapis.com/v1beta/stream 提出授權 GET 要求,取得目前的串流設定,然後修改回應主體,再如上所述將修改後的設定 POST 回 https://risc.googleapis.com/v1beta/stream:update

停止及繼續事件串流

如要停止接收 Google 的事件串流,請向 https://risc.googleapis.com/v1beta/stream/status:update 傳送授權的 POST 要求,並在要求主體中加入 { "status": "disabled" }。停用串流後,Google 不會將事件傳送至端點,也不會在發生安全性事件時緩衝處理。如要重新啟用事件串流,請將 { "status": "enabled" } POST 至相同端點。

3. 選用:測試串流設定

您可以透過事件串流傳送驗證權杖,確認串流設定和接收端點是否能正常運作。這個權杖可包含專屬字串,您可用於驗證權杖是否已在端點接收。如要使用這個流程,請務必在註冊接收器時,訂閱 https://schemas.openid.net/secevent/risc/event-type/verification 事件類型。

如要要求驗證權杖,請向 https://risc.googleapis.com/v1beta/stream:verify發出授權的 HTTPS POST 要求。在要求主體中,指定一些識別字串:

{
  "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()))

如果要求成功,驗證權杖會傳送至您註冊的端點。舉例來說,如果端點處理驗證權杖的方式只是記錄權杖,您可以檢查記錄,確認權杖是否已收到。

錯誤代碼參考資料

RISC API 可能會傳回下列錯誤:

錯誤代碼 錯誤訊息 建議動作
400 串流設定必須包含 $fieldname 欄位。 您對 https://risc.googleapis.com/v1beta/stream:update 端點的要求無效或無法剖析。請在要求中加入 $fieldname
401 未經授權。 無法提供授權,請務必在要求中附上 授權權杖,並確認權杖有效且尚未過期。
403 傳送端點必須是 HTTPS 網址。 傳送端點 (也就是您預期 RISC 事件傳送到的端點) 必須是 HTTPS。我們不會將 RISC 事件傳送至 HTTP 網址。
403 現有串流設定不具備 RISC 的規格相容放送方式。 您的 Google Cloud 專案必須已設定 RISC。如果您使用 Firebase 並已啟用 Google 登入,Firebase 會為您的專案管理 RISC,您無法建立自訂設定。如果 Firebase 專案未使用 Google 登入,請停用這項功能,然後在一小時後再嘗試更新。
403 找不到專案。 請確認您為正確的專案使用正確的服務帳戶。您可能正在使用與已刪除專案相關聯的服務帳戶。瞭解如何 查看與專案相關聯的所有服務帳戶
403 服務帳戶必須有權存取 RISC 設定 前往專案的 API Console ,然後按照這些操作說明,將「RISC 設定管理員」角色 (roles/riscconfigs.admin) 指派給呼叫專案的服務帳戶。
403 串流管理 API 只能由服務帳戶呼叫。 如要進一步瞭解如何使用服務帳戶呼叫 Google API,請參閱這篇文章
403 傳送端點不屬於專案的任何網域。 每個專案都有一組授權網域。如果您的傳送端點 (即您預期 RISC 事件傳送至的端點) 並非託管於上述任一網域,請務必將端點網域加入該組。
403 如要使用這項 API,專案必須至少設定一個 OAuth 用戶端。 您必須建構支援 Google 登入的應用程式,RISC 才會發揮作用。這項連結需要 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.readonlyhttps://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.readonlyhttps://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

需要協助嗎?

首先,請查看錯誤代碼參考資料部分。如果仍有疑問,請前往 Stack Overflow 提問並加上 #SecEvents 標記。