ขยายการตรวจสอบสิทธิ์ Firebase ด้วยฟังก์ชันการบล็อก


ฟังก์ชันการบล็อกช่วยให้คุณสามารถเรียกใช้โค้ดที่กำหนดเองซึ่งจะแก้ไขผลลัพธ์ของ ผู้ใช้ที่ลงทะเบียนหรือลงชื่อเข้าใช้แอปของคุณ ตัวอย่างเช่น คุณสามารถป้องกันไม่ให้ผู้ใช้ ไม่ให้ตรวจสอบสิทธิ์ในกรณีที่ผู้ใช้ไม่ตรงตามเกณฑ์ที่กำหนด หรืออัปเดต ก่อนส่งคืนไปยังแอปไคลเอ็นต์

ก่อนเริ่มต้น

หากต้องการใช้ฟังก์ชันการบล็อก คุณต้องอัปเกรดโปรเจ็กต์ Firebase เป็นการตรวจสอบสิทธิ์ Firebase ด้วย Identity Platform หากคุณยังไม่ได้อัปเกรด โปรดอัปเกรดก่อน

การทำความเข้าใจฟังก์ชันการบล็อก

คุณลงทะเบียนฟังก์ชันการบล็อกได้ 2 เหตุการณ์ ดังนี้

  • beforeCreate: ทริกเกอร์ก่อนบันทึกผู้ใช้ใหม่ไปยัง ฐานข้อมูลการตรวจสอบสิทธิ์ Firebase และก่อนที่จะมีการส่งคืนโทเค็นไปยัง แอปไคลเอ็นต์

  • beforeSignIn: ทริกเกอร์หลังจากยืนยันข้อมูลเข้าสู่ระบบของผู้ใช้แล้ว แต่ ก่อนการตรวจสอบสิทธิ์ Firebase จะส่งโทเค็นรหัสกลับไปยังแอปไคลเอ็นต์ ถ้า แอปของคุณใช้การตรวจสอบสิทธิ์แบบหลายปัจจัย ทริกเกอร์หลังจากที่ผู้ใช้ยืนยันปัจจัยที่ 2 โปรดทราบว่าการสร้าง ผู้ใช้ยังทริกเกอร์ beforeSignIn นอกเหนือจาก beforeCreate

โปรดคำนึงถึงสิ่งต่อไปนี้เมื่อใช้ฟังก์ชันการบล็อก

  • ฟังก์ชันของคุณต้องตอบสนองภายใน 7 วินาที หลังจาก 7 วินาที การตรวจสอบสิทธิ์ Firebase แสดงผลข้อผิดพลาด และการดำเนินการไคลเอ็นต์ล้มเหลว

  • ระบบจะส่งโค้ดตอบกลับ HTTP อื่นที่ไม่ใช่ 200 ไปยังแอปไคลเอ็นต์ของคุณ ตรวจสอบ รหัสไคลเอ็นต์จะจัดการข้อผิดพลาดที่ฟังก์ชันอาจแสดงผลได้

  • ฟังก์ชันจะมีผลกับผู้ใช้ทั้งหมดในโปรเจ็กต์ รวมถึงผู้ใช้ที่อยู่ใน tenant การตรวจสอบสิทธิ์ Firebase จะให้ข้อมูลเกี่ยวกับผู้ใช้ไปยังฟังก์ชันของคุณ ซึ่งรวมถึง กลุ่มผู้ใช้ที่ตนอยู่ เพื่อให้คุณตอบสนองได้อย่างเหมาะสม

  • การลิงก์ผู้ให้บริการข้อมูลประจำตัวรายอื่นกับบัญชีจะทริกเกอร์ทุกอย่างที่ลงทะเบียนไว้อีกครั้ง beforeSignIn ฟังก์ชัน

  • การตรวจสอบสิทธิ์แบบกำหนดเองและไม่ระบุตัวตนจะไม่เรียกใช้ฟังก์ชันการบล็อก

ทำให้ฟังก์ชันการบล็อกใช้งานได้

