หากแอปของคุณอนุญาตให้ผู้ใช้ลงชื่อเข้าใช้บัญชีของตนโดยใช้ Google คุณจะเพิ่มความปลอดภัยของบัญชีผู้ใช้ที่แชร์เหล่านี้ได้โดยการรับฟังและตอบสนองต่อการแจ้งเตือนเหตุการณ์ด้านความปลอดภัยที่บริการการป้องกันแบบครอบคลุมหลายบริการมอบให้
การแจ้งเตือนเหล่านี้จะแจ้งให้คุณทราบถึงการเปลี่ยนแปลงที่สำคัญในบัญชี Google ของผู้ใช้ ซึ่งมักจะส่งผลต่อความปลอดภัยของบัญชีที่ผู้ใช้มีกับแอปของคุณด้วย ตัวอย่างเช่น หากบัญชี Google ของผู้ใช้ถูกลักลอบใช้งาน ก็อาจทำให้บัญชีของผู้ใช้ในแอปของคุณถูกบุกรุกได้ผ่านการกู้คืนบัญชีอีเมลหรือการใช้การลงชื่อเพียงครั้งเดียว
Google จะส่งออบเจ็กต์บริการที่เรียกว่าโทเค็นเหตุการณ์ด้านความปลอดภัยให้คุณเพื่อช่วยลดความเสี่ยงที่อาจเกิดขึ้นจากเหตุการณ์ดังกล่าว โทเค็นเหล่านี้จะแสดงข้อมูลเพียงเล็กน้อย เท่านั้น ซึ่งก็คือประเภทของเหตุการณ์ด้านความปลอดภัยและเวลาที่เกิดเหตุการณ์ รวมถึง ตัวระบุของผู้ใช้ที่ได้รับผลกระทบ แต่คุณสามารถใช้โทเค็นเหล่านี้เพื่อดำเนินการ ที่เหมาะสมเพื่อตอบสนองได้ เช่น หากบัญชี Google ของผู้ใช้ถูกบุกรุก คุณสามารถปิดใช้การลงชื่อเข้าใช้ด้วย Google สำหรับผู้ใช้รายนั้นชั่วคราวและป้องกันไม่ให้ระบบส่งอีเมลการกู้คืนบัญชีไปยังอีเมล Gmail ของผู้ใช้
การป้องกันแบบครอบคลุมหลายบริการอิงตามมาตรฐาน RISC ที่พัฒนาขึ้นใน OpenID Foundation
ภาพรวม
หากต้องการใช้การป้องกันแบบครอบคลุมหลายบริการกับแอปหรือบริการ คุณต้องทําตาม งานต่อไปนี้
- สร้างโปรเจ็กต์ใน 
- สร้างปลายทางตัวรับเหตุการณ์ ซึ่ง Google จะส่งโทเค็นเหตุการณ์ด้านความปลอดภัย ไปยังปลายทางดังกล่าว โดยปลายทางนี้มีหน้าที่ตรวจสอบโทเค็นที่ได้รับ แล้วตอบสนองต่อเหตุการณ์ด้านความปลอดภัยในรูปแบบที่คุณเลือก 
- ลงทะเบียนอุปกรณ์ปลายทางกับ Google เพื่อเริ่มรับโทเค็นเหตุการณ์ด้านความปลอดภัย 
วิชาบังคับก่อน
คุณจะได้รับโทเค็นเหตุการณ์ด้านความปลอดภัยสำหรับผู้ใช้ Google ที่ให้สิทธิ์บริการของคุณ
ในการเข้าถึงข้อมูลโปรไฟล์หรืออีเมลเท่านั้น คุณ
จะได้รับสิทธิ์นี้โดยการขอขอบเขต profile หรือ email SDK ลงชื่อเข้าใช้ด้วย Google เวอร์ชันใหม่กว่าหรือ SDK การลงชื่อเข้าใช้ด้วย Google เวอร์ชันเดิมจะขอขอบเขตเหล่านี้โดยค่าเริ่มต้น แต่หากคุณไม่ได้ใช้การตั้งค่าเริ่มต้น หรือหากเข้าถึงปลายทาง OpenID
Connect ของ Google โดยตรง โปรดตรวจสอบว่าคุณขอขอบเขตเหล่านี้อย่างน้อย 1 รายการ
ตั้งค่าโปรเจ็กต์ใน
ก่อนที่จะเริ่มรับโทเค็นเหตุการณ์ความปลอดภัยได้ คุณต้องสร้างบัญชีบริการและเปิดใช้ RISC API ใน โปรเจ็กต์ คุณต้องใช้ โปรเจ็กต์เดียวกันกับที่ใช้เพื่อเข้าถึง บริการของ Google เช่น การลงชื่อเข้าใช้ด้วย Google ในแอป
วิธีสร้างบัญชีบริการ
- เปิด เมื่อได้รับข้อความแจ้ง ให้เลือก โปรเจ็กต์ที่คุณใช้เพื่อเข้าถึงบริการของ Google ในแอป 
- คลิกสร้างข้อมูลเข้าสู่ระบบ > บัญชีบริการ 
- สร้างบัญชีบริการใหม่ที่มีบทบาทผู้ดูแลระบบการกำหนดค่า RISC ( - roles/riscconfigs.admin) โดยทำตามวิธีการเหล่านี้
- สร้างคีย์สำหรับบัญชีบริการที่สร้างขึ้นใหม่ เลือกประเภทคีย์ JSON แล้วคลิกสร้าง เมื่อสร้างคีย์แล้ว คุณจะดาวน์โหลดไฟล์ JSON ที่มีข้อมูลเข้าสู่ระบบของบัญชีบริการ เก็บไฟล์นี้ไว้ในที่ที่ปลอดภัย แต่ก็ต้องเข้าถึงได้สำหรับ ปลายทางตัวรับเหตุการณ์ 
ขณะอยู่ที่หน้าข้อมูลเข้าสู่ระบบของโปรเจ็กต์ ให้จดบันทึกรหัสไคลเอ็นต์ที่คุณใช้สำหรับลงชื่อเข้าใช้ด้วย Google หรือการลงชื่อเข้าใช้ Google (เดิม) ด้วย โดยปกติแล้ว คุณจะมีรหัสไคลเอ็นต์สำหรับแต่ละ แพลตฟอร์มที่คุณรองรับ คุณจะต้องใช้รหัสไคลเอ็นต์เหล่านี้เพื่อตรวจสอบโทเค็นเหตุการณ์ด้านความปลอดภัยตามที่อธิบายไว้ในส่วนถัดไป
วิธีเปิดใช้ RISC API
- เปิดหน้า RISC API ใน ตรวจสอบว่ายังได้เลือกโปรเจ็กต์ที่คุณใช้ เพื่อเข้าถึงบริการของ Google อยู่ 
- อ่านข้อกำหนดของ RISC และตรวจสอบว่าคุณเข้าใจข้อกำหนด - หากคุณเปิดใช้ API สำหรับโปรเจ็กต์ที่เป็นขององค์กร โปรดตรวจสอบว่าคุณได้รับอนุญาตให้ผูกองค์กรกับข้อกำหนดของ RISC 
- คลิกเปิดใช้ก็ต่อเมื่อคุณยินยอมตามข้อกำหนดของ RISC 
สร้างปลายทางตัวรับเหตุการณ์
หากต้องการรับการแจ้งเตือนเหตุการณ์ด้านความปลอดภัยจาก Google คุณต้องสร้างปลายทาง HTTPS ที่จัดการคำขอ HTTPS POST หลังจากลงทะเบียนปลายทางนี้ (ดูด้านล่าง) Google จะเริ่มโพสต์สตริงที่ลงนามแบบเข้ารหัสลับซึ่งเรียกว่าโทเค็นเหตุการณ์ด้านความปลอดภัยไปยังปลายทาง โทเค็นเหตุการณ์ด้านความปลอดภัยคือ JWT ที่ลงชื่อแล้วซึ่งมี ข้อมูลเกี่ยวกับเหตุการณ์เดียวที่เกี่ยวข้องกับความปลอดภัย
สำหรับโทเค็นเหตุการณ์ด้านความปลอดภัยแต่ละรายการที่คุณได้รับที่ปลายทาง ให้ตรวจสอบและ ถอดรหัสโทเค็นก่อน จากนั้นจัดการเหตุการณ์ด้านความปลอดภัยตามความเหมาะสมกับบริการของคุณ การตรวจสอบโทเค็นเหตุการณ์ก่อนถอดรหัสเป็นสิ่งจำเป็นเพื่อป้องกันการโจมตีที่เป็นอันตรายจากผู้ไม่ประสงค์ดี ส่วนต่อไปนี้จะอธิบายงานเหล่านี้
1. ถอดรหัสและตรวจสอบโทเค็นเหตุการณ์ด้านความปลอดภัย
เนื่องจากโทเค็นเหตุการณ์ด้านความปลอดภัยเป็น JWT ประเภทหนึ่ง คุณจึงใช้ไลบรารี JWT ใดก็ได้ เช่น ไลบรารีที่แสดงใน jwt.io เพื่อถอดรหัสและตรวจสอบโทเค็นเหล่านั้นได้ ไม่ว่าคุณจะใช้ไลบรารีใดก็ตาม โค้ดการตรวจสอบโทเค็นต้องทำสิ่งต่อไปนี้
- รับตัวระบุผู้ออกการป้องกันข้ามบัญชี (issuer) และ URI ของใบรับรองคีย์การลงนาม (jwks_uri) จากเอกสารการกำหนดค่า RISC ของ Google ซึ่งคุณดูได้ที่https://accounts.google.com/.well-known/risc-configuration
- ใช้ไลบรารี JWT ที่คุณเลือกเพื่อรับรหัสคีย์การลงนามจากส่วนหัว ของโทเค็นเหตุการณ์ด้านความปลอดภัย
- จากเอกสารใบรับรองคีย์การลงนามของ Google ให้รับคีย์สาธารณะที่มีรหัสคีย์ที่คุณได้รับในขั้นตอนก่อนหน้า หากเอกสารไม่มีคีย์ ที่มีรหัสที่คุณกำลังค้นหา แสดงว่าโทเค็นเหตุการณ์ความปลอดภัย ไม่ถูกต้อง และปลายทางควรแสดงข้อผิดพลาด HTTP 400
- ใช้ไลบรารี JWT ที่คุณเลือกเพื่อยืนยันข้อมูลต่อไปนี้
- ระบบจะลงนามโทเค็นเหตุการณ์ด้านความปลอดภัยโดยใช้คีย์สาธารณะที่คุณได้รับใน ขั้นตอนก่อนหน้า
- audการอ้างสิทธิ์ของโทเค็นคือรหัสไคลเอ็นต์ของแอป
- issการอ้างสิทธิ์ของโทเค็นตรงกับตัวระบุผู้ออกที่คุณได้รับจาก เอกสารการค้นพบ RISC โปรดทราบว่าคุณไม่จำเป็นต้องยืนยันการหมดอายุของโทเค็น (- exp) เนื่องจากโทเค็นเหตุการณ์ด้านความปลอดภัยแสดงถึงเหตุการณ์ในอดีตและจะไม่หมดอายุ
 
