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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. เขียน Cloud Functions ที่จัดการเหตุการณ์ 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 ได้ที่ข้อมูลอ้างอิง API ของ UserRecord

ออบเจ็กต์ 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 เท่านั้น)

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

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

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 ระบบจะเขียนทับ customClaims ที่ตรงกันในการอ้างสิทธิ์โทเค็นด้วยคีย์ sessionClaims อย่างไรก็ตาม คีย์ 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',
 };
});