หากต้องการแทรกโค้ดที่กำหนดเองลงในขั้นตอนการตรวจสอบสิทธิ์ผู้ใช้ ให้ใช้การบล็อก เมื่อคุณใช้งานฟังก์ชันการบล็อกแล้ว โค้ดที่กำหนดเองจะต้อง เสร็จสมบูรณ์เพื่อการตรวจสอบสิทธิ์และการสร้างผู้ใช้จึงจะประสบความสำเร็จ

คุณทำให้ฟังก์ชันการบล็อกใช้งานได้ในลักษณะเดียวกับการทำให้ฟังก์ชันใดๆ ใช้งานได้ (ดูหน้าการเริ่มต้นใช้งาน Cloud Functions เพื่อดูรายละเอียด) บทสรุปมีดังนี้:

  1. เขียนฟังก์ชันระบบคลาวด์ที่จัดการเหตุการณ์ beforeCreate ซึ่งเป็นค่า beforeSignIn เหตุการณ์ หรือทั้ง 2 อย่าง

    ตัวอย่างเช่น ในการเริ่มต้นใช้งาน คุณสามารถเพิ่มฟังก์ชันที่ไม่มีการดำเนินการต่อไปนี้ใน index.js:

    const functions = require('firebase-functions');
    
    exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
      // TODO
    });
    
    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      // TODO
    });
    

    ตัวอย่างข้างต้นไม่ได้นำตรรกะการตรวจสอบสิทธิ์ที่กำหนดเองมาใช้งาน โปรดดู เพื่อดูวิธีใช้ฟังก์ชันการบล็อกและ สถานการณ์ที่พบบ่อยสำหรับตัวอย่างที่เฉพาะเจาะจง

  2. ทำให้ฟังก์ชันใช้งานได้โดยใช้ Firebase CLI ดังนี้

    firebase deploy --only functions
    

    คุณต้องทำให้ฟังก์ชันใช้งานได้อีกครั้งทุกครั้งที่อัปเดต

กำลังรับข้อมูลผู้ใช้และบริบท

เหตุการณ์ beforeSignIn และ beforeCreate จะมีUserและ EventContext ที่มีข้อมูลเกี่ยวกับผู้ใช้ที่ลงชื่อเข้าใช้ ใช้ค่าเหล่านี้ ในโค้ดของคุณเพื่อพิจารณาว่าจะอนุญาตการดำเนินการต่อไปหรือไม่

ดูรายการพร็อพเพอร์ตี้ที่มีอยู่ในออบเจ็กต์ User ได้ที่ UserRecord เอกสารอ้างอิง API

ออบเจ็กต์ EventContext มีพร็อพเพอร์ตี้ต่อไปนี้

ชื่อ คำอธิบาย ตัวอย่าง
locale ภาษาของแอปพลิเคชัน คุณสามารถตั้งค่าภาษาโดยใช้ SDK ของไคลเอ็นต์ หรือส่งส่วนหัวของภาษาใน REST API fr หรือ sv-SE
ipAddress ที่อยู่ IP ของอุปกรณ์ที่ผู้ใช้ปลายทางกำลังลงทะเบียนหรือลงชื่อเข้าใช้ จาก 114.14.200.1
userAgent User Agent ที่ทริกเกอร์ฟังก์ชันการบล็อก Mozilla/5.0 (X11; Linux x86_64)
eventId ตัวระบุที่ไม่ซ้ำกันของกิจกรรม rWsyPtolplG2TBFoOkkgyg
eventType ประเภทเหตุการณ์ ซึ่งให้ข้อมูลเกี่ยวกับชื่อกิจกรรม เช่น beforeSignIn หรือ beforeCreate และ วิธีการลงชื่อเข้าใช้ที่เกี่ยวข้องซึ่งใช้ เช่น Google หรืออีเมล/รหัสผ่าน providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType USER เสมอ USER
resource โปรเจ็กต์การตรวจสอบสิทธิ์ Firebase หรือกลุ่มผู้ใช้ projects/project-id/tenants/tenant-id
timestamp เวลาที่ทริกเกอร์เหตุการณ์ ซึ่งอยู่ในรูปแบบ RFC 3339 Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo ออบเจ็กต์ที่มีข้อมูลเกี่ยวกับผู้ใช้ AdditionalUserInfo
credential ออบเจ็กต์ที่มีข้อมูลเกี่ยวกับข้อมูลเข้าสู่ระบบของผู้ใช้ AuthCredential