เช่น
Java
การใช้ java-jwt และ jwks-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:
                // ?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:
# ?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 ที่ไม่ซ้ำกันของผู้ใช้ (sub) รหัสบัญชี Google นี้คือตัวระบุเดียวกัน (sub) ที่อยู่ในโทเค็นรหัส JWT ที่ออกโดยไลบรารีการลงชื่อเข้าใช้ด้วย Google (JavaScript
, HTML) เวอร์ชันใหม่กว่า, ไลบรารีการลงชื่อเข้าใช้ด้วย Google เวอร์ชันเดิม หรือ OpenID Connect เมื่อsubject_typeของ
การอ้างสิทธิ์เป็น id_token_claims อาจมีฟิลด์ email ที่มี
อีเมลของผู้ใช้ด้วย
ใช้ข้อมูลในคำกล่าวอ้าง events เพื่อดำเนินการที่เหมาะสมกับ
ประเภทเหตุการณ์ในบัญชีของผู้ใช้ที่ระบุ
ตัวระบุโทเค็น OAuth
สำหรับเหตุการณ์ OAuth เกี่ยวกับโทเค็นแต่ละรายการ ประเภทตัวระบุเรื่องโทเค็นจะมีฟิลด์ต่อไปนี้
- token_type: รองรับเฉพาะ- refresh_token
- token_identifier_alg: ดูค่าที่เป็นไปได้ในตารางด้านล่าง
- token: ดูตารางด้านล่าง
| token_identifier_alg | โทเค็น | 
|---|---|
| prefix | อักขระ 16 ตัวแรกของโทเค็น | 
| hash_base64_sha512_sha512 | แฮช 2 ชั้นของโทเค็นโดยใช้ 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 | ดูส่วนตัวระบุโทเค็น OAuth สำหรับ ตัวระบุโทเค็น | ต้องระบุ: หากคุณจัดเก็บโทเค็นการรีเฟรชที่เกี่ยวข้อง ให้ลบโทเค็นดังกล่าว และขอให้ผู้ใช้ให้ความยินยอมอีกครั้งในครั้งถัดไปที่ต้องใช้โทเค็นเพื่อการเข้าถึง | 
| https://schemas.openid.net/secevent/risc/event-type/account-disabled | reason=hijacking,reason=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อ้างสิทธิ์ (ซึ่งเป็นตัวระบุที่ไม่ซ้ำกันสำหรับเหตุการณ์) เพื่อขจัดเหตุการณ์ที่ซ้ำกัน มีเครื่องมือภายนอก เช่น Google Cloud
Dataflow ที่อาจช่วยคุณดำเนินการ
Dataflow การขจัดข้อมูลที่ซ้ำกัน
โปรดทราบว่าระบบจะส่งกิจกรรมโดยมีการลองใหม่แบบจำกัด ดังนั้นหากผู้รับไม่พร้อมใช้งานเป็นเวลานาน คุณอาจพลาดกิจกรรมบางอย่างอย่างถาวร
ลงทะเบียนตัวรับสัญญาณ
หากต้องการเริ่มรับเหตุการณ์ด้านความปลอดภัย ให้ลงทะเบียนปลายทางผู้รับโดยใช้ RISC API การเรียก RISC API ต้องมาพร้อมกับโทเค็นการให้สิทธิ์
คุณจะได้รับการดำเนินการด้านความปลอดภัยสำหรับผู้ใช้แอปของคุณเท่านั้น ดังนั้นคุณต้องกำหนดค่าหน้าจอคำยินยอม OAuth ในโปรเจ็กต์ GCP เป็นข้อกำหนดเบื้องต้นสำหรับขั้นตอนที่อธิบายไว้ด้านล่าง
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-jwt และ ไลบรารีการตรวจสอบสิทธิ์ของ Google
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 ได้เป็นเวลา 1 ชั่วโมง เมื่อโทเค็นหมดอายุ ให้สร้างโทเค็นใหม่เพื่อเรียกใช้ RISC API ต่อไป
2. เรียกใช้ RISC Stream Configuration API
เมื่อมีโทเค็นการให้สิทธิ์แล้ว คุณจะใช้ RISC API เพื่อกำหนดค่า สตรีมเหตุการณ์ความปลอดภัยของโปรเจ็กต์ รวมถึงลงทะเบียนปลายทางผู้รับ ได้
โดยให้ส่งคำขอ HTTPS POST ไปยัง https://risc.googleapis.com/v1beta/stream:update,
ระบุปลายทางของตัวรับและประเภทของเหตุการณ์ด้านความปลอดภัยที่คุณสนใจ
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 แสดงว่ากำหนดค่าสตรีมเหตุการณ์สำเร็จแล้ว และปลายทางผู้รับควรเริ่มรับโทเค็นเหตุการณ์ความปลอดภัย ส่วนถัดไปจะอธิบายวิธีทดสอบการกำหนดค่าสตรีมและปลายทาง เพื่อยืนยันว่าทุกอย่างทำงานร่วมกันได้อย่างถูกต้อง
รับและอัปเดตการกำหนดค่าสตรีมปัจจุบัน
หากในอนาคตคุณต้องการแก้ไขการกำหนดค่าสตรีม คุณสามารถทำได้โดยส่งคำขอ GET ที่ได้รับอนุญาตไปยัง https://risc.googleapis.com/v1beta/stream เพื่อรับการกำหนดค่าสตรีมปัจจุบัน แก้ไขเนื้อหาการตอบกลับ แล้วจึงโพสต์การกำหนดค่าที่แก้ไขแล้วกลับไปยัง https://risc.googleapis.com/v1beta/stream:update ตามที่อธิบายไว้ข้างต้น
หยุดและเริ่มสตรีมเหตุการณ์ต่อ
หากต้องการหยุดสตรีมเหตุการณ์จาก Google ให้ส่งคำขอ POST ที่ได้รับอนุญาตไปยัง https://risc.googleapis.com/v1beta/stream/status:update พร้อม { "status": "disabled" }
ในเนื้อหาของคำขอ ขณะที่สตรีมถูกปิดใช้งาน Google จะไม่ส่งเหตุการณ์ไปยังอุปกรณ์ปลายทางและจะไม่บัฟเฟอร์เหตุการณ์ด้านความปลอดภัยเมื่อเกิดขึ้น หากต้องการ
เปิดใช้สตรีมเหตุการณ์อีกครั้ง ให้ POST { "status": "enabled" } ไปยังปลายทางเดียวกัน
3. ไม่บังคับ: ทดสอบการกำหนดค่าสตรีม
คุณสามารถยืนยันว่าการกำหนดค่าสตรีมและปลายทางตัวรับทำงานร่วมกันอย่างถูกต้องได้โดยการส่งโทเค็นการยืนยันผ่านสตรีมเหตุการณ์ โทเค็นนี้อาจมีสตริงที่ไม่ซ้ำกันซึ่งคุณใช้เพื่อยืนยันว่าได้รับโทเค็นที่ปลายทางแล้วได้ หากต้องการใช้ขั้นตอนการทำงานนี้ โปรดตรวจสอบว่าได้ ติดตามประเภทเหตุการณ์ https://schemas.openid.net/secevent/risc/event-type/verification เมื่อลงทะเบียนผู้รับ
หากต้องการขอโทเค็นยืนยัน ให้ส่งคำขอ HTTPS POST ที่ได้รับอนุญาตไปยัง
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()))
หากคำขอสำเร็จ ระบบจะส่งโทเค็นยืนยันไปยังปลายทางที่คุณลงทะเบียนไว้ จากนั้น หากปลายทางจัดการโทเค็นการยืนยันโดย เพียงแค่บันทึกโทเค็น คุณจะตรวจสอบบันทึกเพื่อยืนยันว่าได้รับโทเค็นแล้วได้
ข้อมูลอ้างอิงรหัสข้อผิดพลาด
API ของ RISC อาจแสดงข้อผิดพลาดต่อไปนี้
| รหัสข้อผิดพลาด | ข้อความแสดงข้อผิดพลาด | การดำเนินการที่แนะนำ | 
|---|---|---|
| 400 | การกำหนดค่าสตรีมต้องมีฟิลด์ $fieldname | คำขอของคุณไปยังปลายทาง https://risc.googleapis.com/v1beta/stream:update ไม่ถูกต้องหรือแยกวิเคราะห์ไม่ได้ โปรดระบุ $fieldname ในคำขอ | 
| 401 | ไม่ได้รับอนุญาต | การให้สิทธิ์ล้มเหลว โปรดตรวจสอบว่าคุณได้แนบ โทเค็นการให้สิทธิ์ไปกับคำขอ และโทเค็นนั้นถูกต้อง และยังไม่หมดอายุ | 
| 403 | ปลายทางการนำส่งต้องเป็น URL แบบ HTTPS | ปลายทางการนำส่ง (เช่น ปลายทางที่คุณคาดหวังให้ระบบนำส่งเหตุการณ์ RISC ไปยัง) ต้องเป็น HTTPS เราจะไม่ส่งเหตุการณ์ RISC ไปยัง URL ของ HTTP | 
| 403 | การกำหนดค่าสตรีมที่มีอยู่ไม่มีวิธีการนำส่งที่สอดคล้องกับข้อกำหนดสำหรับ RISC | โปรเจ็กต์ Google Cloud ต้องมีการกำหนดค่า RISC อยู่แล้ว หาก คุณใช้ Firebase และเปิดใช้ Google Sign-In แล้ว Firebase จะจัดการ RISC ให้กับโปรเจ็กต์ของคุณ และคุณจะสร้างการกำหนดค่าที่กำหนดเองไม่ได้ หากคุณไม่ได้ใช้ Google Sign-In สำหรับโปรเจ็กต์ Firebase โปรดปิดใช้ แล้วลองอัปเดตอีกครั้งหลังจากผ่านไป 1 ชั่วโมง | 
| 403 | ไม่พบโปรเจ็กต์ | ตรวจสอบว่าคุณใช้บัญชีบริการที่ถูกต้องสำหรับโปรเจ็กต์ที่ถูกต้อง คุณอาจใช้บัญชีบริการที่เชื่อมโยงกับโปรเจ็กต์ที่ถูกลบ ไปแล้ว ดู วิธีดูบัญชีบริการทั้งหมดที่เชื่อมโยงกับโปรเจ็กต์ | 
| 403 | บัญชีบริการต้องมีสิทธิ์เข้าถึงการกำหนดค่า RISC ของคุณ | ไปที่โปรเจ็กต์  และ
        มอบหมายบทบาท "ผู้ดูแลระบบการกำหนดค่า RISC"
        ( roles/riscconfigs.admin)
        ให้กับบัญชีบริการที่ทำการเรียกไปยังโปรเจ็กต์โดยทำตาม
        วิธีการเหล่านี้ | 