การบล็อกการลงทะเบียนหรือการลงชื่อเข้าใช้

หากต้องการบล็อกการลงทะเบียนหรือการพยายามลงชื่อเข้าใช้ ให้ส่ง HttpsError ใน เช่น

Node.js

throw new functions.auth.HttpsError('permission-denied');

ตารางต่อไปนี้แสดงข้อผิดพลาดที่คุณสามารถเพิ่มได้พร้อมข้อผิดพลาดเริ่มต้น ข้อความแสดงข้อผิดพลาด:

ชื่อ รหัส ข้อความ
invalid-argument 400 ไคลเอ็นต์ระบุอาร์กิวเมนต์ไม่ถูกต้อง
failed-precondition 400 ดำเนินการตามคำขอในสถานะปัจจุบันของระบบไม่ได้
out-of-range 400 ไคลเอ็นต์ระบุช่วงไม่ถูกต้อง
unauthenticated 401 ไม่มีโทเค็น OAuth ไม่ถูกต้อง หรือหมดอายุ
permission-denied 403 ไคลเอ็นต์มีสิทธิ์ไม่เพียงพอ
not-found 404 ไม่พบทรัพยากรที่ระบุ
aborted 409 ความขัดแย้งในการดำเนินการพร้อมกัน เช่น ความขัดแย้งแบบ Read-modify-write
already-exists 409 ทรัพยากรที่ไคลเอ็นต์พยายามสร้างมีอยู่แล้ว
resource-exhausted 429 โควต้าทรัพยากรหมดหรือถึงขีดการจำกัดอัตราคำขอ
cancelled 499 ไคลเอ็นต์ยกเลิกคำขอ
data-loss 500 ข้อมูลสูญหายโดยกู้คืนไม่ได้หรือข้อมูลเสียหาย
unknown 500 ข้อผิดพลาดของเซิร์ฟเวอร์ที่ไม่รู้จัก
internal 500 ข้อผิดพลาดภายในเซิร์ฟเวอร์
not-implemented 501 เซิร์ฟเวอร์ไม่ได้นำเมธอด API มาใช้
unavailable 503 ไม่พร้อมให้บริการ
deadline-exceeded 504 เกินกำหนดเวลาในการส่งคำขอแล้ว

นอกจากนี้ คุณยังระบุข้อความแสดงข้อผิดพลาดที่กำหนดเองได้ดังนี้

Node.js

throw new functions.auth.HttpsError('permission-denied', 'Unauthorized request origin!');

ตัวอย่างต่อไปนี้แสดงวิธีบล็อกผู้ใช้ที่ไม่ได้อยู่ใน จากการลงทะเบียนสำหรับแอปของคุณ:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  // (If the user is authenticating within a tenant context, the tenant ID can be determined from
  // user.tenantId or from context.resource, e.g. 'projects/project-id/tenant/tenant-id-1')

  // Only users of a specific domain can sign up.
  if (user.email.indexOf('@acme.com') === -1) {
    throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email "${user.email}"`);
  }
});

ไม่ว่าคุณจะใช้ข้อความเริ่มต้น หรือข้อความที่กำหนดเอง Cloud Functions จะรวมข้อผิดพลาดและส่งกลับไปยังไคลเอ็นต์ในรูปแบบ ข้อผิดพลาดภายใน เช่น

throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email user@evil.com}`);

แอปของคุณควรตรวจจับข้อผิดพลาดได้และจัดการให้เหมาะสม เช่น

JavaScript

// Blocking functions can also be triggered in a multi-tenant context before user creation.
// firebase.auth().tenantId = 'tenant-id-1';
firebase.auth().createUserWithEmailAndPassword('johndoe@example.com', 'password')
  .then((result) => {
    result.user.getIdTokenResult()
  })
  .then((idTokenResult) => {
    console.log(idTokenResult.claim.admin);
  })
  .catch((error) => {
    if (error.code !== 'auth/internal-error' && error.message.indexOf('Cloud Function') !== -1) {
      // Display error.
    } else {
      // Registration succeeds.
    }
  });

การแก้ไขผู้ใช้

แทนที่จะบล็อกการลงทะเบียนหรือการพยายามลงชื่อเข้าใช้ คุณสามารถอนุญาต เพื่อดำเนินการต่อ แต่แก้ไขออบเจ็กต์ User ที่บันทึกไว้ ฐานข้อมูลการตรวจสอบสิทธิ์ Firebase และส่งกลับไปยังไคลเอ็นต์แล้ว

หากต้องการแก้ไขผู้ใช้ ให้แสดงผลออบเจ็กต์จากเครื่องจัดการเหตุการณ์ที่มี ที่ต้องการแก้ไข คุณจะแก้ไขช่องต่อไปนี้ได้

  • displayName
  • disabled
  • emailVerified
  • photoUrl
  • customClaims
  • sessionClaims (beforeSignIn เท่านั้น)

ฟิลด์ที่แก้ไขทั้งหมดจะได้รับการบันทึกไว้ใน ยกเว้น sessionClaims ฐานข้อมูลของการตรวจสอบสิทธิ์ Firebase ซึ่งหมายความว่าจะรวมอยู่ในการตอบกลับ และคงอยู่ระหว่างเซสชันของผู้ใช้

ตัวอย่างต่อไปนี้แสดงวิธีตั้งชื่อที่แสดงเริ่มต้น

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  return {
    // If no display name is provided, set it to "Guest".
    displayName: user.displayName || 'Guest';
  };
});

หากคุณลงทะเบียนเครื่องจัดการเหตุการณ์สำหรับทั้ง beforeCreate และ beforeSignIn โปรดทราบว่า beforeSignIn จะทำงานหลังจาก beforeCreate อัปเดตช่องผู้ใช้แล้วใน beforeCreate จะปรากฏใน beforeSignIn หากคุณตั้งค่าช่องอื่นที่ไม่ใช่ sessionClaims ในตัวแฮนเดิลเหตุการณ์ทั้ง 2 แบบ ค่าที่ตั้งไว้ใน beforeSignIn เขียนทับค่าที่ตั้งไว้ใน beforeCreate สำหรับ sessionClaims เท่านั้น เผยแพร่ไปยังการอ้างสิทธิ์โทเค็นของเซสชันปัจจุบัน แต่ไม่คงอยู่ หรือ จัดเก็บไว้ในฐานข้อมูล

ตัวอย่างเช่น หากตั้งค่า sessionClaims ไว้ beforeSignIn จะแสดงผล ที่มีการอ้างสิทธิ์ beforeCreate รายการ และจะรวมเข้าด้วยกัน เมื่อนำมารวมกัน คีย์ sessionClaims ตรงกับคีย์ใน customClaims sessionClaims จะถูกเขียนทับในการอ้างสิทธิ์โทเค็นcustomClaims อย่างไรก็ตาม คีย์ customClaims ที่ถูกแทนที่จะยังคงอยู่ใน ฐานข้อมูลสำหรับคำขอในอนาคต

ข้อมูลเข้าสู่ระบบ OAuth และข้อมูลที่รองรับ

คุณสามารถส่งผ่านข้อมูลเข้าสู่ระบบ OAuth และข้อมูลไปยังฟังก์ชันการบล็อกจาก ผู้ให้บริการข้อมูลประจำตัว ตารางต่อไปนี้แสดงข้อมูลเข้าสู่ระบบและข้อมูล รองรับผู้ให้บริการข้อมูลประจำตัวแต่ละราย