| 403 | ควรเรียกใช้ API การจัดการสตรีมโดยบัญชีบริการเท่านั้น | ดูข้อมูลเพิ่มเติมเกี่ยวกับ วิธีเรียกใช้ Google APIs ด้วยบัญชีบริการ | 
| 403 | ปลายทางการนำส่งไม่ได้อยู่ในโดเมนของโปรเจ็กต์ | ทุกโปรเจ็กต์มีชุด โดเมนที่ได้รับอนุญาต หากไม่ได้โฮสต์ปลายทางการนำส่ง (เช่น ปลายทางที่คุณคาดหวังให้มีการนำส่งเหตุการณ์ RISC) ในแพลตฟอร์มใดแพลตฟอร์มหนึ่ง เรากำหนดให้คุณเพิ่มโดเมนของปลายทางลงในชุดนั้น | 
| 403 | หากต้องการใช้ API นี้ โปรเจ็กต์ของคุณต้องมีการกำหนดค่าไคลเอ็นต์ OAuth อย่างน้อย 1 รายการ | RISC จะทำงานได้ก็ต่อเมื่อคุณสร้างแอปที่รองรับ การลงชื่อเข้าใช้ด้วย Google การเชื่อมต่อนี้ต้องใช้ไคลเอ็นต์ OAuth หากโปรเจ็กต์ไม่มีไคลเอ็นต์ OAuth คุณก็ไม่น่าจะได้รับประโยชน์จาก RISC ดูข้อมูลเพิ่มเติม เกี่ยวกับการใช้ OAuth ของ Google สำหรับ API ของเรา | 
| 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 | 
หากต้องการความช่วยเหลือ
ก่อนอื่น โปรดดูส่วนข้อมูลอ้างอิงรหัสข้อผิดพลาด หากยังคงมีคำถาม โปรดโพสต์คำถามเหล่านั้นใน Stack Overflow โดยใช้แท็ก #SecEvents