ผู้ให้บริการข้อมูลประจำตัว โทเค็นรหัส โทเค็นเพื่อการเข้าถึง เวลาหมดอายุ ข้อมูลลับของโทเค็น โทเค็นการรีเฟรช การอ้างสิทธิ์ในการลงชื่อเข้าใช้
Google ใช่ ใช่ ใช่ ไม่ใช่ ใช่ ไม่
Facebook ไม่ ใช่ ใช่ ไม่ได้ ไม่ใช่ ไม่
Twitter ไม่ ใช่ ไม่ใช่ ใช่ ไม่ได้ ไม่
GitHub ไม่ ใช่ ไม่ได้ ไม่ใช่ ไม่ใช่ ไม่
Microsoft ใช่ ใช่ ใช่ ไม่ใช่ ใช่ ไม่
LinkedIn ไม่ ใช่ ใช่ ไม่ได้ ไม่ใช่ ไม่
Yahoo ใช่ ใช่ ใช่ ไม่ใช่ ใช่ ไม่
Apple ใช่ ใช่ ใช่ ไม่ใช่ ใช่ ไม่
SAML ไม่ ไม่ใช่ ไม่ใช่ ไม่ใช่ ไม่ได้ ใช่
OIDC ใช่ ใช่ ใช่ ไม่ใช่ ใช่ ใช่

รีเฟรชโทเค็น

หากต้องการใช้โทเค็นการรีเฟรชในฟังก์ชันการบล็อก คุณต้องเลือก ในหน้าฟังก์ชันการบล็อกของคอนโซล Firebase

ผู้ให้บริการข้อมูลประจำตัวจะไม่แสดงผลโทเค็นการรีเฟรชเมื่อลงชื่อเข้าใช้ โดยตรงด้วยข้อมูลเข้าสู่ระบบ OAuth เช่น โทเค็นรหัสหรือโทเค็นเพื่อการเข้าถึง ด้วยวิธีนี้ ข้อมูลเข้าสู่ระบบ OAuth ฝั่งไคลเอ็นต์เดียวกันจะถูกส่งไปยังการบล็อก

ส่วนต่อไปนี้จะอธิบายผู้ให้บริการข้อมูลประจำตัวแต่ละประเภทและที่ระบบรองรับ และข้อมูลเข้าสู่ระบบ

ผู้ให้บริการ OIDC ทั่วไป

เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วยผู้ให้บริการ OIDC ทั่วไป ข้อมูลเข้าสู่ระบบต่อไปนี้ จะผ่านการตรวจสอบ:

  • โทเค็นรหัส: ระบุหากเลือกขั้นตอน id_token
  • โทเค็นเพื่อการเข้าถึง: ระบุไว้หากเลือกโฟลว์ของโค้ด โปรดทราบว่ารหัส ระบบรองรับโฟลว์ได้ผ่านทาง REST API เท่านั้นในขณะนี้
  • โทเค็นการรีเฟรช: ระบุไว้หาก offline_access ขอบเขต ไว้

ตัวอย่าง:

const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);

Google

เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Google ข้อมูลเข้าสู่ระบบต่อไปนี้จะถูกส่งไป

  • โทเค็นรหัส
  • โทเค็นเพื่อการเข้าถึง
  • โทเค็นการรีเฟรช: ระบุเฉพาะในกรณีที่พารามิเตอร์ที่กำหนดเองต่อไปนี้ ส่งคำขอแล้ว:
    • access_type=offline
    • prompt=consent หากผู้ใช้เคยให้ความยินยอมและไม่ให้ความยินยอม ส่งคำขอขอบเขตใหม่แล้ว

ตัวอย่าง:

const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
  'access_type': 'offline',
  'prompt': 'consent'
});
firebase.auth().signInWithPopup(provider);

โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับโทเค็นการรีเฟรชของ Google

Facebook

เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Facebook ข้อมูลเข้าสู่ระบบต่อไปนี้จะถูกส่ง

  • โทเค็นเพื่อการเข้าถึง: จะแสดงโทเค็นเพื่อการเข้าถึงที่แลกเปลี่ยนได้ โทเค็นเพื่อการเข้าถึงอื่น ดูข้อมูลเพิ่มเติมเกี่ยวกับประเภทต่างๆ ของ โทเค็นเพื่อการเข้าถึง Facebook ที่ได้รับการสนับสนุน และคุณจะสามารถแลกเปลี่ยนความคิดเห็น โทเค็นที่ใช้ได้นาน

GitHub

เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย GitHub ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้

  • โทเค็นเพื่อการเข้าถึง: ไม่มีวันหมดอายุจนกว่าจะถูกเพิกถอน

Microsoft

เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Microsoft ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้

  • โทเค็นรหัส
  • โทเค็นเพื่อการเข้าถึง
  • โทเค็นการรีเฟรช: ส่งผ่านไปยังฟังก์ชันการบล็อกหาก offline_access ขอบเขต ไว้

ตัวอย่าง:

const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);

Yahoo

เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Yahoo ข้อมูลเข้าสู่ระบบต่อไปนี้จะถูกส่งไป ที่ไม่มีพารามิเตอร์หรือขอบเขตที่กำหนดเอง

  • โทเค็นรหัส
  • โทเค็นเพื่อการเข้าถึง
  • โทเค็นการรีเฟรช

LinkedIn

เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย LinkedIn ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้

  • โทเค็นเพื่อการเข้าถึง

Apple

เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วย Apple ระบบจะส่งข้อมูลเข้าสู่ระบบต่อไปนี้ ที่ไม่มีพารามิเตอร์หรือขอบเขตที่กำหนดเอง

  • โทเค็นรหัส
  • โทเค็นเพื่อการเข้าถึง
  • โทเค็นการรีเฟรช

สถานการณ์ที่พบบ่อย

ตัวอย่างต่อไปนี้แสดง Use Case ที่พบบ่อยสำหรับฟังก์ชันการบล็อก

อนุญาตให้จดทะเบียนจากโดเมนที่เจาะจงเท่านั้น

ตัวอย่างต่อไปนี้แสดงวิธีการป้องกันผู้ใช้ที่ไม่ได้อยู่ใน example.com โดเมนจากการลงทะเบียนกับแอปของคุณ:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (!user.email || user.email.indexOf('@example.com') === -1) {
    throw new functions.auth.HttpsError(
      'invalid-argument', `Unauthorized email "${user.email}"`);
  }
});

การบล็อกไม่ให้ผู้ใช้ที่มีอีเมลที่ไม่ได้รับการยืนยันลงทะเบียน

ตัวอย่างต่อไปนี้จะแสดงวิธีป้องกันไม่ให้ผู้ใช้ที่มีอีเมลที่ไม่ได้รับการยืนยัน การลงทะเบียนด้วยแอปของคุณ

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.email && !user.emailVerified) {
    throw new functions.auth.HttpsError(
      'invalid-argument', `Unverified email "${user.email}"`);
  }
});

กำหนดให้มีการยืนยันอีเมลเมื่อลงทะเบียน

ตัวอย่างต่อไปนี้แสดงวิธีกำหนดให้ผู้ใช้ยืนยันอีเมลหลังจาก การลงทะเบียน:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  const locale = context.locale;
  if (user.email && !user.emailVerified) {
    // Send custom email verification on sign-up.
    return admin.auth().generateEmailVerificationLink(user.email).then((link) => {
      return sendCustomVerificationEmail(user.email, link, locale);
    });
  }
});

exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
 if (user.email && !user.emailVerified) {
   throw new functions.auth.HttpsError(
     'invalid-argument', `"${user.email}" needs to be verified before access is granted.`);
  }
});

การจัดการกับอีเมลของผู้ให้บริการข้อมูลประจำตัวบางรายว่ายืนยันแล้ว

ตัวอย่างต่อไปนี้แสดงวิธีการจัดการอีเมลของผู้ใช้จากข้อมูลระบุตัวตนบางอย่าง ยืนยันผู้ให้บริการ

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.email && !user.emailVerified && context.eventType.indexOf(':facebook.com') !== -1) {
    return {
      emailVerified: true,
    };
  }
});

การบล็อกการลงชื่อเข้าใช้จากที่อยู่ IP บางรายการ

ตัวอย่างต่อไปนี้บล็อกการลงชื่อเข้าใช้จากช่วงที่อยู่ IP บางช่วงได้

Node.js

exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
  if (isSuspiciousIpAddress(context.ipAddress)) {
    throw new functions.auth.HttpsError(
      'permission-denied', 'Unauthorized access!');
  }
});

การตั้งค่าการอ้างสิทธิ์ที่กำหนดเองและการอ้างสิทธิ์เซสชัน

ตัวอย่างต่อไปนี้แสดงวิธีตั้งการอ้างสิทธิ์ที่กำหนดเองและการอ้างสิทธิ์เซสชัน

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (context.credential &&
      context.credential.providerId === 'saml.my-provider-id') {
    return {
      // Employee ID does not change so save in persistent claims (stored in
      // Auth DB).
      customClaims: {
        eid: context.credential.claims.employeeid,
      },
      // Copy role and groups to token claims. These will not be persisted.
      sessionClaims: {
        role: context.credential.claims.role,
        groups: context.credential.claims.groups,
      }
    }
  }
});

การติดตามที่อยู่ IP เพื่อตรวจสอบกิจกรรมที่น่าสงสัย

คุณป้องกันการขโมยโทเค็นได้ด้วยการติดตามที่อยู่ IP ที่ผู้ใช้ลงชื่อเข้าใช้ และเปรียบเทียบกับที่อยู่ IP ในคำขอที่ตามมา หากคำขอ ดูน่าสงสัย ตัวอย่างเช่น IP มาจากคนละภูมิศาสตร์ ภูมิภาค — คุณสามารถขอให้ผู้ใช้ลงชื่อเข้าใช้อีกครั้ง

  1. ใช้การอ้างสิทธิ์เซสชันเพื่อติดตามที่อยู่ IP ที่ผู้ใช้ลงชื่อเข้าใช้

    Node.js

    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      return {
        sessionClaims: {
          signInIpAddress: context.ipAddress,
        },
      };
    });
    
  2. เมื่อผู้ใช้พยายามเข้าถึงทรัพยากรที่ต้องมีการตรวจสอบสิทธิ์ด้วย การตรวจสอบสิทธิ์ Firebase ให้เปรียบเทียบที่อยู่ IP ในคำขอกับ IP ที่ใช้ เพื่อลงชื่อเข้าใช้:

    Node.js

    app.post('/getRestrictedData', (req, res) => {
      // Get the ID token passed.
      const idToken = req.body.idToken;
      // Verify the ID token, check if revoked and decode its payload.
      admin.auth().verifyIdToken(idToken, true).then((claims) => {
        // Get request IP address
        const requestIpAddress = req.connection.remoteAddress;
        // Get sign-in IP address.
        const signInIpAddress = claims.signInIpAddress;
        // Check if the request IP address origin is suspicious relative to
        // the session IP addresses. The current request timestamp and the
        // auth_time of the ID token can provide additional signals of abuse,
        // especially if the IP address suddenly changed. If there was a sudden
        // geographical change in a short period of time, then it will give
        // stronger signals of possible abuse.
        if (!isSuspiciousIpAddressChange(signInIpAddress, requestIpAddress)) {
          // Suspicious IP address change. Require re-authentication.
          // You can also revoke all user sessions by calling:
          // admin.auth().revokeRefreshTokens(claims.sub).
          res.status(401).send({error: 'Unauthorized access. Please login again!'});
        } else {
          // Access is valid. Try to return data.
          getData(claims).then(data => {
            res.end(JSON.stringify(data);
          }, error => {
            res.status(500).send({ error: 'Server error!' })
          });
        }
      });
    });
    

กำลังคัดกรองรูปภาพของผู้ใช้

ตัวอย่างต่อไปนี้แสดงวิธีทำความสะอาดให้ผู้ใช้ รูปโปรไฟล์:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.photoURL) {
    return isPhotoAppropriate(user.photoURL)
      .then((status) => {
        if (!status) {
          // Sanitize inappropriate photos by replacing them with guest photos.
          // Users could also be blocked from sign-up, disabled, etc.
          return {
            photoUrl: PLACEHOLDER_GUEST_PHOTO_URL,
          };
        }
      });
});

หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีตรวจหาและทำความสะอาดรูปภาพ โปรดดูที่ Cloud Vision

การเข้าถึงข้อมูลเข้าสู่ระบบ OAuth ของผู้ให้บริการข้อมูลประจำตัวของผู้ใช้

ตัวอย่างต่อไปนี้แสดงวิธีรับโทเค็นการรีเฟรชสำหรับผู้ใช้ ที่ลงชื่อเข้าใช้ด้วย Google และใช้เพื่อเรียกใช้ API ของ Google ปฏิทิน โทเค็นการรีเฟรชถูกจัดเก็บสำหรับการเข้าถึงแบบออฟไลน์

Node.js

const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');
// ...
// Initialize Google OAuth client.
const keys = require('./oauth2.keys.json');
const oAuth2Client = new OAuth2Client(
  keys.web.client_id,
  keys.web.client_secret
);

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (context.credential &&
      context.credential.providerId === 'google.com') {
    // Store the refresh token for later offline use.
    // These will only be returned if refresh tokens credentials are included
    // (enabled by Cloud console).
    return saveUserRefreshToken(
        user.uid,
        context.credential.refreshToken,
        'google.com'
      )
      .then(() => {
        // Blocking the function is not required. The function can resolve while
        // this operation continues to run in the background.
        return new Promise((resolve, reject) => {
          // For this operation to succeed, the appropriate OAuth scope should be requested
          // on sign in with Google, client-side. In this case:
          // https://www.googleapis.com/auth/calendar
          // You can check granted_scopes from within:
          // context.additionalUserInfo.profile.granted_scopes (space joined list of scopes).

          // Set access token/refresh token.
          oAuth2Client.setCredentials({
            access_token: context.credential.accessToken,
            refresh_token: context.credential.refreshToken,
          });
          const calendar = google.calendar('v3');
          // Setup Onboarding event on user's calendar.
          const event = {/** ... */};
          calendar.events.insert({
            auth: oauth2client,
            calendarId: 'primary',
            resource: event,
          }, (err, event) => {
            // Do not fail. This is a best effort approach.
            resolve();
          });
      });
    })
  }
});

ลบล้างคำตัดสิน reCAPTCHA Enterprise สำหรับการดำเนินการของผู้ใช้

ตัวอย่างต่อไปนี้แสดงวิธีลบล้างผลการตัดสิน reCAPTCHA Enterprise สําหรับโฟลว์ผู้ใช้ที่รองรับ

โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับการผสานรวม reCAPTCHA Enterprise กับการตรวจสอบสิทธิ์ Firebase ที่หัวข้อเปิดใช้ reCAPTCHA Enterprise

คุณใช้ฟังก์ชันการบล็อกเพื่ออนุญาตหรือบล็อกโฟลว์โดยอิงตามปัจจัยที่กำหนดเองได้ ซึ่งจะลบล้างผลลัพธ์ที่ได้รับจาก reCAPTCHA Enterprise

Node.js

 const {
   auth,
 } = require("firebase-functions/v1");

exports.checkrecaptchaV1 = auth.user().beforeSignIn((userRecord, context) => {
 // Allow users with a specific email domain to sign in regardless of their recaptcha score.
 if (userRecord.email && userRecord.email.indexOf('@acme.com') === -1) {
   return {
     recaptchaActionOverride: 'ALLOW',
   };
 }

 // Allow users to sign in with recaptcha score greater than 0.5
 if (context.additionalUserInfo.recaptchaScore > 0.5) {
   return {
     recaptchaActionOverride: 'ALLOW',
   };
 }

 // Block all others.
 return {
   recaptchaActionOverride: 'BLOCK',
 };
});